Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 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) |
Joe Lawrence | 86e43f2 | 2019-02-04 14:56:51 +0100 | [diff] [blame] | 47 | return -ENOMEM; |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 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 | { |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 63 | int **sv; |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 64 | |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 65 | sv = klp_shadow_get(obj, id); |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 66 | pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n", |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 67 | __func__, ptr_id(obj), id, ptr_id(sv)); |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 68 | |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 69 | return sv; |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 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 | { |
Petr Mladek | be6da98 | 2020-01-16 16:31:44 +0100 | [diff] [blame] | 76 | int **var = ctor_data; |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 77 | int **sv; |
| 78 | |
| 79 | sv = klp_shadow_alloc(obj, id, size, gfp_flags, ctor, var); |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 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), |
Petr Mladek | be6da98 | 2020-01-16 16:31:44 +0100 | [diff] [blame] | 82 | ptr_id(*var), ptr_id(sv)); |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 83 | |
| 84 | return sv; |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 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 | { |
Petr Mladek | be6da98 | 2020-01-16 16:31:44 +0100 | [diff] [blame] | 91 | int **var = ctor_data; |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 92 | int **sv; |
| 93 | |
| 94 | sv = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, var); |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 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), |
Petr Mladek | be6da98 | 2020-01-16 16:31:44 +0100 | [diff] [blame] | 97 | ptr_id(*var), ptr_id(sv)); |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 98 | |
| 99 | return sv; |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 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); |
Yannick Cote | 6a26a9d | 2020-06-03 14:20:56 -0400 | [diff] [blame] | 112 | pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n", __func__, id, ptr_id(dtor)); |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 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 | { |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 119 | int **sv = shadow_data; |
Petr Mladek | be6da98 | 2020-01-16 16:31:44 +0100 | [diff] [blame] | 120 | int **var = ctor_data; |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 121 | |
Petr Mladek | be6da98 | 2020-01-16 16:31:44 +0100 | [diff] [blame] | 122 | if (!var) |
| 123 | return -EINVAL; |
| 124 | |
| 125 | *sv = *var; |
Yannick Cote | 6a26a9d | 2020-06-03 14:20:56 -0400 | [diff] [blame] | 126 | pr_info("%s: PTR%d -> PTR%d\n", __func__, ptr_id(sv), ptr_id(*var)); |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 127 | |
| 128 | return 0; |
| 129 | } |
| 130 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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 | */ |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 136 | static void shadow_dtor(void *obj, void *shadow_data) |
| 137 | { |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 138 | int **sv = shadow_data; |
| 139 | |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 140 | pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n", |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 141 | __func__, ptr_id(obj), ptr_id(sv)); |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 142 | } |
| 143 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 144 | /* number of objects we simulate that need shadow vars */ |
| 145 | #define NUM_OBJS 3 |
| 146 | |
Yannick Cote | 6a26a9d | 2020-06-03 14:20:56 -0400 | [diff] [blame] | 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 | |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 166 | static int test_klp_shadow_vars_init(void) |
| 167 | { |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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]; |
Yannick Cote | 6a26a9d | 2020-06-03 14:20:56 -0400 | [diff] [blame] | 172 | void **sv; |
Yannick Cote | 270f780 | 2020-06-03 14:20:58 -0400 | [diff] [blame] | 173 | int ret; |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 174 | int i; |
Petr Mladek | be6da98 | 2020-01-16 16:31:44 +0100 | [diff] [blame] | 175 | |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 176 | ptr_id(NULL); |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 177 | |
| 178 | /* |
| 179 | * With an empty shadow variable hash table, expect not to find |
| 180 | * any matches. |
| 181 | */ |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 182 | sv = shadow_get(&objs[0], SV_ID1); |
Petr Mladek | c24c57a | 2020-01-16 16:31:43 +0100 | [diff] [blame] | 183 | if (!sv) |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 184 | pr_info(" got expected NULL result\n"); |
| 185 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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]); |
Petr Mladek | 49ee4dd | 2019-02-04 14:56:52 +0100 | [diff] [blame] | 190 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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 | } |
Yannick Cote | 270f780 | 2020-06-03 14:20:58 -0400 | [diff] [blame] | 200 | if (!sv1[i]) { |
| 201 | ret = -ENOMEM; |
| 202 | goto out; |
| 203 | } |
Petr Mladek | 49ee4dd | 2019-02-04 14:56:52 +0100 | [diff] [blame] | 204 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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]); |
Yannick Cote | 270f780 | 2020-06-03 14:20:58 -0400 | [diff] [blame] | 209 | if (!sv2[i]) { |
| 210 | ret = -ENOMEM; |
| 211 | goto out; |
| 212 | } |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 213 | } |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 214 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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); |
Yannick Cote | 270f780 | 2020-06-03 14:20:58 -0400 | [diff] [blame] | 219 | if (!sv) { |
| 220 | ret = -EINVAL; |
| 221 | goto out; |
| 222 | } |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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])); |
Petr Mladek | 49ee4dd | 2019-02-04 14:56:52 +0100 | [diff] [blame] | 226 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 227 | /* check the "int" svar for all objects */ |
| 228 | sv = shadow_get(&objs[i], SV_ID2); |
Yannick Cote | 270f780 | 2020-06-03 14:20:58 -0400 | [diff] [blame] | 229 | if (!sv) { |
| 230 | ret = -EINVAL; |
| 231 | goto out; |
| 232 | } |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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 | } |
Yannick Cote | 6a26a9d | 2020-06-03 14:20:56 -0400 | [diff] [blame] | 237 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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]); |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 242 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 243 | sv = shadow_get_or_alloc(&objs[i], SV_ID1, sizeof(pndup[i]), |
| 244 | GFP_KERNEL, shadow_ctor, &pndup[i]); |
Yannick Cote | 270f780 | 2020-06-03 14:20:58 -0400 | [diff] [blame] | 245 | if (!sv) { |
| 246 | ret = -EINVAL; |
| 247 | goto out; |
| 248 | } |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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 | } |
Petr Mladek | 49ee4dd | 2019-02-04 14:56:52 +0100 | [diff] [blame] | 253 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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 | } |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 261 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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 */ |
Yannick Cote | 270f780 | 2020-06-03 14:20:58 -0400 | [diff] [blame] | 265 | if (!sv) { |
| 266 | ret = -EINVAL; |
| 267 | goto out; |
| 268 | } |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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 | } |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 273 | |
Yannick Cote | 76efe6d | 2020-06-03 14:20:57 -0400 | [diff] [blame] | 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 | } |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 281 | |
| 282 | free_ptr_list(); |
| 283 | |
| 284 | return 0; |
Yannick Cote | 270f780 | 2020-06-03 14:20:58 -0400 | [diff] [blame] | 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; |
Joe Lawrence | a2818ee | 2019-01-09 13:43:29 +0100 | [diff] [blame] | 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"); |