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
This commit is contained in:
Linus Torvalds 2020-01-27 10:54:24 -08:00
commit 715d128569
4 changed files with 99 additions and 67 deletions

View File

@ -60,36 +60,43 @@ static int ptr_id(void *ptr)
*/
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",
__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,
gfp_t gfp_flags, klp_shadow_ctor_t ctor,
void *ctor_data)
{
void *ret = klp_shadow_alloc(obj, id, size, gfp_flags, ctor,
ctor_data);
int **var = 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",
__func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
ptr_id(ctor_data), ptr_id(ret));
return ret;
ptr_id(*var), ptr_id(sv));
return sv;
}
static void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size,
gfp_t gfp_flags, klp_shadow_ctor_t ctor,
void *ctor_data)
{
void *ret = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor,
ctor_data);
int **var = 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",
__func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
ptr_id(ctor_data), ptr_id(ret));
return ret;
ptr_id(*var), ptr_id(sv));
return sv;
}
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)
/* Shadow variable constructor - remember simple pointer data */
static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
{
int **shadow_int = shadow_data;
*shadow_int = ctor_data;
int **sv = shadow_data;
int **var = ctor_data;
if (!var)
return -EINVAL;
*sv = *var;
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;
}
static void shadow_dtor(void *obj, void *shadow_data)
{
int **sv = shadow_data;
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)
{
void *obj = THIS_MODULE;
int id = 0x1234;
size_t size = sizeof(int *);
gfp_t gfp_flags = GFP_KERNEL;
int var1, var2, var3, var4;
int *pv1, *pv2, *pv3, *pv4;
int **sv1, **sv2, **sv3, **sv4;
void *ret;
int **sv;
pv1 = &var1;
pv2 = &var2;
pv3 = &var3;
pv4 = &var4;
ptr_id(NULL);
ptr_id(&var1);
ptr_id(&var2);
ptr_id(&var3);
ptr_id(&var4);
ptr_id(pv1);
ptr_id(pv2);
ptr_id(pv3);
ptr_id(pv4);
/*
* With an empty shadow variable hash table, expect not to find
* any matches.
*/
ret = shadow_get(obj, id);
if (!ret)
sv = shadow_get(obj, id);
if (!sv)
pr_info(" got expected NULL result\n");
/*
* 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)
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)
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)
return -ENOMEM;
@ -169,23 +188,23 @@ static int test_klp_shadow_vars_init(void)
* Verify we can find our new shadow variables and that they point
* to expected data.
*/
ret = shadow_get(obj, id);
if (!ret)
sv = shadow_get(obj, id);
if (!sv)
return -EINVAL;
if (ret == sv1 && *sv1 == &var1)
if (sv == sv1 && *sv1 == pv1)
pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv1), ptr_id(*sv1));
ret = shadow_get(obj + 1, id);
if (!ret)
sv = shadow_get(obj + 1, id);
if (!sv)
return -EINVAL;
if (ret == sv2 && *sv2 == &var2)
if (sv == sv2 && *sv2 == pv2)
pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv2), ptr_id(*sv2));
ret = shadow_get(obj, id + 1);
if (!ret)
sv = shadow_get(obj, id + 1);
if (!sv)
return -EINVAL;
if (ret == sv3 && *sv3 == &var3)
if (sv == sv3 && *sv3 == pv3)
pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv3), ptr_id(*sv3));
@ -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>.
* 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)
return -ENOMEM;
ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
if (!ret)
sv = shadow_get_or_alloc(obj + 2, id, sizeof(pv4), gfp_flags, shadow_ctor, &pv4);
if (!sv)
return -EINVAL;
if (ret == sv4 && *sv4 == &var4)
if (sv == sv4 && *sv4 == pv4)
pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv4), ptr_id(*sv4));
@ -209,27 +228,27 @@ static int test_klp_shadow_vars_init(void)
* longer find them.
*/
shadow_free(obj, id, shadow_dtor); /* sv1 */
ret = shadow_get(obj, id);
if (!ret)
sv = shadow_get(obj, id);
if (!sv)
pr_info(" got expected NULL result\n");
shadow_free(obj + 1, id, shadow_dtor); /* sv2 */
ret = shadow_get(obj + 1, id);
if (!ret)
sv = shadow_get(obj + 1, id);
if (!sv)
pr_info(" got expected NULL result\n");
shadow_free(obj + 2, id, shadow_dtor); /* sv4 */
ret = shadow_get(obj + 2, id);
if (!ret)
sv = shadow_get(obj + 2, id);
if (!sv)
pr_info(" got expected NULL result\n");
/*
* We should still find an <id+1> variable.
*/
ret = shadow_get(obj, id + 1);
if (!ret)
sv = shadow_get(obj, id + 1);
if (!sv)
return -EINVAL;
if (ret == sv3 && *sv3 == &var3)
if (sv == sv3 && *sv3 == pv3)
pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv3), ptr_id(*sv3));
@ -237,8 +256,8 @@ static int test_klp_shadow_vars_init(void)
* Free all the <id+1> variables, too.
*/
shadow_free_all(id + 1, shadow_dtor); /* sv3 */
ret = shadow_get(obj, id);
if (!ret)
sv = shadow_get(obj, id);
if (!sv)
pr_info(" shadow_get() got expected NULL result\n");

View File

@ -52,17 +52,21 @@ struct dummy {
*/
static int shadow_leak_ctor(void *obj, void *shadow_data, void *ctor_data)
{
void **shadow_leak = shadow_data;
void *leak = ctor_data;
int **shadow_leak = shadow_data;
int **leak = ctor_data;
*shadow_leak = leak;
if (!ctor_data)
return -EINVAL;
*shadow_leak = *leak;
return 0;
}
static struct dummy *livepatch_fix1_dummy_alloc(void)
{
struct dummy *d;
void *leak;
int *leak;
int **shadow_leak;
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
@ -76,25 +80,34 @@ static struct dummy *livepatch_fix1_dummy_alloc(void)
* variable. A patched dummy_free routine can later fetch this
* pointer to handle resource release.
*/
leak = kzalloc(sizeof(int), GFP_KERNEL);
if (!leak) {
kfree(d);
return NULL;
}
leak = kzalloc(sizeof(*leak), GFP_KERNEL);
if (!leak)
goto err_leak;
klp_shadow_alloc(d, SV_LEAK, sizeof(leak), GFP_KERNEL,
shadow_leak_ctor, leak);
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;
}
pr_info("%s: dummy @ %p, expires @ %lx\n",
__func__, d, d->jiffies_expire);
return d;
err_shadow:
kfree(leak);
err_leak:
kfree(d);
return NULL;
}
static void livepatch_fix1_dummy_leak_dtor(void *obj, void *shadow_data)
{
void *d = obj;
void **shadow_leak = shadow_data;
int **shadow_leak = shadow_data;
kfree(*shadow_leak);
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)
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

View File

@ -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)
{
void *d = obj;
void **shadow_leak = shadow_data;
int **shadow_leak = shadow_data;
kfree(*shadow_leak);
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)
static void livepatch_fix2_dummy_free(struct dummy *d)
{
void **shadow_leak;
int **shadow_leak;
int *shadow_count;
/* Patch: copy the memory leak patch from the fix1 module. */

View File

@ -95,7 +95,7 @@ struct dummy {
static __used noinline struct dummy *dummy_alloc(void)
{
struct dummy *d;
void *leak;
int *leak;
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
@ -105,7 +105,7 @@ static __used noinline struct dummy *dummy_alloc(void)
msecs_to_jiffies(1000 * EXPIRE_PERIOD);
/* Oops, forgot to save leak! */
leak = kzalloc(sizeof(int), GFP_KERNEL);
leak = kzalloc(sizeof(*leak), GFP_KERNEL);
if (!leak) {
kfree(d);
return NULL;