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 | { | |
63 | void *ret = klp_shadow_get(obj, id); | |
64 | ||
65 | pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n", | |
66 | __func__, ptr_id(obj), id, ptr_id(ret)); | |
67 | ||
68 | return ret; | |
69 | } | |
70 | ||
71 | static void *shadow_alloc(void *obj, unsigned long id, size_t size, | |
72 | gfp_t gfp_flags, klp_shadow_ctor_t ctor, | |
73 | void *ctor_data) | |
74 | { | |
75 | void *ret = klp_shadow_alloc(obj, id, size, gfp_flags, ctor, | |
76 | ctor_data); | |
77 | 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", | |
78 | __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor), | |
79 | ptr_id(ctor_data), ptr_id(ret)); | |
80 | return ret; | |
81 | } | |
82 | ||
83 | static void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size, | |
84 | gfp_t gfp_flags, klp_shadow_ctor_t ctor, | |
85 | void *ctor_data) | |
86 | { | |
87 | void *ret = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, | |
88 | ctor_data); | |
89 | 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", | |
90 | __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor), | |
91 | ptr_id(ctor_data), ptr_id(ret)); | |
92 | return ret; | |
93 | } | |
94 | ||
95 | static void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor) | |
96 | { | |
97 | klp_shadow_free(obj, id, dtor); | |
98 | pr_info("klp_%s(obj=PTR%d, id=0x%lx, dtor=PTR%d)\n", | |
99 | __func__, ptr_id(obj), id, ptr_id(dtor)); | |
100 | } | |
101 | ||
102 | static void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor) | |
103 | { | |
104 | klp_shadow_free_all(id, dtor); | |
105 | pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n", | |
106 | __func__, id, ptr_id(dtor)); | |
107 | } | |
108 | ||
109 | ||
110 | /* Shadow variable constructor - remember simple pointer data */ | |
111 | static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data) | |
112 | { | |
113 | int **shadow_int = shadow_data; | |
114 | *shadow_int = ctor_data; | |
115 | pr_info("%s: PTR%d -> PTR%d\n", | |
116 | __func__, ptr_id(shadow_int), ptr_id(ctor_data)); | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
121 | static void shadow_dtor(void *obj, void *shadow_data) | |
122 | { | |
123 | pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n", | |
124 | __func__, ptr_id(obj), ptr_id(shadow_data)); | |
125 | } | |
126 | ||
127 | static int test_klp_shadow_vars_init(void) | |
128 | { | |
129 | void *obj = THIS_MODULE; | |
130 | int id = 0x1234; | |
131 | size_t size = sizeof(int *); | |
132 | gfp_t gfp_flags = GFP_KERNEL; | |
133 | ||
134 | int var1, var2, var3, var4; | |
135 | int **sv1, **sv2, **sv3, **sv4; | |
136 | ||
137 | void *ret; | |
138 | ||
139 | ptr_id(NULL); | |
140 | ptr_id(&var1); | |
141 | ptr_id(&var2); | |
142 | ptr_id(&var3); | |
143 | ptr_id(&var4); | |
144 | ||
145 | /* | |
146 | * With an empty shadow variable hash table, expect not to find | |
147 | * any matches. | |
148 | */ | |
149 | ret = shadow_get(obj, id); | |
150 | if (!ret) | |
151 | pr_info(" got expected NULL result\n"); | |
152 | ||
153 | /* | |
154 | * Allocate a few shadow variables with different <obj> and <id>. | |
155 | */ | |
156 | sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1); | |
49ee4dd2 PM |
157 | if (!sv1) |
158 | return -ENOMEM; | |
159 | ||
a2818ee4 | 160 | sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2); |
49ee4dd2 PM |
161 | if (!sv2) |
162 | return -ENOMEM; | |
163 | ||
a2818ee4 | 164 | sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3); |
49ee4dd2 PM |
165 | if (!sv3) |
166 | return -ENOMEM; | |
a2818ee4 JL |
167 | |
168 | /* | |
169 | * Verify we can find our new shadow variables and that they point | |
170 | * to expected data. | |
171 | */ | |
172 | ret = shadow_get(obj, id); | |
49ee4dd2 PM |
173 | if (!ret) |
174 | return -EINVAL; | |
a2818ee4 JL |
175 | if (ret == sv1 && *sv1 == &var1) |
176 | pr_info(" got expected PTR%d -> PTR%d result\n", | |
177 | ptr_id(sv1), ptr_id(*sv1)); | |
49ee4dd2 | 178 | |
a2818ee4 | 179 | ret = shadow_get(obj + 1, id); |
49ee4dd2 PM |
180 | if (!ret) |
181 | return -EINVAL; | |
a2818ee4 JL |
182 | if (ret == sv2 && *sv2 == &var2) |
183 | pr_info(" got expected PTR%d -> PTR%d result\n", | |
184 | ptr_id(sv2), ptr_id(*sv2)); | |
185 | ret = shadow_get(obj, id + 1); | |
49ee4dd2 PM |
186 | if (!ret) |
187 | return -EINVAL; | |
a2818ee4 JL |
188 | if (ret == sv3 && *sv3 == &var3) |
189 | pr_info(" got expected PTR%d -> PTR%d result\n", | |
190 | ptr_id(sv3), ptr_id(*sv3)); | |
191 | ||
192 | /* | |
193 | * Allocate or get a few more, this time with the same <obj>, <id>. | |
194 | * The second invocation should return the same shadow var. | |
195 | */ | |
196 | sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4); | |
49ee4dd2 PM |
197 | if (!sv4) |
198 | return -ENOMEM; | |
199 | ||
a2818ee4 | 200 | ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4); |
49ee4dd2 PM |
201 | if (!ret) |
202 | return -EINVAL; | |
a2818ee4 JL |
203 | if (ret == sv4 && *sv4 == &var4) |
204 | pr_info(" got expected PTR%d -> PTR%d result\n", | |
205 | ptr_id(sv4), ptr_id(*sv4)); | |
206 | ||
207 | /* | |
208 | * Free the <obj=*, id> shadow variables and check that we can no | |
209 | * longer find them. | |
210 | */ | |
211 | shadow_free(obj, id, shadow_dtor); /* sv1 */ | |
212 | ret = shadow_get(obj, id); | |
213 | if (!ret) | |
214 | pr_info(" got expected NULL result\n"); | |
215 | ||
216 | shadow_free(obj + 1, id, shadow_dtor); /* sv2 */ | |
217 | ret = shadow_get(obj + 1, id); | |
218 | if (!ret) | |
219 | pr_info(" got expected NULL result\n"); | |
220 | ||
221 | shadow_free(obj + 2, id, shadow_dtor); /* sv4 */ | |
222 | ret = shadow_get(obj + 2, id); | |
223 | if (!ret) | |
224 | pr_info(" got expected NULL result\n"); | |
225 | ||
226 | /* | |
227 | * We should still find an <id+1> variable. | |
228 | */ | |
229 | ret = shadow_get(obj, id + 1); | |
49ee4dd2 PM |
230 | if (!ret) |
231 | return -EINVAL; | |
a2818ee4 JL |
232 | if (ret == sv3 && *sv3 == &var3) |
233 | pr_info(" got expected PTR%d -> PTR%d result\n", | |
234 | ptr_id(sv3), ptr_id(*sv3)); | |
235 | ||
236 | /* | |
237 | * Free all the <id+1> variables, too. | |
238 | */ | |
239 | shadow_free_all(id + 1, shadow_dtor); /* sv3 */ | |
240 | ret = shadow_get(obj, id); | |
241 | if (!ret) | |
242 | pr_info(" shadow_get() got expected NULL result\n"); | |
243 | ||
244 | ||
245 | free_ptr_list(); | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | static void test_klp_shadow_vars_exit(void) | |
251 | { | |
252 | } | |
253 | ||
254 | module_init(test_klp_shadow_vars_init); | |
255 | module_exit(test_klp_shadow_vars_exit); | |
256 | MODULE_LICENSE("GPL"); | |
257 | MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>"); | |
258 | MODULE_DESCRIPTION("Livepatch test: shadow variables"); |