Skip to content

Commit

Permalink
Merge branch 'for-5.1/atomic-replace' into for-linus
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Jiri Kosina committed Mar 5, 2019
2 parents 7185a96 + fbb76d5 commit f9d1381
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 39 deletions.
36 changes: 11 additions & 25 deletions kernel/livepatch/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}


Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -1101,29 +1101,22 @@ 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;

klp_for_each_object(patch, obj) {
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;
Expand All @@ -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;
Expand All @@ -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);

Expand Down
6 changes: 6 additions & 0 deletions kernel/livepatch/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion kernel/livepatch/transition.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
24 changes: 23 additions & 1 deletion lib/livepatch/test_klp_shadow_vars.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++;

Expand Down Expand Up @@ -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));
Expand All @@ -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));
Expand Down Expand Up @@ -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));
Expand Down
19 changes: 7 additions & 12 deletions tools/testing/selftests/livepatch/functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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" ]]' ||
Expand All @@ -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' ||
Expand All @@ -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
Expand Down

0 comments on commit f9d1381

Please sign in to comment.