xref: /OK3568_Linux_fs/kernel/lib/livepatch/test_klp_shadow_vars.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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