From 958ef1e39d24d6cb8bf2a7406130a98c9564230f Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 9 Jan 2019 13:43:23 +0100 Subject: livepatch: Simplify API by removing registration step The possibility to re-enable a registered patch was useful for immediate patches where the livepatch module had to stay until the system reboot. The improved consistency model allows to achieve the same result by unloading and loading the livepatch module again. Also we are going to add a feature called atomic replace. It will allow to create a patch that would replace all already registered patches. The aim is to handle dependent patches more securely. It will obsolete the stack of patches that helped to handle the dependencies so far. Then it might be unclear when a cumulative patch re-enabling is safe. It would be complicated to support the many modes. Instead we could actually make the API and code easier to understand. Therefore, remove the two step public API. All the checks and init calls are moved from klp_register_patch() to klp_enabled_patch(). Also the patch is automatically freed, including the sysfs interface when the transition to the disabled state is completed. As a result, there is never a disabled patch on the top of the stack. Therefore we do not need to check the stack in __klp_enable_patch(). And we could simplify the check in __klp_disable_patch(). Also the API and logic is much easier. It is enough to call klp_enable_patch() in module_init() call. The patch can be disabled by writing '0' into /sys/kernel/livepatch//enabled. Then the module can be removed once the transition finishes and sysfs interface is freed. The only problem is how to free the structures and kobjects safely. The operation is triggered from the sysfs interface. We could not put the related kobject from there because it would cause lock inversion between klp_mutex and kernfs locks, see kn->count lockdep map. Therefore, offload the free task to a workqueue. It is perfectly fine: + The patch can no longer be used in the livepatch operations. + The module could not be removed until the free operation finishes and module_put() is called. + The operation is asynchronous already when the first klp_try_complete_transition() fails and another call is queued with a delay. Suggested-by: Josh Poimboeuf Signed-off-by: Petr Mladek Acked-by: Miroslav Benes Acked-by: Josh Poimboeuf Signed-off-by: Jiri Kosina --- samples/livepatch/livepatch-callbacks-demo.c | 13 +------------ samples/livepatch/livepatch-sample.c | 13 +------------ samples/livepatch/livepatch-shadow-fix1.c | 14 +------------- samples/livepatch/livepatch-shadow-fix2.c | 14 +------------- 4 files changed, 4 insertions(+), 50 deletions(-) (limited to 'samples') diff --git a/samples/livepatch/livepatch-callbacks-demo.c b/samples/livepatch/livepatch-callbacks-demo.c index 72f9e6d1387b..62d97953ad02 100644 --- a/samples/livepatch/livepatch-callbacks-demo.c +++ b/samples/livepatch/livepatch-callbacks-demo.c @@ -195,22 +195,11 @@ static struct klp_patch patch = { static int livepatch_callbacks_demo_init(void) { - int ret; - - ret = klp_register_patch(&patch); - if (ret) - return ret; - ret = klp_enable_patch(&patch); - if (ret) { - WARN_ON(klp_unregister_patch(&patch)); - return ret; - } - return 0; + return klp_enable_patch(&patch); } static void livepatch_callbacks_demo_exit(void) { - WARN_ON(klp_unregister_patch(&patch)); } module_init(livepatch_callbacks_demo_init); diff --git a/samples/livepatch/livepatch-sample.c b/samples/livepatch/livepatch-sample.c index 2d554dd930e2..01c9cf003ca2 100644 --- a/samples/livepatch/livepatch-sample.c +++ b/samples/livepatch/livepatch-sample.c @@ -69,22 +69,11 @@ static struct klp_patch patch = { static int livepatch_init(void) { - int ret; - - ret = klp_register_patch(&patch); - if (ret) - return ret; - ret = klp_enable_patch(&patch); - if (ret) { - WARN_ON(klp_unregister_patch(&patch)); - return ret; - } - return 0; + return klp_enable_patch(&patch); } static void livepatch_exit(void) { - WARN_ON(klp_unregister_patch(&patch)); } module_init(livepatch_init); diff --git a/samples/livepatch/livepatch-shadow-fix1.c b/samples/livepatch/livepatch-shadow-fix1.c index e8f1bd6b29b1..a5a5cac21d4d 100644 --- a/samples/livepatch/livepatch-shadow-fix1.c +++ b/samples/livepatch/livepatch-shadow-fix1.c @@ -157,25 +157,13 @@ static struct klp_patch patch = { static int livepatch_shadow_fix1_init(void) { - int ret; - - ret = klp_register_patch(&patch); - if (ret) - return ret; - ret = klp_enable_patch(&patch); - if (ret) { - WARN_ON(klp_unregister_patch(&patch)); - return ret; - } - return 0; + return klp_enable_patch(&patch); } static void livepatch_shadow_fix1_exit(void) { /* Cleanup any existing SV_LEAK shadow variables */ klp_shadow_free_all(SV_LEAK, livepatch_fix1_dummy_leak_dtor); - - WARN_ON(klp_unregister_patch(&patch)); } module_init(livepatch_shadow_fix1_init); diff --git a/samples/livepatch/livepatch-shadow-fix2.c b/samples/livepatch/livepatch-shadow-fix2.c index b34c7bf83356..52de947b5526 100644 --- a/samples/livepatch/livepatch-shadow-fix2.c +++ b/samples/livepatch/livepatch-shadow-fix2.c @@ -129,25 +129,13 @@ static struct klp_patch patch = { static int livepatch_shadow_fix2_init(void) { - int ret; - - ret = klp_register_patch(&patch); - if (ret) - return ret; - ret = klp_enable_patch(&patch); - if (ret) { - WARN_ON(klp_unregister_patch(&patch)); - return ret; - } - return 0; + return klp_enable_patch(&patch); } static void livepatch_shadow_fix2_exit(void) { /* Cleanup any existing SV_COUNTER shadow variables */ klp_shadow_free_all(SV_COUNTER, NULL); - - WARN_ON(klp_unregister_patch(&patch)); } module_init(livepatch_shadow_fix2_init); -- cgit From b73d5dc72272c0012999f939476b703d269d21b6 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Thu, 24 Jan 2019 02:48:16 +0100 Subject: livepatch: samples: non static warnings fix Sparse reported warnings about non-static symbols. For the variables a simple static attribute is fine - for the functions referenced by livepatch via klp_func the symbol-names must be unmodified in the symbol table and the patchable code has to be emitted. The resolution is to attach __used attribute to the shared statically declared functions. Link: https://lore.kernel.org/lkml/1544965657-26804-1-git-send-email-hofrat@osadl.org/ Suggested-by: Joe Lawrence Signed-off-by: Nicholas Mc Guire Acked-by: Miroslav Benes Signed-off-by: Jiri Kosina --- samples/livepatch/livepatch-shadow-fix1.c | 4 ++-- samples/livepatch/livepatch-shadow-fix2.c | 4 ++-- samples/livepatch/livepatch-shadow-mod.c | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) (limited to 'samples') diff --git a/samples/livepatch/livepatch-shadow-fix1.c b/samples/livepatch/livepatch-shadow-fix1.c index e8f1bd6b29b1..dd49c9473580 100644 --- a/samples/livepatch/livepatch-shadow-fix1.c +++ b/samples/livepatch/livepatch-shadow-fix1.c @@ -71,7 +71,7 @@ static int shadow_leak_ctor(void *obj, void *shadow_data, void *ctor_data) return 0; } -struct dummy *livepatch_fix1_dummy_alloc(void) +static struct dummy *livepatch_fix1_dummy_alloc(void) { struct dummy *d; void *leak; @@ -113,7 +113,7 @@ static void livepatch_fix1_dummy_leak_dtor(void *obj, void *shadow_data) __func__, d, *shadow_leak); } -void livepatch_fix1_dummy_free(struct dummy *d) +static void livepatch_fix1_dummy_free(struct dummy *d) { void **shadow_leak; diff --git a/samples/livepatch/livepatch-shadow-fix2.c b/samples/livepatch/livepatch-shadow-fix2.c index b34c7bf83356..b6dac2b9f97f 100644 --- a/samples/livepatch/livepatch-shadow-fix2.c +++ b/samples/livepatch/livepatch-shadow-fix2.c @@ -50,7 +50,7 @@ struct dummy { unsigned long jiffies_expire; }; -bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies) +static bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies) { int *shadow_count; @@ -78,7 +78,7 @@ static void livepatch_fix2_dummy_leak_dtor(void *obj, void *shadow_data) __func__, d, *shadow_leak); } -void livepatch_fix2_dummy_free(struct dummy *d) +static void livepatch_fix2_dummy_free(struct dummy *d) { void **shadow_leak; int *shadow_count; diff --git a/samples/livepatch/livepatch-shadow-mod.c b/samples/livepatch/livepatch-shadow-mod.c index 4aa8a88d3cd6..4d79c6dc055b 100644 --- a/samples/livepatch/livepatch-shadow-mod.c +++ b/samples/livepatch/livepatch-shadow-mod.c @@ -96,15 +96,15 @@ MODULE_DESCRIPTION("Buggy module for shadow variable demo"); * Keep a list of all the dummies so we can clean up any residual ones * on module exit */ -LIST_HEAD(dummy_list); -DEFINE_MUTEX(dummy_list_mutex); +static LIST_HEAD(dummy_list); +static DEFINE_MUTEX(dummy_list_mutex); struct dummy { struct list_head list; unsigned long jiffies_expire; }; -noinline struct dummy *dummy_alloc(void) +static __used noinline struct dummy *dummy_alloc(void) { struct dummy *d; void *leak; @@ -129,7 +129,7 @@ noinline struct dummy *dummy_alloc(void) return d; } -noinline void dummy_free(struct dummy *d) +static __used noinline void dummy_free(struct dummy *d) { pr_info("%s: dummy @ %p, expired = %lx\n", __func__, d, d->jiffies_expire); @@ -137,7 +137,8 @@ noinline void dummy_free(struct dummy *d) kfree(d); } -noinline bool dummy_check(struct dummy *d, unsigned long jiffies) +static __used noinline bool dummy_check(struct dummy *d, + unsigned long jiffies) { return time_after(jiffies, d->jiffies_expire); } -- cgit