1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun // Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
3*4882a593Smuzhiyun
4*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/module.h>
7*4882a593Smuzhiyun #include <linux/kernel.h>
8*4882a593Smuzhiyun #include <linux/list.h>
9*4882a593Smuzhiyun #include <linux/livepatch.h>
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun /*
13*4882a593Smuzhiyun * Keep a small list of pointers so that we can print address-agnostic
14*4882a593Smuzhiyun * pointer values. Use a rolling integer count to differentiate the values.
15*4882a593Smuzhiyun * Ironically we could have used the shadow variable API to do this, but
16*4882a593Smuzhiyun * let's not lean too heavily on the very code we're testing.
17*4882a593Smuzhiyun */
18*4882a593Smuzhiyun static LIST_HEAD(ptr_list);
19*4882a593Smuzhiyun struct shadow_ptr {
20*4882a593Smuzhiyun void *ptr;
21*4882a593Smuzhiyun int id;
22*4882a593Smuzhiyun struct list_head list;
23*4882a593Smuzhiyun };
24*4882a593Smuzhiyun
free_ptr_list(void)25*4882a593Smuzhiyun static void free_ptr_list(void)
26*4882a593Smuzhiyun {
27*4882a593Smuzhiyun struct shadow_ptr *sp, *tmp_sp;
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun list_for_each_entry_safe(sp, tmp_sp, &ptr_list, list) {
30*4882a593Smuzhiyun list_del(&sp->list);
31*4882a593Smuzhiyun kfree(sp);
32*4882a593Smuzhiyun }
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun
ptr_id(void * ptr)35*4882a593Smuzhiyun static int ptr_id(void *ptr)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun struct shadow_ptr *sp;
38*4882a593Smuzhiyun static int count;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun list_for_each_entry(sp, &ptr_list, list) {
41*4882a593Smuzhiyun if (sp->ptr == ptr)
42*4882a593Smuzhiyun return sp->id;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun sp = kmalloc(sizeof(*sp), GFP_ATOMIC);
46*4882a593Smuzhiyun if (!sp)
47*4882a593Smuzhiyun return -ENOMEM;
48*4882a593Smuzhiyun sp->ptr = ptr;
49*4882a593Smuzhiyun sp->id = count++;
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun list_add(&sp->list, &ptr_list);
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun return sp->id;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun /*
57*4882a593Smuzhiyun * Shadow variable wrapper functions that echo the function and arguments
58*4882a593Smuzhiyun * to the kernel log for testing verification. Don't display raw pointers,
59*4882a593Smuzhiyun * but use the ptr_id() value instead.
60*4882a593Smuzhiyun */
shadow_get(void * obj,unsigned long id)61*4882a593Smuzhiyun static void *shadow_get(void *obj, unsigned long id)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun int **sv;
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun sv = klp_shadow_get(obj, id);
66*4882a593Smuzhiyun pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n",
67*4882a593Smuzhiyun __func__, ptr_id(obj), id, ptr_id(sv));
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun return sv;
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
shadow_alloc(void * obj,unsigned long id,size_t size,gfp_t gfp_flags,klp_shadow_ctor_t ctor,void * ctor_data)72*4882a593Smuzhiyun static void *shadow_alloc(void *obj, unsigned long id, size_t size,
73*4882a593Smuzhiyun gfp_t gfp_flags, klp_shadow_ctor_t ctor,
74*4882a593Smuzhiyun void *ctor_data)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun int **var = ctor_data;
77*4882a593Smuzhiyun int **sv;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun sv = klp_shadow_alloc(obj, id, size, gfp_flags, ctor, var);
80*4882a593Smuzhiyun pr_info("klp_%s(obj=PTR%d, id=0x%lx, size=%zx, gfp_flags=%pGg), ctor=PTR%d, ctor_data=PTR%d = PTR%d\n",
81*4882a593Smuzhiyun __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
82*4882a593Smuzhiyun ptr_id(*var), ptr_id(sv));
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun return sv;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun
shadow_get_or_alloc(void * obj,unsigned long id,size_t size,gfp_t gfp_flags,klp_shadow_ctor_t ctor,void * ctor_data)87*4882a593Smuzhiyun static void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size,
88*4882a593Smuzhiyun gfp_t gfp_flags, klp_shadow_ctor_t ctor,
89*4882a593Smuzhiyun void *ctor_data)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun int **var = ctor_data;
92*4882a593Smuzhiyun int **sv;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun sv = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, var);
95*4882a593Smuzhiyun pr_info("klp_%s(obj=PTR%d, id=0x%lx, size=%zx, gfp_flags=%pGg), ctor=PTR%d, ctor_data=PTR%d = PTR%d\n",
96*4882a593Smuzhiyun __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
97*4882a593Smuzhiyun ptr_id(*var), ptr_id(sv));
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun return sv;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
shadow_free(void * obj,unsigned long id,klp_shadow_dtor_t dtor)102*4882a593Smuzhiyun static void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun klp_shadow_free(obj, id, dtor);
105*4882a593Smuzhiyun pr_info("klp_%s(obj=PTR%d, id=0x%lx, dtor=PTR%d)\n",
106*4882a593Smuzhiyun __func__, ptr_id(obj), id, ptr_id(dtor));
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
shadow_free_all(unsigned long id,klp_shadow_dtor_t dtor)109*4882a593Smuzhiyun static void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun klp_shadow_free_all(id, dtor);
112*4882a593Smuzhiyun pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n", __func__, id, ptr_id(dtor));
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun /* Shadow variable constructor - remember simple pointer data */
shadow_ctor(void * obj,void * shadow_data,void * ctor_data)117*4882a593Smuzhiyun static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun int **sv = shadow_data;
120*4882a593Smuzhiyun int **var = ctor_data;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun if (!var)
123*4882a593Smuzhiyun return -EINVAL;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun *sv = *var;
126*4882a593Smuzhiyun pr_info("%s: PTR%d -> PTR%d\n", __func__, ptr_id(sv), ptr_id(*var));
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun return 0;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun /*
132*4882a593Smuzhiyun * With more than one item to free in the list, order is not determined and
133*4882a593Smuzhiyun * shadow_dtor will not be passed to shadow_free_all() which would make the
134*4882a593Smuzhiyun * test fail. (see pass 6)
135*4882a593Smuzhiyun */
shadow_dtor(void * obj,void * shadow_data)136*4882a593Smuzhiyun static void shadow_dtor(void *obj, void *shadow_data)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun int **sv = shadow_data;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n",
141*4882a593Smuzhiyun __func__, ptr_id(obj), ptr_id(sv));
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun /* number of objects we simulate that need shadow vars */
145*4882a593Smuzhiyun #define NUM_OBJS 3
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun /* dynamically created obj fields have the following shadow var id values */
148*4882a593Smuzhiyun #define SV_ID1 0x1234
149*4882a593Smuzhiyun #define SV_ID2 0x1235
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun /*
152*4882a593Smuzhiyun * The main test case adds/removes new fields (shadow var) to each of these
153*4882a593Smuzhiyun * test structure instances. The last group of fields in the struct represent
154*4882a593Smuzhiyun * the idea that shadow variables may be added and removed to and from the
155*4882a593Smuzhiyun * struct during execution.
156*4882a593Smuzhiyun */
157*4882a593Smuzhiyun struct test_object {
158*4882a593Smuzhiyun /* add anything here below and avoid to define an empty struct */
159*4882a593Smuzhiyun struct shadow_ptr sp;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun /* these represent shadow vars added and removed with SV_ID{1,2} */
162*4882a593Smuzhiyun /* char nfield1; */
163*4882a593Smuzhiyun /* int nfield2; */
164*4882a593Smuzhiyun };
165*4882a593Smuzhiyun
test_klp_shadow_vars_init(void)166*4882a593Smuzhiyun static int test_klp_shadow_vars_init(void)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun struct test_object objs[NUM_OBJS];
169*4882a593Smuzhiyun char nfields1[NUM_OBJS], *pnfields1[NUM_OBJS], **sv1[NUM_OBJS];
170*4882a593Smuzhiyun char *pndup[NUM_OBJS];
171*4882a593Smuzhiyun int nfields2[NUM_OBJS], *pnfields2[NUM_OBJS], **sv2[NUM_OBJS];
172*4882a593Smuzhiyun void **sv;
173*4882a593Smuzhiyun int ret;
174*4882a593Smuzhiyun int i;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun ptr_id(NULL);
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun /*
179*4882a593Smuzhiyun * With an empty shadow variable hash table, expect not to find
180*4882a593Smuzhiyun * any matches.
181*4882a593Smuzhiyun */
182*4882a593Smuzhiyun sv = shadow_get(&objs[0], SV_ID1);
183*4882a593Smuzhiyun if (!sv)
184*4882a593Smuzhiyun pr_info(" got expected NULL result\n");
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun /* pass 1: init & alloc a char+int pair of svars for each objs */
187*4882a593Smuzhiyun for (i = 0; i < NUM_OBJS; i++) {
188*4882a593Smuzhiyun pnfields1[i] = &nfields1[i];
189*4882a593Smuzhiyun ptr_id(pnfields1[i]);
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun if (i % 2) {
192*4882a593Smuzhiyun sv1[i] = shadow_alloc(&objs[i], SV_ID1,
193*4882a593Smuzhiyun sizeof(pnfields1[i]), GFP_KERNEL,
194*4882a593Smuzhiyun shadow_ctor, &pnfields1[i]);
195*4882a593Smuzhiyun } else {
196*4882a593Smuzhiyun sv1[i] = shadow_get_or_alloc(&objs[i], SV_ID1,
197*4882a593Smuzhiyun sizeof(pnfields1[i]), GFP_KERNEL,
198*4882a593Smuzhiyun shadow_ctor, &pnfields1[i]);
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun if (!sv1[i]) {
201*4882a593Smuzhiyun ret = -ENOMEM;
202*4882a593Smuzhiyun goto out;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun pnfields2[i] = &nfields2[i];
206*4882a593Smuzhiyun ptr_id(pnfields2[i]);
207*4882a593Smuzhiyun sv2[i] = shadow_alloc(&objs[i], SV_ID2, sizeof(pnfields2[i]),
208*4882a593Smuzhiyun GFP_KERNEL, shadow_ctor, &pnfields2[i]);
209*4882a593Smuzhiyun if (!sv2[i]) {
210*4882a593Smuzhiyun ret = -ENOMEM;
211*4882a593Smuzhiyun goto out;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /* pass 2: verify we find allocated svars and where they point to */
216*4882a593Smuzhiyun for (i = 0; i < NUM_OBJS; i++) {
217*4882a593Smuzhiyun /* check the "char" svar for all objects */
218*4882a593Smuzhiyun sv = shadow_get(&objs[i], SV_ID1);
219*4882a593Smuzhiyun if (!sv) {
220*4882a593Smuzhiyun ret = -EINVAL;
221*4882a593Smuzhiyun goto out;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun if ((char **)sv == sv1[i] && *sv1[i] == pnfields1[i])
224*4882a593Smuzhiyun pr_info(" got expected PTR%d -> PTR%d result\n",
225*4882a593Smuzhiyun ptr_id(sv1[i]), ptr_id(*sv1[i]));
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun /* check the "int" svar for all objects */
228*4882a593Smuzhiyun sv = shadow_get(&objs[i], SV_ID2);
229*4882a593Smuzhiyun if (!sv) {
230*4882a593Smuzhiyun ret = -EINVAL;
231*4882a593Smuzhiyun goto out;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun if ((int **)sv == sv2[i] && *sv2[i] == pnfields2[i])
234*4882a593Smuzhiyun pr_info(" got expected PTR%d -> PTR%d result\n",
235*4882a593Smuzhiyun ptr_id(sv2[i]), ptr_id(*sv2[i]));
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun /* pass 3: verify that 'get_or_alloc' returns already allocated svars */
239*4882a593Smuzhiyun for (i = 0; i < NUM_OBJS; i++) {
240*4882a593Smuzhiyun pndup[i] = &nfields1[i];
241*4882a593Smuzhiyun ptr_id(pndup[i]);
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun sv = shadow_get_or_alloc(&objs[i], SV_ID1, sizeof(pndup[i]),
244*4882a593Smuzhiyun GFP_KERNEL, shadow_ctor, &pndup[i]);
245*4882a593Smuzhiyun if (!sv) {
246*4882a593Smuzhiyun ret = -EINVAL;
247*4882a593Smuzhiyun goto out;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun if ((char **)sv == sv1[i] && *sv1[i] == pnfields1[i])
250*4882a593Smuzhiyun pr_info(" got expected PTR%d -> PTR%d result\n",
251*4882a593Smuzhiyun ptr_id(sv1[i]), ptr_id(*sv1[i]));
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun /* pass 4: free <objs[*], SV_ID1> pairs of svars, verify removal */
255*4882a593Smuzhiyun for (i = 0; i < NUM_OBJS; i++) {
256*4882a593Smuzhiyun shadow_free(&objs[i], SV_ID1, shadow_dtor); /* 'char' pairs */
257*4882a593Smuzhiyun sv = shadow_get(&objs[i], SV_ID1);
258*4882a593Smuzhiyun if (!sv)
259*4882a593Smuzhiyun pr_info(" got expected NULL result\n");
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun /* pass 5: check we still find <objs[*], SV_ID2> svar pairs */
263*4882a593Smuzhiyun for (i = 0; i < NUM_OBJS; i++) {
264*4882a593Smuzhiyun sv = shadow_get(&objs[i], SV_ID2); /* 'int' pairs */
265*4882a593Smuzhiyun if (!sv) {
266*4882a593Smuzhiyun ret = -EINVAL;
267*4882a593Smuzhiyun goto out;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun if ((int **)sv == sv2[i] && *sv2[i] == pnfields2[i])
270*4882a593Smuzhiyun pr_info(" got expected PTR%d -> PTR%d result\n",
271*4882a593Smuzhiyun ptr_id(sv2[i]), ptr_id(*sv2[i]));
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun /* pass 6: free all the <objs[*], SV_ID2> svar pairs too. */
275*4882a593Smuzhiyun shadow_free_all(SV_ID2, NULL); /* 'int' pairs */
276*4882a593Smuzhiyun for (i = 0; i < NUM_OBJS; i++) {
277*4882a593Smuzhiyun sv = shadow_get(&objs[i], SV_ID2);
278*4882a593Smuzhiyun if (!sv)
279*4882a593Smuzhiyun pr_info(" got expected NULL result\n");
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun free_ptr_list();
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun return 0;
285*4882a593Smuzhiyun out:
286*4882a593Smuzhiyun shadow_free_all(SV_ID1, NULL); /* 'char' pairs */
287*4882a593Smuzhiyun shadow_free_all(SV_ID2, NULL); /* 'int' pairs */
288*4882a593Smuzhiyun free_ptr_list();
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun return ret;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
test_klp_shadow_vars_exit(void)293*4882a593Smuzhiyun static void test_klp_shadow_vars_exit(void)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun module_init(test_klp_shadow_vars_init);
298*4882a593Smuzhiyun module_exit(test_klp_shadow_vars_exit);
299*4882a593Smuzhiyun MODULE_LICENSE("GPL");
300*4882a593Smuzhiyun MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
301*4882a593Smuzhiyun MODULE_DESCRIPTION("Livepatch test: shadow variables");
302