summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2019-03-05 15:56:59 +0100
committerJiri Kosina <jkosina@suse.cz>2019-03-05 15:56:59 +0100
commitf9d138145686b52b48ccb36557d6842076e2b9dd (patch)
tree95f5c8952a06616ddf4e7ed0288b82bfdb3aea72
parent7185a96981a2f8bb523dd87cad20a6b96c721ad5 (diff)
parentfbb76d579dff4a2e332566dcd1d5979ac92bc34b (diff)
Merge branch 'for-5.1/atomic-replace' into for-linus
The atomic replace allows to create cumulative patches. They are useful when you maintain many livepatches and want to remove one that is lower on the stack. In addition it is very useful when more patches touch the same function and there are dependencies between them. It's also a feature some of the distros are using already to distribute their patches.
-rw-r--r--kernel/livepatch/core.c36
-rw-r--r--kernel/livepatch/core.h6
-rw-r--r--kernel/livepatch/transition.c2
-rw-r--r--lib/livepatch/test_klp_shadow_vars.c24
-rw-r--r--tools/testing/selftests/livepatch/functions.sh19
5 files changed, 48 insertions, 39 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index fe1993399823..eb0ee10a1981 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -522,7 +522,7 @@ static int klp_add_nops(struct klp_patch *patch)
struct klp_patch *old_patch;
struct klp_object *old_obj;
- list_for_each_entry(old_patch, &klp_patches, list) {
+ klp_for_each_patch(old_patch) {
klp_for_each_object(old_patch, old_obj) {
int err;
@@ -1004,7 +1004,7 @@ int klp_enable_patch(struct klp_patch *patch)
if (!klp_have_reliable_stack()) {
pr_err("This architecture doesn't have support for the livepatch consistency model.\n");
- return -ENOSYS;
+ return -EOPNOTSUPP;
}
@@ -1057,7 +1057,7 @@ void klp_discard_replaced_patches(struct klp_patch *new_patch)
{
struct klp_patch *old_patch, *tmp_patch;
- list_for_each_entry_safe(old_patch, tmp_patch, &klp_patches, list) {
+ klp_for_each_patch_safe(old_patch, tmp_patch) {
if (old_patch == new_patch)
return;
@@ -1101,7 +1101,7 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
struct klp_patch *patch;
struct klp_object *obj;
- list_for_each_entry(patch, &klp_patches, list) {
+ klp_for_each_patch(patch) {
if (patch == limit)
break;
@@ -1109,21 +1109,14 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
continue;
- /*
- * Only unpatch the module if the patch is enabled or
- * is in transition.
- */
- if (patch->enabled || patch == klp_transition_patch) {
-
- if (patch != klp_transition_patch)
- klp_pre_unpatch_callback(obj);
+ if (patch != klp_transition_patch)
+ klp_pre_unpatch_callback(obj);
- pr_notice("reverting patch '%s' on unloading module '%s'\n",
- patch->mod->name, obj->mod->name);
- klp_unpatch_object(obj);
+ pr_notice("reverting patch '%s' on unloading module '%s'\n",
+ patch->mod->name, obj->mod->name);
+ klp_unpatch_object(obj);
- klp_post_unpatch_callback(obj);
- }
+ klp_post_unpatch_callback(obj);
klp_free_object_loaded(obj);
break;
@@ -1148,7 +1141,7 @@ int klp_module_coming(struct module *mod)
*/
mod->klp_alive = true;
- list_for_each_entry(patch, &klp_patches, list) {
+ klp_for_each_patch(patch) {
klp_for_each_object(patch, obj) {
if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
continue;
@@ -1162,13 +1155,6 @@ int klp_module_coming(struct module *mod)
goto err;
}
- /*
- * Only patch the module if the patch is enabled or is
- * in transition.
- */
- if (!patch->enabled && patch != klp_transition_patch)
- break;
-
pr_notice("applying patch '%s' to loading module '%s'\n",
patch->mod->name, obj->mod->name);
diff --git a/kernel/livepatch/core.h b/kernel/livepatch/core.h
index e6200f38701f..ec43a40b853f 100644
--- a/kernel/livepatch/core.h
+++ b/kernel/livepatch/core.h
@@ -7,6 +7,12 @@
extern struct mutex klp_mutex;
extern struct list_head klp_patches;
+#define klp_for_each_patch_safe(patch, tmp_patch) \
+ list_for_each_entry_safe(patch, tmp_patch, &klp_patches, list)
+
+#define klp_for_each_patch(patch) \
+ list_for_each_entry(patch, &klp_patches, list)
+
void klp_free_patch_start(struct klp_patch *patch);
void klp_discard_replaced_patches(struct klp_patch *new_patch);
void klp_discard_nops(struct klp_patch *new_patch);
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
index 183b2086ba03..9c89ae8b337a 100644
--- a/kernel/livepatch/transition.c
+++ b/kernel/livepatch/transition.c
@@ -652,6 +652,6 @@ void klp_force_transition(void)
for_each_possible_cpu(cpu)
klp_update_patch_state(idle_task(cpu));
- list_for_each_entry(patch, &klp_patches, list)
+ klp_for_each_patch(patch)
patch->forced = true;
}
diff --git a/lib/livepatch/test_klp_shadow_vars.c b/lib/livepatch/test_klp_shadow_vars.c
index 02f892f941dc..fe5c413efe96 100644
--- a/lib/livepatch/test_klp_shadow_vars.c
+++ b/lib/livepatch/test_klp_shadow_vars.c
@@ -44,7 +44,7 @@ static int ptr_id(void *ptr)
sp = kmalloc(sizeof(*sp), GFP_ATOMIC);
if (!sp)
- return -1;
+ return -ENOMEM;
sp->ptr = ptr;
sp->id = count++;
@@ -154,22 +154,37 @@ static int test_klp_shadow_vars_init(void)
* Allocate a few shadow variables with different <obj> and <id>.
*/
sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1);
+ if (!sv1)
+ return -ENOMEM;
+
sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2);
+ if (!sv2)
+ return -ENOMEM;
+
sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3);
+ if (!sv3)
+ return -ENOMEM;
/*
* Verify we can find our new shadow variables and that they point
* to expected data.
*/
ret = shadow_get(obj, id);
+ if (!ret)
+ return -EINVAL;
if (ret == sv1 && *sv1 == &var1)
pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv1), ptr_id(*sv1));
+
ret = shadow_get(obj + 1, id);
+ if (!ret)
+ return -EINVAL;
if (ret == sv2 && *sv2 == &var2)
pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv2), ptr_id(*sv2));
ret = shadow_get(obj, id + 1);
+ if (!ret)
+ return -EINVAL;
if (ret == sv3 && *sv3 == &var3)
pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv3), ptr_id(*sv3));
@@ -179,7 +194,12 @@ static int test_klp_shadow_vars_init(void)
* The second invocation should return the same shadow var.
*/
sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
+ if (!sv4)
+ return -ENOMEM;
+
ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
+ if (!ret)
+ return -EINVAL;
if (ret == sv4 && *sv4 == &var4)
pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv4), ptr_id(*sv4));
@@ -207,6 +227,8 @@ static int test_klp_shadow_vars_init(void)
* We should still find an <id+1> variable.
*/
ret = shadow_get(obj, id + 1);
+ if (!ret)
+ return -EINVAL;
if (ret == sv3 && *sv3 == &var3)
pr_info(" got expected PTR%d -> PTR%d result\n",
ptr_id(sv3), ptr_id(*sv3));
diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh
index c7b9fb45d7c9..30195449c63c 100644
--- a/tools/testing/selftests/livepatch/functions.sh
+++ b/tools/testing/selftests/livepatch/functions.sh
@@ -55,11 +55,10 @@ function is_livepatch_mod() {
function __load_mod() {
local mod="$1"; shift
- local args="$*"
- local msg="% modprobe $mod $args"
+ local msg="% modprobe $mod $*"
log "${msg%% }"
- ret=$(modprobe "$mod" "$args" 2>&1)
+ ret=$(modprobe "$mod" "$@" 2>&1)
if [[ "$ret" != "" ]]; then
die "$ret"
fi
@@ -75,12 +74,11 @@ function __load_mod() {
# params - module parameters to pass to modprobe
function load_mod() {
local mod="$1"; shift
- local args="$*"
is_livepatch_mod "$mod" &&
die "use load_lp() to load the livepatch module $mod"
- __load_mod "$mod" "$args"
+ __load_mod "$mod" "$@"
}
# load_lp_nowait(modname, params) - load a kernel module with a livepatch
@@ -89,12 +87,11 @@ function load_mod() {
# params - module parameters to pass to modprobe
function load_lp_nowait() {
local mod="$1"; shift
- local args="$*"
is_livepatch_mod "$mod" ||
die "module $mod is not a livepatch"
- __load_mod "$mod" "$args"
+ __load_mod "$mod" "$@"
# Wait for livepatch in sysfs ...
loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' ||
@@ -106,9 +103,8 @@ function load_lp_nowait() {
# params - module parameters to pass to modprobe
function load_lp() {
local mod="$1"; shift
- local args="$*"
- load_lp_nowait "$mod" "$args"
+ load_lp_nowait "$mod" "$@"
# Wait until the transition finishes ...
loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' ||
@@ -120,11 +116,10 @@ function load_lp() {
# params - module parameters to pass to modprobe
function load_failing_mod() {
local mod="$1"; shift
- local args="$*"
- local msg="% modprobe $mod $args"
+ local msg="% modprobe $mod $*"
log "${msg%% }"
- ret=$(modprobe "$mod" "$args" 2>&1)
+ ret=$(modprobe "$mod" "$@" 2>&1)
if [[ "$ret" == "" ]]; then
die "$mod unexpectedly loaded"
fi