]>
Commit | Line | Data |
---|---|---|
a2818ee4 JL |
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(&sp->list); | |
31 | kfree(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(sizeof(*sp), GFP_ATOMIC); | |
46 | if (!sp) | |
86e43f23 | 47 | return -ENOMEM; |
a2818ee4 JL |
48 | sp->ptr = ptr; |
49 | sp->id = count++; | |
50 | ||
51 | list_add(&sp->list, &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 | { | |
c24c57a4 | 63 | int **sv; |
a2818ee4 | 64 | |
c24c57a4 | 65 | sv = klp_shadow_get(obj, id); |
a2818ee4 | 66 | pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n", |
c24c57a4 | 67 | __func__, ptr_id(obj), id, ptr_id(sv)); |
a2818ee4 | 68 | |
c24c57a4 | 69 | return sv; |
a2818ee4 JL |
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 | { | |
be6da984 | 76 | int **var = ctor_data; |
c24c57a4 PM |
77 | int **sv; |
78 | ||
79 | sv = klp_shadow_alloc(obj, id, size, gfp_flags, ctor, var); | |
a2818ee4 JL |
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), | |
be6da984 | 82 | ptr_id(*var), ptr_id(sv)); |
c24c57a4 PM |
83 | |
84 | return sv; | |
a2818ee4 JL |
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 | { | |
be6da984 | 91 | int **var = ctor_data; |
c24c57a4 PM |
92 | int **sv; |
93 | ||
94 | sv = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, var); | |
a2818ee4 JL |
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), | |
be6da984 | 97 | ptr_id(*var), ptr_id(sv)); |
c24c57a4 PM |
98 | |
99 | return sv; | |
a2818ee4 JL |
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); | |
6a26a9df | 112 | pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n", __func__, id, ptr_id(dtor)); |
a2818ee4 JL |
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 | { | |
c24c57a4 | 119 | int **sv = shadow_data; |
be6da984 | 120 | int **var = ctor_data; |
c24c57a4 | 121 | |
be6da984 PM |
122 | if (!var) |
123 | return -EINVAL; | |
124 | ||
125 | *sv = *var; | |
6a26a9df | 126 | pr_info("%s: PTR%d -> PTR%d\n", __func__, ptr_id(sv), ptr_id(*var)); |
a2818ee4 JL |
127 | |
128 | return 0; | |
129 | } | |
130 | ||
76efe6da YC |
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 | */ | |
a2818ee4 JL |
136 | static void shadow_dtor(void *obj, void *shadow_data) |
137 | { | |
c24c57a4 PM |
138 | int **sv = shadow_data; |
139 | ||
a2818ee4 | 140 | pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n", |
c24c57a4 | 141 | __func__, ptr_id(obj), ptr_id(sv)); |
a2818ee4 JL |
142 | } |
143 | ||
76efe6da YC |
144 | /* number of objects we simulate that need shadow vars */ |
145 | #define NUM_OBJS 3 | |
146 | ||
6a26a9df YC |
147 | /* dynamically created obj fields have the following shadow var id values */ |
148 | #define SV_ID1 0x1234 | |
149 | #define SV_ID2 0x1235 | |
a2818ee4 | 150 | |
6a26a9df YC |
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; | |
a2818ee4 | 160 | |
6a26a9df YC |
161 | /* these represent shadow vars added and removed with SV_ID{1,2} */ |
162 | /* char nfield1; */ | |
163 | /* int nfield2; */ | |
164 | }; | |
a2818ee4 | 165 | |
6a26a9df YC |
166 | static int test_klp_shadow_vars_init(void) |
167 | { | |
76efe6da YC |
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]; | |
6a26a9df | 172 | void **sv; |
270f7806 | 173 | int ret; |
76efe6da | 174 | int i; |
be6da984 | 175 | |
a2818ee4 | 176 | ptr_id(NULL); |
a2818ee4 JL |
177 | |
178 | /* | |
179 | * With an empty shadow variable hash table, expect not to find | |
180 | * any matches. | |
181 | */ | |
76efe6da | 182 | sv = shadow_get(&objs[0], SV_ID1); |
c24c57a4 | 183 | if (!sv) |
a2818ee4 JL |
184 | pr_info(" got expected NULL result\n"); |
185 | ||
76efe6da YC |
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(pnfields1[i]); | |
190 | ||
191 | if (i % 2) { | |
192 | sv1[i] = shadow_alloc(&objs[i], SV_ID1, | |
193 | sizeof(pnfields1[i]), GFP_KERNEL, | |
194 | shadow_ctor, &pnfields1[i]); | |
195 | } else { | |
196 | sv1[i] = shadow_get_or_alloc(&objs[i], SV_ID1, | |
197 | sizeof(pnfields1[i]), GFP_KERNEL, | |
198 | shadow_ctor, &pnfields1[i]); | |
199 | } | |
270f7806 YC |
200 | if (!sv1[i]) { |
201 | ret = -ENOMEM; | |
202 | goto out; | |
203 | } | |
76efe6da YC |
204 | |
205 | pnfields2[i] = &nfields2[i]; | |
206 | ptr_id(pnfields2[i]); | |
207 | sv2[i] = shadow_alloc(&objs[i], SV_ID2, sizeof(pnfields2[i]), | |
208 | GFP_KERNEL, shadow_ctor, &pnfields2[i]); | |
270f7806 YC |
209 | if (!sv2[i]) { |
210 | ret = -ENOMEM; | |
211 | goto out; | |
212 | } | |
76efe6da | 213 | } |
a2818ee4 | 214 | |
76efe6da YC |
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(&objs[i], SV_ID1); | |
270f7806 YC |
219 | if (!sv) { |
220 | ret = -EINVAL; | |
221 | goto out; | |
222 | } | |
76efe6da YC |
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(&objs[i], SV_ID2); | |
270f7806 YC |
229 | if (!sv) { |
230 | ret = -EINVAL; | |
231 | goto out; | |
232 | } | |
76efe6da YC |
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 | } | |
a2818ee4 | 237 | |
76efe6da YC |
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(pndup[i]); | |
242 | ||
243 | sv = shadow_get_or_alloc(&objs[i], SV_ID1, sizeof(pndup[i]), | |
244 | GFP_KERNEL, shadow_ctor, &pndup[i]); | |
270f7806 YC |
245 | if (!sv) { |
246 | ret = -EINVAL; | |
247 | goto out; | |
248 | } | |
76efe6da YC |
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 | } | |
a2818ee4 | 253 | |
76efe6da YC |
254 | /* pass 4: free <objs[*], SV_ID1> pairs of svars, verify removal */ |
255 | for (i = 0; i < NUM_OBJS; i++) { | |
256 | shadow_free(&objs[i], SV_ID1, shadow_dtor); /* 'char' pairs */ | |
257 | sv = shadow_get(&objs[i], SV_ID1); | |
258 | if (!sv) | |
259 | pr_info(" got expected NULL result\n"); | |
260 | } | |
a2818ee4 | 261 | |
76efe6da YC |
262 | /* pass 5: check we still find <objs[*], SV_ID2> svar pairs */ |
263 | for (i = 0; i < NUM_OBJS; i++) { | |
264 | sv = shadow_get(&objs[i], SV_ID2); /* 'int' pairs */ | |
270f7806 YC |
265 | if (!sv) { |
266 | ret = -EINVAL; | |
267 | goto out; | |
268 | } | |
76efe6da YC |
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 | } | |
a2818ee4 | 273 | |
76efe6da YC |
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(&objs[i], SV_ID2); | |
278 | if (!sv) | |
279 | pr_info(" got expected NULL result\n"); | |
280 | } | |
a2818ee4 JL |
281 | |
282 | free_ptr_list(); | |
283 | ||
284 | return 0; | |
270f7806 YC |
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; | |
a2818ee4 JL |
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"); |