xref: /linux/tools/testing/selftests/livepatch/test_modules/test_klp_shadow_vars.c (revision c4bbe83d27c2446a033cc0381c3fb6be5e8c41c7)
1a2818ee4SJoe Lawrence // SPDX-License-Identifier: GPL-2.0
2a2818ee4SJoe Lawrence // Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
3a2818ee4SJoe Lawrence 
4a2818ee4SJoe Lawrence #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
5a2818ee4SJoe Lawrence 
6a2818ee4SJoe Lawrence #include <linux/module.h>
7a2818ee4SJoe Lawrence #include <linux/kernel.h>
8a2818ee4SJoe Lawrence #include <linux/list.h>
9a2818ee4SJoe Lawrence #include <linux/livepatch.h>
10a2818ee4SJoe Lawrence #include <linux/slab.h>
11a2818ee4SJoe Lawrence 
12a2818ee4SJoe Lawrence /*
13a2818ee4SJoe Lawrence  * Keep a small list of pointers so that we can print address-agnostic
14a2818ee4SJoe Lawrence  * pointer values.  Use a rolling integer count to differentiate the values.
15a2818ee4SJoe Lawrence  * Ironically we could have used the shadow variable API to do this, but
16a2818ee4SJoe Lawrence  * let's not lean too heavily on the very code we're testing.
17a2818ee4SJoe Lawrence  */
18a2818ee4SJoe Lawrence static LIST_HEAD(ptr_list);
19a2818ee4SJoe Lawrence struct shadow_ptr {
20a2818ee4SJoe Lawrence 	void *ptr;
21a2818ee4SJoe Lawrence 	int id;
22a2818ee4SJoe Lawrence 	struct list_head list;
23a2818ee4SJoe Lawrence };
24a2818ee4SJoe Lawrence 
free_ptr_list(void)25a2818ee4SJoe Lawrence static void free_ptr_list(void)
26a2818ee4SJoe Lawrence {
27a2818ee4SJoe Lawrence 	struct shadow_ptr *sp, *tmp_sp;
28a2818ee4SJoe Lawrence 
29a2818ee4SJoe Lawrence 	list_for_each_entry_safe(sp, tmp_sp, &ptr_list, list) {
30a2818ee4SJoe Lawrence 		list_del(&sp->list);
31a2818ee4SJoe Lawrence 		kfree(sp);
32a2818ee4SJoe Lawrence 	}
33a2818ee4SJoe Lawrence }
34a2818ee4SJoe Lawrence 
ptr_id(void * ptr)35a2818ee4SJoe Lawrence static int ptr_id(void *ptr)
36a2818ee4SJoe Lawrence {
37a2818ee4SJoe Lawrence 	struct shadow_ptr *sp;
38a2818ee4SJoe Lawrence 	static int count;
39a2818ee4SJoe Lawrence 
40a2818ee4SJoe Lawrence 	list_for_each_entry(sp, &ptr_list, list) {
41a2818ee4SJoe Lawrence 		if (sp->ptr == ptr)
42a2818ee4SJoe Lawrence 			return sp->id;
43a2818ee4SJoe Lawrence 	}
44a2818ee4SJoe Lawrence 
45a2818ee4SJoe Lawrence 	sp = kmalloc(sizeof(*sp), GFP_ATOMIC);
46a2818ee4SJoe Lawrence 	if (!sp)
4786e43f23SJoe Lawrence 		return -ENOMEM;
48a2818ee4SJoe Lawrence 	sp->ptr = ptr;
49a2818ee4SJoe Lawrence 	sp->id = count++;
50a2818ee4SJoe Lawrence 
51a2818ee4SJoe Lawrence 	list_add(&sp->list, &ptr_list);
52a2818ee4SJoe Lawrence 
53a2818ee4SJoe Lawrence 	return sp->id;
54a2818ee4SJoe Lawrence }
55a2818ee4SJoe Lawrence 
56a2818ee4SJoe Lawrence /*
57a2818ee4SJoe Lawrence  * Shadow variable wrapper functions that echo the function and arguments
58a2818ee4SJoe Lawrence  * to the kernel log for testing verification.  Don't display raw pointers,
59a2818ee4SJoe Lawrence  * but use the ptr_id() value instead.
60a2818ee4SJoe Lawrence  */
shadow_get(void * obj,unsigned long id)61a2818ee4SJoe Lawrence static void *shadow_get(void *obj, unsigned long id)
62a2818ee4SJoe Lawrence {
63c24c57a4SPetr Mladek 	int **sv;
64a2818ee4SJoe Lawrence 
65c24c57a4SPetr Mladek 	sv = klp_shadow_get(obj, id);
66a2818ee4SJoe Lawrence 	pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n",
67c24c57a4SPetr Mladek 		__func__, ptr_id(obj), id, ptr_id(sv));
68a2818ee4SJoe Lawrence 
69c24c57a4SPetr Mladek 	return sv;
70a2818ee4SJoe Lawrence }
71a2818ee4SJoe Lawrence 
shadow_alloc(void * obj,unsigned long id,size_t size,gfp_t gfp_flags,klp_shadow_ctor_t ctor,void * ctor_data)72a2818ee4SJoe Lawrence static void *shadow_alloc(void *obj, unsigned long id, size_t size,
73a2818ee4SJoe Lawrence 			  gfp_t gfp_flags, klp_shadow_ctor_t ctor,
74a2818ee4SJoe Lawrence 			  void *ctor_data)
75a2818ee4SJoe Lawrence {
76be6da984SPetr Mladek 	int **var = ctor_data;
77c24c57a4SPetr Mladek 	int **sv;
78c24c57a4SPetr Mladek 
79c24c57a4SPetr Mladek 	sv = klp_shadow_alloc(obj, id, size, gfp_flags, ctor, var);
80a2818ee4SJoe Lawrence 	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",
81a2818ee4SJoe Lawrence 		__func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
82be6da984SPetr Mladek 		ptr_id(*var), ptr_id(sv));
83c24c57a4SPetr Mladek 
84c24c57a4SPetr Mladek 	return sv;
85a2818ee4SJoe Lawrence }
86a2818ee4SJoe Lawrence 
shadow_get_or_alloc(void * obj,unsigned long id,size_t size,gfp_t gfp_flags,klp_shadow_ctor_t ctor,void * ctor_data)87a2818ee4SJoe Lawrence static void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size,
88a2818ee4SJoe Lawrence 				 gfp_t gfp_flags, klp_shadow_ctor_t ctor,
89a2818ee4SJoe Lawrence 				 void *ctor_data)
90a2818ee4SJoe Lawrence {
91be6da984SPetr Mladek 	int **var = ctor_data;
92c24c57a4SPetr Mladek 	int **sv;
93c24c57a4SPetr Mladek 
94c24c57a4SPetr Mladek 	sv = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, var);
95a2818ee4SJoe Lawrence 	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",
96a2818ee4SJoe Lawrence 		__func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
97be6da984SPetr Mladek 		ptr_id(*var), ptr_id(sv));
98c24c57a4SPetr Mladek 
99c24c57a4SPetr Mladek 	return sv;
100a2818ee4SJoe Lawrence }
101a2818ee4SJoe Lawrence 
shadow_free(void * obj,unsigned long id,klp_shadow_dtor_t dtor)102a2818ee4SJoe Lawrence static void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor)
103a2818ee4SJoe Lawrence {
104a2818ee4SJoe Lawrence 	klp_shadow_free(obj, id, dtor);
105a2818ee4SJoe Lawrence 	pr_info("klp_%s(obj=PTR%d, id=0x%lx, dtor=PTR%d)\n",
106a2818ee4SJoe Lawrence 		__func__, ptr_id(obj), id, ptr_id(dtor));
107a2818ee4SJoe Lawrence }
108a2818ee4SJoe Lawrence 
shadow_free_all(unsigned long id,klp_shadow_dtor_t dtor)109a2818ee4SJoe Lawrence static void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
110a2818ee4SJoe Lawrence {
111a2818ee4SJoe Lawrence 	klp_shadow_free_all(id, dtor);
1126a26a9dfSYannick Cote 	pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n", __func__, id, ptr_id(dtor));
113a2818ee4SJoe Lawrence }
114a2818ee4SJoe Lawrence 
115a2818ee4SJoe Lawrence 
116a2818ee4SJoe Lawrence /* Shadow variable constructor - remember simple pointer data */
shadow_ctor(void * obj,void * shadow_data,void * ctor_data)117a2818ee4SJoe Lawrence static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
118a2818ee4SJoe Lawrence {
119c24c57a4SPetr Mladek 	int **sv = shadow_data;
120be6da984SPetr Mladek 	int **var = ctor_data;
121c24c57a4SPetr Mladek 
122be6da984SPetr Mladek 	if (!var)
123be6da984SPetr Mladek 		return -EINVAL;
124be6da984SPetr Mladek 
125be6da984SPetr Mladek 	*sv = *var;
1266a26a9dfSYannick Cote 	pr_info("%s: PTR%d -> PTR%d\n", __func__, ptr_id(sv), ptr_id(*var));
127a2818ee4SJoe Lawrence 
128a2818ee4SJoe Lawrence 	return 0;
129a2818ee4SJoe Lawrence }
130a2818ee4SJoe Lawrence 
13176efe6daSYannick Cote /*
13276efe6daSYannick Cote  * With more than one item to free in the list, order is not determined and
13376efe6daSYannick Cote  * shadow_dtor will not be passed to shadow_free_all() which would make the
13476efe6daSYannick Cote  * test fail. (see pass 6)
13576efe6daSYannick Cote  */
shadow_dtor(void * obj,void * shadow_data)136a2818ee4SJoe Lawrence static void shadow_dtor(void *obj, void *shadow_data)
137a2818ee4SJoe Lawrence {
138c24c57a4SPetr Mladek 	int **sv = shadow_data;
139c24c57a4SPetr Mladek 
140a2818ee4SJoe Lawrence 	pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n",
141c24c57a4SPetr Mladek 		__func__, ptr_id(obj), ptr_id(sv));
142a2818ee4SJoe Lawrence }
143a2818ee4SJoe Lawrence 
14476efe6daSYannick Cote /* number of objects we simulate that need shadow vars */
14576efe6daSYannick Cote #define NUM_OBJS 3
14676efe6daSYannick Cote 
1476a26a9dfSYannick Cote /* dynamically created obj fields have the following shadow var id values */
1486a26a9dfSYannick Cote #define SV_ID1 0x1234
1496a26a9dfSYannick Cote #define SV_ID2 0x1235
1506a26a9dfSYannick Cote 
1516a26a9dfSYannick Cote /*
1526a26a9dfSYannick Cote  * The main test case adds/removes new fields (shadow var) to each of these
1536a26a9dfSYannick Cote  * test structure instances. The last group of fields in the struct represent
1546a26a9dfSYannick Cote  * the idea that shadow variables may be added and removed to and from the
1556a26a9dfSYannick Cote  * struct during execution.
1566a26a9dfSYannick Cote  */
1576a26a9dfSYannick Cote struct test_object {
1586a26a9dfSYannick Cote 	 /* add anything here below and avoid to define an empty struct */
1596a26a9dfSYannick Cote 	struct shadow_ptr sp;
1606a26a9dfSYannick Cote 
1616a26a9dfSYannick Cote 	/* these represent shadow vars added and removed with SV_ID{1,2} */
1626a26a9dfSYannick Cote 	/* char nfield1; */
1636a26a9dfSYannick Cote 	/* int  nfield2; */
1646a26a9dfSYannick Cote };
1656a26a9dfSYannick Cote 
test_klp_shadow_vars_init(void)166a2818ee4SJoe Lawrence static int test_klp_shadow_vars_init(void)
167a2818ee4SJoe Lawrence {
16876efe6daSYannick Cote 	struct test_object objs[NUM_OBJS];
16976efe6daSYannick Cote 	char nfields1[NUM_OBJS], *pnfields1[NUM_OBJS], **sv1[NUM_OBJS];
17076efe6daSYannick Cote 	char *pndup[NUM_OBJS];
17176efe6daSYannick Cote 	int nfields2[NUM_OBJS], *pnfields2[NUM_OBJS], **sv2[NUM_OBJS];
1726a26a9dfSYannick Cote 	void **sv;
173*270f7806SYannick Cote 	int ret;
17476efe6daSYannick Cote 	int i;
175be6da984SPetr Mladek 
176a2818ee4SJoe Lawrence 	ptr_id(NULL);
177a2818ee4SJoe Lawrence 
178a2818ee4SJoe Lawrence 	/*
179a2818ee4SJoe Lawrence 	 * With an empty shadow variable hash table, expect not to find
180a2818ee4SJoe Lawrence 	 * any matches.
181a2818ee4SJoe Lawrence 	 */
18276efe6daSYannick Cote 	sv = shadow_get(&objs[0], SV_ID1);
183c24c57a4SPetr Mladek 	if (!sv)
184a2818ee4SJoe Lawrence 		pr_info("  got expected NULL result\n");
185a2818ee4SJoe Lawrence 
18676efe6daSYannick Cote 	/* pass 1: init & alloc a char+int pair of svars for each objs */
18776efe6daSYannick Cote 	for (i = 0; i < NUM_OBJS; i++) {
18876efe6daSYannick Cote 		pnfields1[i] = &nfields1[i];
18976efe6daSYannick Cote 		ptr_id(pnfields1[i]);
19076efe6daSYannick Cote 
19176efe6daSYannick Cote 		if (i % 2) {
19276efe6daSYannick Cote 			sv1[i] = shadow_alloc(&objs[i], SV_ID1,
19376efe6daSYannick Cote 					sizeof(pnfields1[i]), GFP_KERNEL,
19476efe6daSYannick Cote 					shadow_ctor, &pnfields1[i]);
19576efe6daSYannick Cote 		} else {
19676efe6daSYannick Cote 			sv1[i] = shadow_get_or_alloc(&objs[i], SV_ID1,
19776efe6daSYannick Cote 					sizeof(pnfields1[i]), GFP_KERNEL,
19876efe6daSYannick Cote 					shadow_ctor, &pnfields1[i]);
19976efe6daSYannick Cote 		}
200*270f7806SYannick Cote 		if (!sv1[i]) {
201*270f7806SYannick Cote 			ret = -ENOMEM;
202*270f7806SYannick Cote 			goto out;
203*270f7806SYannick Cote 		}
20449ee4dd2SPetr Mladek 
20576efe6daSYannick Cote 		pnfields2[i] = &nfields2[i];
20676efe6daSYannick Cote 		ptr_id(pnfields2[i]);
20776efe6daSYannick Cote 		sv2[i] = shadow_alloc(&objs[i], SV_ID2, sizeof(pnfields2[i]),
20876efe6daSYannick Cote 					GFP_KERNEL, shadow_ctor, &pnfields2[i]);
209*270f7806SYannick Cote 		if (!sv2[i]) {
210*270f7806SYannick Cote 			ret = -ENOMEM;
211*270f7806SYannick Cote 			goto out;
212*270f7806SYannick Cote 		}
21376efe6daSYannick Cote 	}
21449ee4dd2SPetr Mladek 
21576efe6daSYannick Cote 	/* pass 2: verify we find allocated svars and where they point to */
21676efe6daSYannick Cote 	for (i = 0; i < NUM_OBJS; i++) {
21776efe6daSYannick Cote 		/* check the "char" svar for all objects */
21876efe6daSYannick Cote 		sv = shadow_get(&objs[i], SV_ID1);
219*270f7806SYannick Cote 		if (!sv) {
220*270f7806SYannick Cote 			ret = -EINVAL;
221*270f7806SYannick Cote 			goto out;
222*270f7806SYannick Cote 		}
22376efe6daSYannick Cote 		if ((char **)sv == sv1[i] && *sv1[i] == pnfields1[i])
224a2818ee4SJoe Lawrence 			pr_info("  got expected PTR%d -> PTR%d result\n",
22576efe6daSYannick Cote 				ptr_id(sv1[i]), ptr_id(*sv1[i]));
22649ee4dd2SPetr Mladek 
22776efe6daSYannick Cote 		/* check the "int" svar for all objects */
22876efe6daSYannick Cote 		sv = shadow_get(&objs[i], SV_ID2);
229*270f7806SYannick Cote 		if (!sv) {
230*270f7806SYannick Cote 			ret = -EINVAL;
231*270f7806SYannick Cote 			goto out;
232*270f7806SYannick Cote 		}
23376efe6daSYannick Cote 		if ((int **)sv == sv2[i] && *sv2[i] == pnfields2[i])
234a2818ee4SJoe Lawrence 			pr_info("  got expected PTR%d -> PTR%d result\n",
23576efe6daSYannick Cote 				ptr_id(sv2[i]), ptr_id(*sv2[i]));
23676efe6daSYannick Cote 	}
2376a26a9dfSYannick Cote 
23876efe6daSYannick Cote 	/* pass 3: verify that 'get_or_alloc' returns already allocated svars */
23976efe6daSYannick Cote 	for (i = 0; i < NUM_OBJS; i++) {
24076efe6daSYannick Cote 		pndup[i] = &nfields1[i];
24176efe6daSYannick Cote 		ptr_id(pndup[i]);
24276efe6daSYannick Cote 
24376efe6daSYannick Cote 		sv = shadow_get_or_alloc(&objs[i], SV_ID1, sizeof(pndup[i]),
24476efe6daSYannick Cote 					GFP_KERNEL, shadow_ctor, &pndup[i]);
245*270f7806SYannick Cote 		if (!sv) {
246*270f7806SYannick Cote 			ret = -EINVAL;
247*270f7806SYannick Cote 			goto out;
248*270f7806SYannick Cote 		}
24976efe6daSYannick Cote 		if ((char **)sv == sv1[i] && *sv1[i] == pnfields1[i])
250a2818ee4SJoe Lawrence 			pr_info("  got expected PTR%d -> PTR%d result\n",
25176efe6daSYannick Cote 					ptr_id(sv1[i]), ptr_id(*sv1[i]));
25276efe6daSYannick Cote 	}
253a2818ee4SJoe Lawrence 
25476efe6daSYannick Cote 	/* pass 4: free <objs[*], SV_ID1> pairs of svars, verify removal */
25576efe6daSYannick Cote 	for (i = 0; i < NUM_OBJS; i++) {
25676efe6daSYannick Cote 		shadow_free(&objs[i], SV_ID1, shadow_dtor); /* 'char' pairs */
25776efe6daSYannick Cote 		sv = shadow_get(&objs[i], SV_ID1);
258c24c57a4SPetr Mladek 		if (!sv)
259a2818ee4SJoe Lawrence 			pr_info("  got expected NULL result\n");
26076efe6daSYannick Cote 	}
261a2818ee4SJoe Lawrence 
26276efe6daSYannick Cote 	/* pass 5: check we still find <objs[*], SV_ID2> svar pairs */
26376efe6daSYannick Cote 	for (i = 0; i < NUM_OBJS; i++) {
26476efe6daSYannick Cote 		sv = shadow_get(&objs[i], SV_ID2);	/* 'int' pairs */
265*270f7806SYannick Cote 		if (!sv) {
266*270f7806SYannick Cote 			ret = -EINVAL;
267*270f7806SYannick Cote 			goto out;
268*270f7806SYannick Cote 		}
26976efe6daSYannick Cote 		if ((int **)sv == sv2[i] && *sv2[i] == pnfields2[i])
270a2818ee4SJoe Lawrence 			pr_info("  got expected PTR%d -> PTR%d result\n",
27176efe6daSYannick Cote 					ptr_id(sv2[i]), ptr_id(*sv2[i]));
27276efe6daSYannick Cote 	}
273a2818ee4SJoe Lawrence 
27476efe6daSYannick Cote 	/* pass 6: free all the <objs[*], SV_ID2> svar pairs too. */
27576efe6daSYannick Cote 	shadow_free_all(SV_ID2, NULL);		/* 'int' pairs */
27676efe6daSYannick Cote 	for (i = 0; i < NUM_OBJS; i++) {
27776efe6daSYannick Cote 		sv = shadow_get(&objs[i], SV_ID2);
278c24c57a4SPetr Mladek 		if (!sv)
27976efe6daSYannick Cote 			pr_info("  got expected NULL result\n");
28076efe6daSYannick Cote 	}
281a2818ee4SJoe Lawrence 
282a2818ee4SJoe Lawrence 	free_ptr_list();
283a2818ee4SJoe Lawrence 
284a2818ee4SJoe Lawrence 	return 0;
285*270f7806SYannick Cote out:
286*270f7806SYannick Cote 	shadow_free_all(SV_ID1, NULL);		/* 'char' pairs */
287*270f7806SYannick Cote 	shadow_free_all(SV_ID2, NULL);		/* 'int' pairs */
288*270f7806SYannick Cote 	free_ptr_list();
289*270f7806SYannick Cote 
290*270f7806SYannick Cote 	return ret;
291a2818ee4SJoe Lawrence }
292a2818ee4SJoe Lawrence 
test_klp_shadow_vars_exit(void)293a2818ee4SJoe Lawrence static void test_klp_shadow_vars_exit(void)
294a2818ee4SJoe Lawrence {
295a2818ee4SJoe Lawrence }
296a2818ee4SJoe Lawrence 
297a2818ee4SJoe Lawrence module_init(test_klp_shadow_vars_init);
298a2818ee4SJoe Lawrence module_exit(test_klp_shadow_vars_exit);
299a2818ee4SJoe Lawrence MODULE_LICENSE("GPL");
300a2818ee4SJoe Lawrence MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
301a2818ee4SJoe Lawrence MODULE_DESCRIPTION("Livepatch test: shadow variables");
302