Commit 715d1285 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching

Pull livepatching updates from Jiri Kosina:
 "Fixes for selftests and samples for 'shadow variables' livepatching
  feature, from Petr Mladek"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching:
  livepatch: Handle allocation failure in the sample of shadow variable API
  livepatch/samples/selftest: Use klp_shadow_alloc() API correctly
  livepatch/selftest: Clean up shadow variable names and type
  livepatch/sample: Use the right type for the leaking data pointer
parents 12fb2b99 f46e49a9
...@@ -60,36 +60,43 @@ static int ptr_id(void *ptr) ...@@ -60,36 +60,43 @@ static int ptr_id(void *ptr)
*/ */
static void *shadow_get(void *obj, unsigned long id) static void *shadow_get(void *obj, unsigned long id)
{ {
void *ret = klp_shadow_get(obj, id); int **sv;
sv = klp_shadow_get(obj, id);
pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n", pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n",
__func__, ptr_id(obj), id, ptr_id(ret)); __func__, ptr_id(obj), id, ptr_id(sv));
return ret; return sv;
} }
static void *shadow_alloc(void *obj, unsigned long id, size_t size, static void *shadow_alloc(void *obj, unsigned long id, size_t size,
gfp_t gfp_flags, klp_shadow_ctor_t ctor, gfp_t gfp_flags, klp_shadow_ctor_t ctor,
void *ctor_data) void *ctor_data)
{ {
void *ret = klp_shadow_alloc(obj, id, size, gfp_flags, ctor, int **var = ctor_data;
ctor_data); int **sv;
sv = klp_shadow_alloc(obj, id, size, gfp_flags, ctor, var);
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", 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",
__func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor), __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
ptr_id(ctor_data), ptr_id(ret)); ptr_id(*var), ptr_id(sv));
return ret;
return sv;
} }
static void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size, static void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size,
gfp_t gfp_flags, klp_shadow_ctor_t ctor, gfp_t gfp_flags, klp_shadow_ctor_t ctor,
void *ctor_data) void *ctor_data)
{ {
void *ret = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, int **var = ctor_data;
ctor_data); int **sv;
sv = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, var);
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", 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",
__func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor), __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
ptr_id(ctor_data), ptr_id(ret)); ptr_id(*var), ptr_id(sv));
return ret;
return sv;
} }
static void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor) static void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor)
...@@ -110,58 +117,70 @@ static void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor) ...@@ -110,58 +117,70 @@ static void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
/* Shadow variable constructor - remember simple pointer data */ /* Shadow variable constructor - remember simple pointer data */
static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data) static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
{ {
int **shadow_int = shadow_data; int **sv = shadow_data;
*shadow_int = ctor_data; int **var = ctor_data;
if (!var)
return -EINVAL;
*sv = *var;
pr_info("%s: PTR%d -> PTR%d\n", pr_info("%s: PTR%d -> PTR%d\n",
__func__, ptr_id(shadow_int), ptr_id(ctor_data)); __func__, ptr_id(sv), ptr_id(*var));
return 0; return 0;
} }
static void shadow_dtor(void *obj, void *shadow_data) static void shadow_dtor(void *obj, void *shadow_data)
{ {
int **sv = shadow_data;
pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n", pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n",
__func__, ptr_id(obj), ptr_id(shadow_data)); __func__, ptr_id(obj), ptr_id(sv));
} }
static int test_klp_shadow_vars_init(void) static int test_klp_shadow_vars_init(void)
{ {
void *obj = THIS_MODULE; void *obj = THIS_MODULE;
int id = 0x1234; int id = 0x1234;
size_t size = sizeof(int *);
gfp_t gfp_flags = GFP_KERNEL; gfp_t gfp_flags = GFP_KERNEL;
int var1, var2, var3, var4; int var1, var2, var3, var4;
int *pv1, *pv2, *pv3, *pv4;
int **sv1, **sv2, **sv3, **sv4; int **sv1, **sv2, **sv3, **sv4;
void *ret; int **sv;
pv1 = &var1;
pv2 = &var2;
pv3 = &var3;
pv4 = &var4;
ptr_id(NULL); ptr_id(NULL);
ptr_id(&var1); ptr_id(pv1);
ptr_id(&var2); ptr_id(pv2);
ptr_id(&var3); ptr_id(pv3);
ptr_id(&var4); ptr_id(pv4);
/* /*
* With an empty shadow variable hash table, expect not to find * With an empty shadow variable hash table, expect not to find
* any matches. * any matches.
*/ */
ret = shadow_get(obj, id); sv = shadow_get(obj, id);
if (!ret) if (!sv)
pr_info(" got expected NULL result\n"); pr_info(" got expected NULL result\n");
/* /*
* Allocate a few shadow variables with different <obj> and <id>. * Allocate a few shadow variables with different <obj> and <id>.
*/ */
sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1); sv1 = shadow_alloc(obj, id, sizeof(pv1), gfp_flags, shadow_ctor, &pv1);
if (!sv1) if (!sv1)
return -ENOMEM; return -ENOMEM;
sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2); sv2 = shadow_alloc(obj + 1, id, sizeof(pv2), gfp_flags, shadow_ctor, &pv2);
if (!sv2) if (!sv2)
return -ENOMEM; return -ENOMEM;
sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3); sv3 = shadow_alloc(obj, id + 1, sizeof(pv3), gfp_flags, shadow_ctor, &pv3);
if (!sv3) if (!sv3)
return -ENOMEM; return -ENOMEM;
...@@ -169,23 +188,23 @@ static int test_klp_shadow_vars_init(void) ...@@ -169,23 +188,23 @@ static int test_klp_shadow_vars_init(void)
* Verify we can find our new shadow variables and that they point * Verify we can find our new shadow variables and that they point
* to expected data. * to expected data.
*/ */
ret = shadow_get(obj, id); sv = shadow_get(obj, id);
if (!ret) if (!sv)
return -EINVAL; return -EINVAL;
if (ret == sv1 && *sv1 == &var1) if (sv == sv1 && *sv1 == pv1)
pr_info(" got expected PTR%d -> PTR%d result\n", pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv1), ptr_id(*sv1)); ptr_id(sv1), ptr_id(*sv1));
ret = shadow_get(obj + 1, id); sv = shadow_get(obj + 1, id);
if (!ret) if (!sv)
return -EINVAL; return -EINVAL;
if (ret == sv2 && *sv2 == &var2) if (sv == sv2 && *sv2 == pv2)
pr_info(" got expected PTR%d -> PTR%d result\n", pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv2), ptr_id(*sv2)); ptr_id(sv2), ptr_id(*sv2));
ret = shadow_get(obj, id + 1); sv = shadow_get(obj, id + 1);
if (!ret) if (!sv)
return -EINVAL; return -EINVAL;
if (ret == sv3 && *sv3 == &var3) if (sv == sv3 && *sv3 == pv3)
pr_info(" got expected PTR%d -> PTR%d result\n", pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv3), ptr_id(*sv3)); ptr_id(sv3), ptr_id(*sv3));
...@@ -193,14 +212,14 @@ static int test_klp_shadow_vars_init(void) ...@@ -193,14 +212,14 @@ static int test_klp_shadow_vars_init(void)
* Allocate or get a few more, this time with the same <obj>, <id>. * Allocate or get a few more, this time with the same <obj>, <id>.
* The second invocation should return the same shadow var. * The second invocation should return the same shadow var.
*/ */
sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4); sv4 = shadow_get_or_alloc(obj + 2, id, sizeof(pv4), gfp_flags, shadow_ctor, &pv4);
if (!sv4) if (!sv4)
return -ENOMEM; return -ENOMEM;
ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4); sv = shadow_get_or_alloc(obj + 2, id, sizeof(pv4), gfp_flags, shadow_ctor, &pv4);
if (!ret) if (!sv)
return -EINVAL; return -EINVAL;
if (ret == sv4 && *sv4 == &var4) if (sv == sv4 && *sv4 == pv4)
pr_info(" got expected PTR%d -> PTR%d result\n", pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv4), ptr_id(*sv4)); ptr_id(sv4), ptr_id(*sv4));
...@@ -209,27 +228,27 @@ static int test_klp_shadow_vars_init(void) ...@@ -209,27 +228,27 @@ static int test_klp_shadow_vars_init(void)
* longer find them. * longer find them.
*/ */
shadow_free(obj, id, shadow_dtor); /* sv1 */ shadow_free(obj, id, shadow_dtor); /* sv1 */
ret = shadow_get(obj, id); sv = shadow_get(obj, id);
if (!ret) if (!sv)
pr_info(" got expected NULL result\n"); pr_info(" got expected NULL result\n");
shadow_free(obj + 1, id, shadow_dtor); /* sv2 */ shadow_free(obj + 1, id, shadow_dtor); /* sv2 */
ret = shadow_get(obj + 1, id); sv = shadow_get(obj + 1, id);
if (!ret) if (!sv)
pr_info(" got expected NULL result\n"); pr_info(" got expected NULL result\n");
shadow_free(obj + 2, id, shadow_dtor); /* sv4 */ shadow_free(obj + 2, id, shadow_dtor); /* sv4 */
ret = shadow_get(obj + 2, id); sv = shadow_get(obj + 2, id);
if (!ret) if (!sv)
pr_info(" got expected NULL result\n"); pr_info(" got expected NULL result\n");
/* /*
* We should still find an <id+1> variable. * We should still find an <id+1> variable.
*/ */
ret = shadow_get(obj, id + 1); sv = shadow_get(obj, id + 1);
if (!ret) if (!sv)
return -EINVAL; return -EINVAL;
if (ret == sv3 && *sv3 == &var3) if (sv == sv3 && *sv3 == pv3)
pr_info(" got expected PTR%d -> PTR%d result\n", pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv3), ptr_id(*sv3)); ptr_id(sv3), ptr_id(*sv3));
...@@ -237,8 +256,8 @@ static int test_klp_shadow_vars_init(void) ...@@ -237,8 +256,8 @@ static int test_klp_shadow_vars_init(void)
* Free all the <id+1> variables, too. * Free all the <id+1> variables, too.
*/ */
shadow_free_all(id + 1, shadow_dtor); /* sv3 */ shadow_free_all(id + 1, shadow_dtor); /* sv3 */
ret = shadow_get(obj, id); sv = shadow_get(obj, id);
if (!ret) if (!sv)
pr_info(" shadow_get() got expected NULL result\n"); pr_info(" shadow_get() got expected NULL result\n");
......
...@@ -52,17 +52,21 @@ struct dummy { ...@@ -52,17 +52,21 @@ struct dummy {
*/ */
static int shadow_leak_ctor(void *obj, void *shadow_data, void *ctor_data) static int shadow_leak_ctor(void *obj, void *shadow_data, void *ctor_data)
{ {
void **shadow_leak = shadow_data; int **shadow_leak = shadow_data;
void *leak = ctor_data; int **leak = ctor_data;
*shadow_leak = leak; if (!ctor_data)
return -EINVAL;
*shadow_leak = *leak;
return 0; return 0;
} }
static struct dummy *livepatch_fix1_dummy_alloc(void) static struct dummy *livepatch_fix1_dummy_alloc(void)
{ {
struct dummy *d; struct dummy *d;
void *leak; int *leak;
int **shadow_leak;
d = kzalloc(sizeof(*d), GFP_KERNEL); d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d) if (!d)
...@@ -76,25 +80,34 @@ static struct dummy *livepatch_fix1_dummy_alloc(void) ...@@ -76,25 +80,34 @@ static struct dummy *livepatch_fix1_dummy_alloc(void)
* variable. A patched dummy_free routine can later fetch this * variable. A patched dummy_free routine can later fetch this
* pointer to handle resource release. * pointer to handle resource release.
*/ */
leak = kzalloc(sizeof(int), GFP_KERNEL); leak = kzalloc(sizeof(*leak), GFP_KERNEL);
if (!leak) { if (!leak)
kfree(d); goto err_leak;
return NULL;
shadow_leak = klp_shadow_alloc(d, SV_LEAK, sizeof(leak), GFP_KERNEL,
shadow_leak_ctor, &leak);
if (!shadow_leak) {
pr_err("%s: failed to allocate shadow variable for the leaking pointer: dummy @ %p, leak @ %p\n",
__func__, d, leak);
goto err_shadow;
} }
klp_shadow_alloc(d, SV_LEAK, sizeof(leak), GFP_KERNEL,
shadow_leak_ctor, leak);
pr_info("%s: dummy @ %p, expires @ %lx\n", pr_info("%s: dummy @ %p, expires @ %lx\n",
__func__, d, d->jiffies_expire); __func__, d, d->jiffies_expire);
return d; return d;
err_shadow:
kfree(leak);
err_leak:
kfree(d);
return NULL;
} }
static void livepatch_fix1_dummy_leak_dtor(void *obj, void *shadow_data) static void livepatch_fix1_dummy_leak_dtor(void *obj, void *shadow_data)
{ {
void *d = obj; void *d = obj;
void **shadow_leak = shadow_data; int **shadow_leak = shadow_data;
kfree(*shadow_leak); kfree(*shadow_leak);
pr_info("%s: dummy @ %p, prevented leak @ %p\n", pr_info("%s: dummy @ %p, prevented leak @ %p\n",
...@@ -103,7 +116,7 @@ static void livepatch_fix1_dummy_leak_dtor(void *obj, void *shadow_data) ...@@ -103,7 +116,7 @@ static void livepatch_fix1_dummy_leak_dtor(void *obj, void *shadow_data)
static void livepatch_fix1_dummy_free(struct dummy *d) static void livepatch_fix1_dummy_free(struct dummy *d)
{ {
void **shadow_leak; int **shadow_leak;
/* /*
* Patch: fetch the saved SV_LEAK shadow variable, detach and * Patch: fetch the saved SV_LEAK shadow variable, detach and
......
...@@ -59,7 +59,7 @@ static bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies) ...@@ -59,7 +59,7 @@ static bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies)
static void livepatch_fix2_dummy_leak_dtor(void *obj, void *shadow_data) static void livepatch_fix2_dummy_leak_dtor(void *obj, void *shadow_data)
{ {
void *d = obj; void *d = obj;
void **shadow_leak = shadow_data; int **shadow_leak = shadow_data;
kfree(*shadow_leak); kfree(*shadow_leak);
pr_info("%s: dummy @ %p, prevented leak @ %p\n", pr_info("%s: dummy @ %p, prevented leak @ %p\n",
...@@ -68,7 +68,7 @@ static void livepatch_fix2_dummy_leak_dtor(void *obj, void *shadow_data) ...@@ -68,7 +68,7 @@ static void livepatch_fix2_dummy_leak_dtor(void *obj, void *shadow_data)
static void livepatch_fix2_dummy_free(struct dummy *d) static void livepatch_fix2_dummy_free(struct dummy *d)
{ {
void **shadow_leak; int **shadow_leak;
int *shadow_count; int *shadow_count;
/* Patch: copy the memory leak patch from the fix1 module. */ /* Patch: copy the memory leak patch from the fix1 module. */
......
...@@ -95,7 +95,7 @@ struct dummy { ...@@ -95,7 +95,7 @@ struct dummy {
static __used noinline struct dummy *dummy_alloc(void) static __used noinline struct dummy *dummy_alloc(void)
{ {
struct dummy *d; struct dummy *d;
void *leak; int *leak;
d = kzalloc(sizeof(*d), GFP_KERNEL); d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d) if (!d)
...@@ -105,7 +105,7 @@ static __used noinline struct dummy *dummy_alloc(void) ...@@ -105,7 +105,7 @@ static __used noinline struct dummy *dummy_alloc(void)
msecs_to_jiffies(1000 * EXPIRE_PERIOD); msecs_to_jiffies(1000 * EXPIRE_PERIOD);
/* Oops, forgot to save leak! */ /* Oops, forgot to save leak! */
leak = kzalloc(sizeof(int), GFP_KERNEL); leak = kzalloc(sizeof(*leak), GFP_KERNEL);
if (!leak) { if (!leak) {
kfree(d); kfree(d);
return NULL; return NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment