Merge pull request #986 from euspectre/old-replace-fix

Patch replacement fixes for the old KPatch core
This commit is contained in:
Josh Poimboeuf 2019-08-15 16:05:13 -05:00 committed by GitHub
commit c9fa73bb9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -83,6 +83,11 @@ struct kpatch_kallsyms_args {
unsigned long pos; unsigned long pos;
}; };
struct kpatch_apply_patch_args {
struct kpatch_module *kpmod;
bool replace;
};
/* this is a double loop, use goto instead of break */ /* this is a double loop, use goto instead of break */
#define do_for_each_linked_func(kpmod, func) { \ #define do_for_each_linked_func(kpmod, func) { \
struct kpatch_object *_object; \ struct kpatch_object *_object; \
@ -207,7 +212,8 @@ static inline int kpatch_compare_addresses(unsigned long stack_addr,
} }
static int kpatch_backtrace_address_verify(struct kpatch_module *kpmod, static int kpatch_backtrace_address_verify(struct kpatch_module *kpmod,
unsigned long address) unsigned long address,
bool replace)
{ {
struct kpatch_func *func; struct kpatch_func *func;
int i; int i;
@ -242,8 +248,11 @@ static int kpatch_backtrace_address_verify(struct kpatch_module *kpmod,
} while_for_each_linked_func(); } while_for_each_linked_func();
/* in the replace case, need to check the func hash as well */ /* in the replace case, need to check the func hash as well */
hash_for_each_rcu(kpatch_func_hash, i, func, node) { if (replace) {
if (func->op == KPATCH_OP_UNPATCH && !func->force) { hash_for_each_rcu(kpatch_func_hash, i, func, node) {
if (func->op != KPATCH_OP_UNPATCH || func->force)
continue;
ret = kpatch_compare_addresses(address, ret = kpatch_compare_addresses(address,
func->new_addr, func->new_addr,
func->new_size, func->new_size,
@ -262,7 +271,8 @@ static int kpatch_backtrace_address_verify(struct kpatch_module *kpmod,
* *
* This function is called from stop_machine() context. * This function is called from stop_machine() context.
*/ */
static int kpatch_verify_activeness_safety(struct kpatch_module *kpmod) static int kpatch_verify_activeness_safety(struct kpatch_module *kpmod,
bool replace)
{ {
struct task_struct *g, *t; struct task_struct *g, *t;
int i; int i;
@ -284,7 +294,8 @@ static int kpatch_verify_activeness_safety(struct kpatch_module *kpmod)
if (trace.entries[i] == ULONG_MAX) if (trace.entries[i] == ULONG_MAX)
break; break;
ret = kpatch_backtrace_address_verify(kpmod, ret = kpatch_backtrace_address_verify(kpmod,
trace.entries[i]); trace.entries[i],
replace);
if (ret) if (ret)
goto out; goto out;
} }
@ -350,12 +361,17 @@ static inline void post_unpatch_callback(struct kpatch_object *object)
/* Called from stop_machine */ /* Called from stop_machine */
static int kpatch_apply_patch(void *data) static int kpatch_apply_patch(void *data)
{ {
struct kpatch_module *kpmod = data; struct kpatch_apply_patch_args *args = data;
struct kpatch_module *kpmod;
struct kpatch_func *func; struct kpatch_func *func;
struct hlist_node *tmp;
struct kpatch_object *object; struct kpatch_object *object;
int ret; int ret;
int i;
ret = kpatch_verify_activeness_safety(kpmod); kpmod = args->kpmod;
ret = kpatch_verify_activeness_safety(kpmod, args->replace);
if (ret) { if (ret) {
kpatch_state_finish(KPATCH_STATE_FAILURE); kpatch_state_finish(KPATCH_STATE_FAILURE);
return ret; return ret;
@ -396,6 +412,19 @@ static int kpatch_apply_patch(void *data)
goto err; goto err;
} }
/*
* The new patch has been applied successfully. Remove the functions
* provided by the replaced patches (if any) from hash, to make sure
* they will not be executed anymore.
*/
if (args->replace) {
hash_for_each_safe(kpatch_func_hash, i, tmp, func, node) {
if (func->op != KPATCH_OP_UNPATCH)
continue;
hash_del_rcu(&func->node);
}
}
/* run any user-defined post-patch callbacks */ /* run any user-defined post-patch callbacks */
list_for_each_entry(object, &kpmod->objects, list) list_for_each_entry(object, &kpmod->objects, list)
post_patch_callback(object); post_patch_callback(object);
@ -417,7 +446,7 @@ static int kpatch_remove_patch(void *data)
struct kpatch_object *object; struct kpatch_object *object;
int ret; int ret;
ret = kpatch_verify_activeness_safety(kpmod); ret = kpatch_verify_activeness_safety(kpmod, false);
if (ret) { if (ret) {
kpatch_state_finish(KPATCH_STATE_FAILURE); kpatch_state_finish(KPATCH_STATE_FAILURE);
return ret; return ret;
@ -975,12 +1004,41 @@ out:
return 0; return 0;
} }
/*
* Remove the obsolete functions from the ftrace filter.
* Return 1 if one or more of such functions have 'force' flag set,
* 0 otherwise.
*/
static int kpatch_ftrace_remove_unpatched_funcs(void)
{
struct kpatch_module *kpmod;
struct kpatch_func *func;
int force = 0;
list_for_each_entry(kpmod, &kpmod_list, list) {
do_for_each_linked_func(kpmod, func) {
if (func->op != KPATCH_OP_UNPATCH)
continue;
if (func->force)
force = 1;
WARN_ON(kpatch_ftrace_remove_func(func->old_addr));
} while_for_each_linked_func();
}
return force;
}
int kpatch_register(struct kpatch_module *kpmod, bool replace) int kpatch_register(struct kpatch_module *kpmod, bool replace)
{ {
int ret, i, force = 0; int ret, i;
struct kpatch_object *object, *object_err = NULL; struct kpatch_object *object, *object_err = NULL;
struct kpatch_func *func; struct kpatch_func *func;
struct kpatch_apply_patch_args args = {
.kpmod = kpmod,
.replace = replace,
};
if (!kpmod->mod || list_empty(&kpmod->objects)) if (!kpmod->mod || list_empty(&kpmod->objects))
return -EINVAL; return -EINVAL;
@ -1032,24 +1090,18 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
* Idle the CPUs, verify activeness safety, and atomically make the new * Idle the CPUs, verify activeness safety, and atomically make the new
* functions visible to the ftrace handler. * functions visible to the ftrace handler.
*/ */
ret = stop_machine(kpatch_apply_patch, kpmod, NULL); ret = stop_machine(kpatch_apply_patch, &args, NULL);
/* /*
* For the replace case, remove any obsolete funcs from the hash and * For the replace case, remove any obsolete funcs from the ftrace
* the ftrace filter, and disable the owning patch module so that it * filter, and disable the owning patch module so that it can be
* can be removed. * removed.
*/ */
if (!ret && replace) { if (!ret && replace) {
struct kpatch_module *kpmod2, *safe; struct kpatch_module *kpmod2, *safe;
int force;
hash_for_each_rcu(kpatch_func_hash, i, func, node) { force = kpatch_ftrace_remove_unpatched_funcs();
if (func->op != KPATCH_OP_UNPATCH)
continue;
if (func->force)
force = 1;
hash_del_rcu(&func->node);
WARN_ON(kpatch_ftrace_remove_func(func->old_addr));
}
list_for_each_entry_safe(kpmod2, safe, &kpmod_list, list) { list_for_each_entry_safe(kpmod2, safe, &kpmod_list, list) {
if (kpmod == kpmod2) if (kpmod == kpmod2)