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