162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/list.h> 962306a36Sopenharmony_ci#include <linux/livepatch.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci * Keep a small list of pointers so that we can print address-agnostic 1462306a36Sopenharmony_ci * pointer values. Use a rolling integer count to differentiate the values. 1562306a36Sopenharmony_ci * Ironically we could have used the shadow variable API to do this, but 1662306a36Sopenharmony_ci * let's not lean too heavily on the very code we're testing. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_cistatic LIST_HEAD(ptr_list); 1962306a36Sopenharmony_cistruct shadow_ptr { 2062306a36Sopenharmony_ci void *ptr; 2162306a36Sopenharmony_ci int id; 2262306a36Sopenharmony_ci struct list_head list; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void free_ptr_list(void) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct shadow_ptr *sp, *tmp_sp; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci list_for_each_entry_safe(sp, tmp_sp, &ptr_list, list) { 3062306a36Sopenharmony_ci list_del(&sp->list); 3162306a36Sopenharmony_ci kfree(sp); 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int ptr_id(void *ptr) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct shadow_ptr *sp; 3862306a36Sopenharmony_ci static int count; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci list_for_each_entry(sp, &ptr_list, list) { 4162306a36Sopenharmony_ci if (sp->ptr == ptr) 4262306a36Sopenharmony_ci return sp->id; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci sp = kmalloc(sizeof(*sp), GFP_ATOMIC); 4662306a36Sopenharmony_ci if (!sp) 4762306a36Sopenharmony_ci return -ENOMEM; 4862306a36Sopenharmony_ci sp->ptr = ptr; 4962306a36Sopenharmony_ci sp->id = count++; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci list_add(&sp->list, &ptr_list); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return sp->id; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * Shadow variable wrapper functions that echo the function and arguments 5862306a36Sopenharmony_ci * to the kernel log for testing verification. Don't display raw pointers, 5962306a36Sopenharmony_ci * but use the ptr_id() value instead. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistatic void *shadow_get(void *obj, unsigned long id) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci int **sv; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci sv = klp_shadow_get(obj, id); 6662306a36Sopenharmony_ci pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n", 6762306a36Sopenharmony_ci __func__, ptr_id(obj), id, ptr_id(sv)); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return sv; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void *shadow_alloc(void *obj, unsigned long id, size_t size, 7362306a36Sopenharmony_ci gfp_t gfp_flags, klp_shadow_ctor_t ctor, 7462306a36Sopenharmony_ci void *ctor_data) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int **var = ctor_data; 7762306a36Sopenharmony_ci int **sv; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci sv = klp_shadow_alloc(obj, id, size, gfp_flags, ctor, var); 8062306a36Sopenharmony_ci 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", 8162306a36Sopenharmony_ci __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor), 8262306a36Sopenharmony_ci ptr_id(*var), ptr_id(sv)); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return sv; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size, 8862306a36Sopenharmony_ci gfp_t gfp_flags, klp_shadow_ctor_t ctor, 8962306a36Sopenharmony_ci void *ctor_data) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int **var = ctor_data; 9262306a36Sopenharmony_ci int **sv; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci sv = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, var); 9562306a36Sopenharmony_ci 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", 9662306a36Sopenharmony_ci __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor), 9762306a36Sopenharmony_ci ptr_id(*var), ptr_id(sv)); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return sv; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci klp_shadow_free(obj, id, dtor); 10562306a36Sopenharmony_ci pr_info("klp_%s(obj=PTR%d, id=0x%lx, dtor=PTR%d)\n", 10662306a36Sopenharmony_ci __func__, ptr_id(obj), id, ptr_id(dtor)); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci klp_shadow_free_all(id, dtor); 11262306a36Sopenharmony_ci pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n", __func__, id, ptr_id(dtor)); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* Shadow variable constructor - remember simple pointer data */ 11762306a36Sopenharmony_cistatic int shadow_ctor(void *obj, void *shadow_data, void *ctor_data) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int **sv = shadow_data; 12062306a36Sopenharmony_ci int **var = ctor_data; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (!var) 12362306a36Sopenharmony_ci return -EINVAL; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci *sv = *var; 12662306a36Sopenharmony_ci pr_info("%s: PTR%d -> PTR%d\n", __func__, ptr_id(sv), ptr_id(*var)); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * With more than one item to free in the list, order is not determined and 13362306a36Sopenharmony_ci * shadow_dtor will not be passed to shadow_free_all() which would make the 13462306a36Sopenharmony_ci * test fail. (see pass 6) 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_cistatic void shadow_dtor(void *obj, void *shadow_data) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int **sv = shadow_data; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n", 14162306a36Sopenharmony_ci __func__, ptr_id(obj), ptr_id(sv)); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* number of objects we simulate that need shadow vars */ 14562306a36Sopenharmony_ci#define NUM_OBJS 3 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* dynamically created obj fields have the following shadow var id values */ 14862306a36Sopenharmony_ci#define SV_ID1 0x1234 14962306a36Sopenharmony_ci#define SV_ID2 0x1235 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* 15262306a36Sopenharmony_ci * The main test case adds/removes new fields (shadow var) to each of these 15362306a36Sopenharmony_ci * test structure instances. The last group of fields in the struct represent 15462306a36Sopenharmony_ci * the idea that shadow variables may be added and removed to and from the 15562306a36Sopenharmony_ci * struct during execution. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistruct test_object { 15862306a36Sopenharmony_ci /* add anything here below and avoid to define an empty struct */ 15962306a36Sopenharmony_ci struct shadow_ptr sp; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* these represent shadow vars added and removed with SV_ID{1,2} */ 16262306a36Sopenharmony_ci /* char nfield1; */ 16362306a36Sopenharmony_ci /* int nfield2; */ 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int test_klp_shadow_vars_init(void) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct test_object objs[NUM_OBJS]; 16962306a36Sopenharmony_ci char nfields1[NUM_OBJS], *pnfields1[NUM_OBJS], **sv1[NUM_OBJS]; 17062306a36Sopenharmony_ci char *pndup[NUM_OBJS]; 17162306a36Sopenharmony_ci int nfields2[NUM_OBJS], *pnfields2[NUM_OBJS], **sv2[NUM_OBJS]; 17262306a36Sopenharmony_ci void **sv; 17362306a36Sopenharmony_ci int ret; 17462306a36Sopenharmony_ci int i; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ptr_id(NULL); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * With an empty shadow variable hash table, expect not to find 18062306a36Sopenharmony_ci * any matches. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci sv = shadow_get(&objs[0], SV_ID1); 18362306a36Sopenharmony_ci if (!sv) 18462306a36Sopenharmony_ci pr_info(" got expected NULL result\n"); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* pass 1: init & alloc a char+int pair of svars for each objs */ 18762306a36Sopenharmony_ci for (i = 0; i < NUM_OBJS; i++) { 18862306a36Sopenharmony_ci pnfields1[i] = &nfields1[i]; 18962306a36Sopenharmony_ci ptr_id(pnfields1[i]); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (i % 2) { 19262306a36Sopenharmony_ci sv1[i] = shadow_alloc(&objs[i], SV_ID1, 19362306a36Sopenharmony_ci sizeof(pnfields1[i]), GFP_KERNEL, 19462306a36Sopenharmony_ci shadow_ctor, &pnfields1[i]); 19562306a36Sopenharmony_ci } else { 19662306a36Sopenharmony_ci sv1[i] = shadow_get_or_alloc(&objs[i], SV_ID1, 19762306a36Sopenharmony_ci sizeof(pnfields1[i]), GFP_KERNEL, 19862306a36Sopenharmony_ci shadow_ctor, &pnfields1[i]); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci if (!sv1[i]) { 20162306a36Sopenharmony_ci ret = -ENOMEM; 20262306a36Sopenharmony_ci goto out; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci pnfields2[i] = &nfields2[i]; 20662306a36Sopenharmony_ci ptr_id(pnfields2[i]); 20762306a36Sopenharmony_ci sv2[i] = shadow_alloc(&objs[i], SV_ID2, sizeof(pnfields2[i]), 20862306a36Sopenharmony_ci GFP_KERNEL, shadow_ctor, &pnfields2[i]); 20962306a36Sopenharmony_ci if (!sv2[i]) { 21062306a36Sopenharmony_ci ret = -ENOMEM; 21162306a36Sopenharmony_ci goto out; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* pass 2: verify we find allocated svars and where they point to */ 21662306a36Sopenharmony_ci for (i = 0; i < NUM_OBJS; i++) { 21762306a36Sopenharmony_ci /* check the "char" svar for all objects */ 21862306a36Sopenharmony_ci sv = shadow_get(&objs[i], SV_ID1); 21962306a36Sopenharmony_ci if (!sv) { 22062306a36Sopenharmony_ci ret = -EINVAL; 22162306a36Sopenharmony_ci goto out; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci if ((char **)sv == sv1[i] && *sv1[i] == pnfields1[i]) 22462306a36Sopenharmony_ci pr_info(" got expected PTR%d -> PTR%d result\n", 22562306a36Sopenharmony_ci ptr_id(sv1[i]), ptr_id(*sv1[i])); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* check the "int" svar for all objects */ 22862306a36Sopenharmony_ci sv = shadow_get(&objs[i], SV_ID2); 22962306a36Sopenharmony_ci if (!sv) { 23062306a36Sopenharmony_ci ret = -EINVAL; 23162306a36Sopenharmony_ci goto out; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci if ((int **)sv == sv2[i] && *sv2[i] == pnfields2[i]) 23462306a36Sopenharmony_ci pr_info(" got expected PTR%d -> PTR%d result\n", 23562306a36Sopenharmony_ci ptr_id(sv2[i]), ptr_id(*sv2[i])); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* pass 3: verify that 'get_or_alloc' returns already allocated svars */ 23962306a36Sopenharmony_ci for (i = 0; i < NUM_OBJS; i++) { 24062306a36Sopenharmony_ci pndup[i] = &nfields1[i]; 24162306a36Sopenharmony_ci ptr_id(pndup[i]); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci sv = shadow_get_or_alloc(&objs[i], SV_ID1, sizeof(pndup[i]), 24462306a36Sopenharmony_ci GFP_KERNEL, shadow_ctor, &pndup[i]); 24562306a36Sopenharmony_ci if (!sv) { 24662306a36Sopenharmony_ci ret = -EINVAL; 24762306a36Sopenharmony_ci goto out; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci if ((char **)sv == sv1[i] && *sv1[i] == pnfields1[i]) 25062306a36Sopenharmony_ci pr_info(" got expected PTR%d -> PTR%d result\n", 25162306a36Sopenharmony_ci ptr_id(sv1[i]), ptr_id(*sv1[i])); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* pass 4: free <objs[*], SV_ID1> pairs of svars, verify removal */ 25562306a36Sopenharmony_ci for (i = 0; i < NUM_OBJS; i++) { 25662306a36Sopenharmony_ci shadow_free(&objs[i], SV_ID1, shadow_dtor); /* 'char' pairs */ 25762306a36Sopenharmony_ci sv = shadow_get(&objs[i], SV_ID1); 25862306a36Sopenharmony_ci if (!sv) 25962306a36Sopenharmony_ci pr_info(" got expected NULL result\n"); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* pass 5: check we still find <objs[*], SV_ID2> svar pairs */ 26362306a36Sopenharmony_ci for (i = 0; i < NUM_OBJS; i++) { 26462306a36Sopenharmony_ci sv = shadow_get(&objs[i], SV_ID2); /* 'int' pairs */ 26562306a36Sopenharmony_ci if (!sv) { 26662306a36Sopenharmony_ci ret = -EINVAL; 26762306a36Sopenharmony_ci goto out; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci if ((int **)sv == sv2[i] && *sv2[i] == pnfields2[i]) 27062306a36Sopenharmony_ci pr_info(" got expected PTR%d -> PTR%d result\n", 27162306a36Sopenharmony_ci ptr_id(sv2[i]), ptr_id(*sv2[i])); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* pass 6: free all the <objs[*], SV_ID2> svar pairs too. */ 27562306a36Sopenharmony_ci shadow_free_all(SV_ID2, NULL); /* 'int' pairs */ 27662306a36Sopenharmony_ci for (i = 0; i < NUM_OBJS; i++) { 27762306a36Sopenharmony_ci sv = shadow_get(&objs[i], SV_ID2); 27862306a36Sopenharmony_ci if (!sv) 27962306a36Sopenharmony_ci pr_info(" got expected NULL result\n"); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci free_ptr_list(); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return 0; 28562306a36Sopenharmony_ciout: 28662306a36Sopenharmony_ci shadow_free_all(SV_ID1, NULL); /* 'char' pairs */ 28762306a36Sopenharmony_ci shadow_free_all(SV_ID2, NULL); /* 'int' pairs */ 28862306a36Sopenharmony_ci free_ptr_list(); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void test_klp_shadow_vars_exit(void) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cimodule_init(test_klp_shadow_vars_init); 29862306a36Sopenharmony_cimodule_exit(test_klp_shadow_vars_exit); 29962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 30062306a36Sopenharmony_ciMODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>"); 30162306a36Sopenharmony_ciMODULE_DESCRIPTION("Livepatch test: shadow variables"); 302