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:
commit
715d128569
|
@ -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");
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue