mirror of
https://github.com/dynup/kpatch
synced 2025-01-14 00:50:52 +00:00
Merge pull request #160 from jpoimboe/kpatch-load-replace
support for "kpatch replace"
This commit is contained in:
commit
bf45d8dc5e
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
*.o
|
||||
*.o.cmd
|
||||
*.o.d
|
||||
*.ko
|
||||
*.ko.cmd
|
||||
*.mod.c
|
||||
|
@ -141,19 +141,33 @@ static struct kpatch_func *kpatch_get_prev_func(struct kpatch_func *f,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int kpatch_compare_addresses(unsigned long stack_addr,
|
||||
unsigned long func_addr,
|
||||
unsigned long func_size)
|
||||
{
|
||||
if (stack_addr >= func_addr && stack_addr < func_addr + func_size) {
|
||||
/* TODO: use kallsyms to print symbol name */
|
||||
pr_err("activeness safety check failed for function at address 0x%lx\n", stack_addr);
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kpatch_backtrace_address_verify(void *data, unsigned long address,
|
||||
int reliable)
|
||||
{
|
||||
struct kpatch_backtrace_args *args = data;
|
||||
struct kpatch_module *kpmod = args->kpmod;
|
||||
struct kpatch_func *func;
|
||||
int i;
|
||||
|
||||
if (args->ret)
|
||||
return;
|
||||
|
||||
/* check kpmod funcs */
|
||||
for (i = 0; i < kpmod->num_funcs; i++) {
|
||||
unsigned long func_addr, func_size;
|
||||
struct kpatch_func *func, *active_func;
|
||||
struct kpatch_func *active_func;
|
||||
|
||||
func = &kpmod->funcs[i];
|
||||
active_func = kpatch_get_func(func->old_addr);
|
||||
@ -167,11 +181,20 @@ static void kpatch_backtrace_address_verify(void *data, unsigned long address,
|
||||
func_size = active_func->new_size;
|
||||
}
|
||||
|
||||
if (address >= func_addr && address < func_addr + func_size) {
|
||||
pr_err("activeness safety check failed for function at address 0x%lx\n",
|
||||
func_addr);
|
||||
args->ret = -EBUSY;
|
||||
args->ret = kpatch_compare_addresses(address, func_addr,
|
||||
func_size);
|
||||
if (args->ret)
|
||||
return;
|
||||
}
|
||||
|
||||
/* in the replace case, need to check the func hash as well */
|
||||
hash_for_each_rcu(kpatch_func_hash, i, func, node) {
|
||||
if (func->op == KPATCH_OP_UNPATCH) {
|
||||
args->ret = kpatch_compare_addresses(address,
|
||||
func->new_addr,
|
||||
func->new_size);
|
||||
if (args->ret)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -366,10 +389,11 @@ static void kpatch_remove_funcs_from_filter(struct kpatch_func *funcs,
|
||||
}
|
||||
}
|
||||
|
||||
int kpatch_register(struct kpatch_module *kpmod)
|
||||
int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
||||
{
|
||||
int ret, i;
|
||||
struct kpatch_func *funcs = kpmod->funcs;
|
||||
struct kpatch_func *func;
|
||||
int num_funcs = kpmod->num_funcs;
|
||||
|
||||
if (!kpmod->mod || !funcs || !num_funcs)
|
||||
@ -385,9 +409,10 @@ int kpatch_register(struct kpatch_module *kpmod)
|
||||
}
|
||||
|
||||
for (i = 0; i < num_funcs; i++) {
|
||||
struct kpatch_func *func = &funcs[i];
|
||||
func = &funcs[i];
|
||||
|
||||
func->op = KPATCH_OP_PATCH;
|
||||
func->kpmod = kpmod;
|
||||
|
||||
/*
|
||||
* If any other modules have also patched this function, it
|
||||
@ -417,6 +442,10 @@ int kpatch_register(struct kpatch_module *kpmod)
|
||||
}
|
||||
kpatch_num_registered++;
|
||||
|
||||
if (replace)
|
||||
hash_for_each_rcu(kpatch_func_hash, i, func, node)
|
||||
func->op = KPATCH_OP_UNPATCH;
|
||||
|
||||
/* memory barrier between func hash and state write */
|
||||
smp_wmb();
|
||||
|
||||
@ -428,6 +457,29 @@ int kpatch_register(struct kpatch_module *kpmod)
|
||||
*/
|
||||
ret = stop_machine(kpatch_apply_patch, kpmod, NULL);
|
||||
|
||||
/*
|
||||
* For the replace case, remove any obsolete funcs from the hash and
|
||||
* the ftrace filter, and disable the owning patch module so that it
|
||||
* can be removed.
|
||||
*/
|
||||
if (!ret && replace)
|
||||
hash_for_each_rcu(kpatch_func_hash, i, func, node) {
|
||||
if (func->op != KPATCH_OP_UNPATCH)
|
||||
continue;
|
||||
hash_del_rcu(&func->node);
|
||||
kpatch_remove_funcs_from_filter(func, 1);
|
||||
if (func->kpmod->enabled) {
|
||||
kpatch_num_registered--;
|
||||
func->kpmod->enabled = false;
|
||||
pr_notice("unloaded patch module \"%s\"\n",
|
||||
func->kpmod->mod->name);
|
||||
module_put(func->kpmod->mod);
|
||||
}
|
||||
}
|
||||
|
||||
/* memory barrier between func hash and state write */
|
||||
smp_wmb();
|
||||
|
||||
/* NMI handlers can return to normal now */
|
||||
kpatch_state_idle();
|
||||
|
||||
@ -458,6 +510,9 @@ int kpatch_register(struct kpatch_module *kpmod)
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
if (replace)
|
||||
hash_for_each_rcu(kpatch_func_hash, i, func, node)
|
||||
func->op = KPATCH_OP_NONE;
|
||||
if (kpatch_num_registered == 1) {
|
||||
int ret2 = unregister_ftrace_function(&kpatch_ftrace_ops);
|
||||
if (ret2) {
|
||||
@ -550,6 +605,7 @@ static int kpatch_init(void)
|
||||
|
||||
static void kpatch_exit(void)
|
||||
{
|
||||
WARN_ON(kpatch_num_registered != 0);
|
||||
kobject_put(kpatch_patches_kobj);
|
||||
kobject_put(kpatch_root_kobj);
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ struct kpatch_func {
|
||||
|
||||
/* private */
|
||||
struct hlist_node node;
|
||||
struct kpatch_module *kpmod;
|
||||
enum kpatch_op op;
|
||||
};
|
||||
|
||||
@ -54,7 +55,7 @@ struct kpatch_module {
|
||||
|
||||
extern struct kobject *kpatch_patches_kobj;
|
||||
|
||||
extern int kpatch_register(struct kpatch_module *kpmod);
|
||||
extern int kpatch_register(struct kpatch_module *kpmod, bool replace);
|
||||
extern int kpatch_unregister(struct kpatch_module *kpmod);
|
||||
|
||||
#endif /* _KPATCH_H_ */
|
||||
|
@ -25,6 +25,10 @@
|
||||
#include "kpatch.h"
|
||||
#include "kpatch-patch.h"
|
||||
|
||||
static bool replace;
|
||||
module_param(replace, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(replace, "replace all previously loaded patch modules");
|
||||
|
||||
extern char __kpatch_patches, __kpatch_patches_end;
|
||||
|
||||
static struct kpatch_module kpmod;
|
||||
@ -99,7 +103,7 @@ static int __init patch_init(void)
|
||||
if (ret)
|
||||
goto err_put;
|
||||
|
||||
ret = kpatch_register(&kpmod);
|
||||
ret = kpatch_register(&kpmod, replace);
|
||||
if (ret)
|
||||
goto err_sysfs;
|
||||
|
||||
|
@ -28,20 +28,27 @@ SYSDIR="/usr/lib/kpatch/$KERNELRELEASE"
|
||||
USERDIR="/var/lib/kpatch/$KERNELRELEASE"
|
||||
SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))"
|
||||
|
||||
usage_cmd() {
|
||||
printf ' %-20s\n %s\n' "$1" "$2" >&2
|
||||
}
|
||||
|
||||
usage () {
|
||||
# ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION
|
||||
# When changing this, please also update the man page. Thanks!
|
||||
echo "usage: kpatch <command> [<args>]" >&2
|
||||
echo >&2
|
||||
echo "Valid commands:" >&2
|
||||
printf ' %-20s %s\n' "install <module>" "install patch module to the initrd to be loaded at boot" >&2
|
||||
printf ' %-20s %s\n' "uninstall <module>" "uninstall patch module from the initrd" >&2
|
||||
usage_cmd "install <module>" "install patch module to the initrd to be loaded at boot"
|
||||
usage_cmd "uninstall <module>" "uninstall patch module from the initrd"
|
||||
echo >&2
|
||||
printf ' %-20s %s\n' "load --all" "load all installed patch modules into the running kernel" >&2
|
||||
printf ' %-20s %s\n' "load <module>" "load patch module into the running kernel" >&2
|
||||
printf ' %-20s %s\n' "unload <module>" "unload patch module from the running kernel" >&2
|
||||
usage_cmd "load --all" "load all installed patch modules into the running kernel"
|
||||
usage_cmd "load <module>" "load patch module into the running kernel"
|
||||
usage_cmd "replace <module>" "load patch module into the running kernel, replacing all other modules"
|
||||
usage_cmd "unload <module>" "unload patch module from the running kernel"
|
||||
echo >&2
|
||||
printf ' %-20s %s\n' "info <module>" "show information about a patch module" >&2
|
||||
usage_cmd "info <module>" "show information about a patch module"
|
||||
echo >&2
|
||||
printf ' %-20s %s\n' "list" "list installed patch modules" >&2
|
||||
usage_cmd "list" "list installed patch modules"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@ -96,7 +103,7 @@ load_module () {
|
||||
/usr/sbin/insmod "$COREMOD" || die "failed to load core module"
|
||||
fi
|
||||
echo "loading patch module: $1"
|
||||
/usr/sbin/insmod "$1"
|
||||
/usr/sbin/insmod "$1" "$2"
|
||||
}
|
||||
|
||||
unload_module () {
|
||||
@ -109,14 +116,22 @@ unload_module () {
|
||||
ENABLED=/sys/kernel/kpatch/patches/"$PATCH"/enabled
|
||||
|
||||
if [[ -e "$ENABLED" ]] && [[ $(cat "$ENABLED") -eq 1 ]]; then
|
||||
echo "disabling patch module: $1"
|
||||
echo "disabling patch module: $PATCH"
|
||||
echo 0 > $ENABLED || die "can't disable $PATCH"
|
||||
fi
|
||||
|
||||
echo "unloading patch module: $1"
|
||||
echo "unloading patch module: $PATCH"
|
||||
/usr/sbin/rmmod "$(basename $1)"
|
||||
}
|
||||
|
||||
unload_disabled_modules() {
|
||||
for module in /sys/kernel/kpatch/patches/*; do
|
||||
if [[ $(cat $module/enabled) -eq 0 ]]; then
|
||||
unload_module $module || die "failed to unload $module"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
echo_patch_name() {
|
||||
NAME="$(basename $1)"
|
||||
echo $NAME
|
||||
@ -146,6 +161,14 @@ case "$1" in
|
||||
esac
|
||||
;;
|
||||
|
||||
"replace")
|
||||
[[ "$#" -ne 2 ]] && usage
|
||||
PATCH="$2"
|
||||
find_module "$PATCH" || die "can't find $PATCH"
|
||||
load_module "$MODULE" replace=1 || die "failed to load patch $PATCH"
|
||||
unload_disabled_modules || die "failed to unload old modules"
|
||||
;;
|
||||
|
||||
"unload")
|
||||
[[ "$#" -ne 2 ]] && usage
|
||||
unload_module "$2" || die "failed to unload patch $2"
|
||||
|
@ -9,7 +9,7 @@ kpatch <command> [<args>]
|
||||
kpatch is a user script that manages installing, loading, and
|
||||
displaying information about kernel patch modules installed on
|
||||
the system.
|
||||
.SH OPTIONS
|
||||
.SH COMMANDS
|
||||
|
||||
install <module>
|
||||
install patch module to the initrd to be loaded at boot
|
||||
@ -23,6 +23,9 @@ load --all
|
||||
load <module>
|
||||
load patch module into the running kernel
|
||||
|
||||
replace <module>
|
||||
load patch module into the running kernel, replacing all other modules
|
||||
|
||||
unload <module>
|
||||
unload patch module from the running kernel
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user