mirror of
https://github.com/dynup/kpatch
synced 2025-05-07 10:27:55 +00:00
Merge pull request #242 from jpoimboe/future-loaded-modules
support for patching future loaded modules
This commit is contained in:
commit
d22ddec366
390
kmod/core/core.c
390
kmod/core/core.c
@ -58,7 +58,9 @@ static DEFINE_HASHTABLE(kpatch_func_hash, KPATCH_HASH_BITS);
|
|||||||
|
|
||||||
static DEFINE_SEMAPHORE(kpatch_mutex);
|
static DEFINE_SEMAPHORE(kpatch_mutex);
|
||||||
|
|
||||||
static int kpatch_num_registered;
|
LIST_HEAD(kpmod_list);
|
||||||
|
|
||||||
|
static int kpatch_num_patched;
|
||||||
|
|
||||||
static struct kobject *kpatch_root_kobj;
|
static struct kobject *kpatch_root_kobj;
|
||||||
struct kobject *kpatch_patches_kobj;
|
struct kobject *kpatch_patches_kobj;
|
||||||
@ -127,16 +129,6 @@ struct kpatch_func {
|
|||||||
struct module *mod;
|
struct module *mod;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* This structure is allocated on a per registered module basis
|
|
||||||
* and is stored in the struct kpatch_module "internal" field.
|
|
||||||
* Any data associated with each registered module used internally
|
|
||||||
* by this core module can be added here.
|
|
||||||
*/
|
|
||||||
struct kpatch_internal {
|
|
||||||
struct kpatch_func *funcs;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void kpatch_state_idle(void)
|
static inline void kpatch_state_idle(void)
|
||||||
{
|
{
|
||||||
int state = atomic_read(&kpatch_state);
|
int state = atomic_read(&kpatch_state);
|
||||||
@ -208,7 +200,12 @@ static void kpatch_backtrace_address_verify(void *data, unsigned long address,
|
|||||||
char *func_name;
|
char *func_name;
|
||||||
struct kpatch_func *active_func;
|
struct kpatch_func *active_func;
|
||||||
|
|
||||||
func = &kpmod->internal->funcs[i];
|
func = &kpmod->funcs[i];
|
||||||
|
|
||||||
|
/* check if patch depends on a future module load */
|
||||||
|
if (!func->old_addr)
|
||||||
|
continue;
|
||||||
|
|
||||||
active_func = kpatch_get_func(func->old_addr);
|
active_func = kpatch_get_func(func->old_addr);
|
||||||
if (!active_func) {
|
if (!active_func) {
|
||||||
/* patching an unpatched func */
|
/* patching an unpatched func */
|
||||||
@ -285,7 +282,7 @@ out:
|
|||||||
static int kpatch_apply_patch(void *data)
|
static int kpatch_apply_patch(void *data)
|
||||||
{
|
{
|
||||||
struct kpatch_module *kpmod = data;
|
struct kpatch_module *kpmod = data;
|
||||||
struct kpatch_func *funcs = kpmod->internal->funcs;
|
struct kpatch_func *funcs = kpmod->funcs;
|
||||||
int num_funcs = kpmod->patches_nr;
|
int num_funcs = kpmod->patches_nr;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
@ -297,6 +294,7 @@ static int kpatch_apply_patch(void *data)
|
|||||||
|
|
||||||
/* tentatively add the new funcs to the global func hash */
|
/* tentatively add the new funcs to the global func hash */
|
||||||
for (i = 0; i < num_funcs; i++)
|
for (i = 0; i < num_funcs; i++)
|
||||||
|
if (funcs[i].old_addr)
|
||||||
hash_add_rcu(kpatch_func_hash, &funcs[i].node,
|
hash_add_rcu(kpatch_func_hash, &funcs[i].node,
|
||||||
funcs[i].old_addr);
|
funcs[i].old_addr);
|
||||||
|
|
||||||
@ -313,6 +311,7 @@ static int kpatch_apply_patch(void *data)
|
|||||||
|
|
||||||
/* Failed, we have to rollback patching process */
|
/* Failed, we have to rollback patching process */
|
||||||
for (i = 0; i < num_funcs; i++)
|
for (i = 0; i < num_funcs; i++)
|
||||||
|
if (funcs[i].old_addr)
|
||||||
hash_del_rcu(&funcs[i].node);
|
hash_del_rcu(&funcs[i].node);
|
||||||
|
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
@ -325,7 +324,7 @@ static int kpatch_apply_patch(void *data)
|
|||||||
static int kpatch_remove_patch(void *data)
|
static int kpatch_remove_patch(void *data)
|
||||||
{
|
{
|
||||||
struct kpatch_module *kpmod = data;
|
struct kpatch_module *kpmod = data;
|
||||||
struct kpatch_func *funcs = kpmod->internal->funcs;
|
struct kpatch_func *funcs = kpmod->funcs;
|
||||||
int num_funcs = kpmod->patches_nr;
|
int num_funcs = kpmod->patches_nr;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
@ -342,6 +341,7 @@ static int kpatch_remove_patch(void *data)
|
|||||||
|
|
||||||
/* Succeeded, remove all updating funcs from hash table */
|
/* Succeeded, remove all updating funcs from hash table */
|
||||||
for (i = 0; i < num_funcs; i++)
|
for (i = 0; i < num_funcs; i++)
|
||||||
|
if (funcs[i].old_addr)
|
||||||
hash_del_rcu(&funcs[i].node);
|
hash_del_rcu(&funcs[i].node);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -406,6 +406,60 @@ static struct ftrace_ops kpatch_ftrace_ops __read_mostly = {
|
|||||||
.flags = FTRACE_OPS_FL_SAVE_REGS,
|
.flags = FTRACE_OPS_FL_SAVE_REGS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int kpatch_ftrace_add_func(unsigned long ip)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* check if any other patch modules have also patched this func */
|
||||||
|
if (kpatch_get_func(ip))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = ftrace_set_filter_ip(&kpatch_ftrace_ops, ip, 0, 0);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("can't set ftrace filter at address 0x%lx\n", ip);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kpatch_num_patched) {
|
||||||
|
ret = register_ftrace_function(&kpatch_ftrace_ops);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("can't register ftrace handler\n");
|
||||||
|
ftrace_set_filter_ip(&kpatch_ftrace_ops, ip, 1, 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kpatch_num_patched++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kpatch_ftrace_remove_func(unsigned long ip)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* check if any other patch modules have also patched this func */
|
||||||
|
if (kpatch_get_func(ip))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = ftrace_set_filter_ip(&kpatch_ftrace_ops, ip, 1, 0);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("can't remove ftrace filter at address 0x%lx\n", ip);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kpatch_num_patched == 1) {
|
||||||
|
ret = unregister_ftrace_function(&kpatch_ftrace_ops);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("can't unregister ftrace handler\n");
|
||||||
|
ftrace_set_filter_ip(&kpatch_ftrace_ops, ip, 0, 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kpatch_num_patched--;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void kpatch_put_modules(struct kpatch_func *funcs, int num_funcs)
|
static void kpatch_put_modules(struct kpatch_func *funcs, int num_funcs)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -418,31 +472,6 @@ static void kpatch_put_modules(struct kpatch_func *funcs, int num_funcs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove kpatch_funcs from ftrace filter */
|
|
||||||
static void kpatch_remove_funcs_from_filter(struct kpatch_func *funcs,
|
|
||||||
int num_funcs)
|
|
||||||
{
|
|
||||||
int i, ret = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < num_funcs; i++) {
|
|
||||||
struct kpatch_func *func = &funcs[i];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If any other modules have also patched this function, don't
|
|
||||||
* remove its ftrace handler.
|
|
||||||
*/
|
|
||||||
if (kpatch_get_func(func->old_addr))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Remove the ftrace handler for this function. */
|
|
||||||
ret = ftrace_set_filter_ip(&kpatch_ftrace_ops,
|
|
||||||
func->old_addr, 1, 0);
|
|
||||||
|
|
||||||
WARN(ret, "can't remove ftrace filter at address 0x%lx (rc=%d)",
|
|
||||||
func->old_addr, ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int kpatch_kallsyms_callback(void *data, const char *name,
|
static int kpatch_kallsyms_callback(void *data, const char *name,
|
||||||
struct module *mod,
|
struct module *mod,
|
||||||
unsigned long addr)
|
unsigned long addr)
|
||||||
@ -501,7 +530,7 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod)
|
|||||||
unsigned long core = (unsigned long)kpmod->mod->module_core;
|
unsigned long core = (unsigned long)kpmod->mod->module_core;
|
||||||
unsigned long core_ro_size = kpmod->mod->core_ro_size;
|
unsigned long core_ro_size = kpmod->mod->core_ro_size;
|
||||||
unsigned long core_size = kpmod->mod->core_size;
|
unsigned long core_size = kpmod->mod->core_size;
|
||||||
unsigned long src_addr;
|
unsigned long src;
|
||||||
struct module *mod;
|
struct module *mod;
|
||||||
|
|
||||||
for (i = 0; i < kpmod->dynrelas_nr; i++) {
|
for (i = 0; i < kpmod->dynrelas_nr; i++) {
|
||||||
@ -509,47 +538,60 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod)
|
|||||||
|
|
||||||
if (!strcmp(dynrela->objname, "vmlinux")) {
|
if (!strcmp(dynrela->objname, "vmlinux")) {
|
||||||
/* vmlinux */
|
/* vmlinux */
|
||||||
ret = kpatch_verify_symbol_match(dynrela->name, dynrela->src);
|
|
||||||
|
/* check if reloc has already been written */
|
||||||
|
if (kpmod->enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = kpatch_verify_symbol_match(dynrela->name,
|
||||||
|
dynrela->src);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
src_addr = dynrela->src;
|
|
||||||
} else {
|
} else {
|
||||||
/* module, dynrela->src is not right */
|
/* module, dynrela->src needs to be discovered */
|
||||||
|
|
||||||
|
/* check if reloc has already been written */
|
||||||
|
if (dynrela->src)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* check if dynrela depends on a future loaded module */
|
||||||
|
mutex_lock(&module_mutex);
|
||||||
mod = find_module(dynrela->objname);
|
mod = find_module(dynrela->objname);
|
||||||
if (!mod) {
|
mutex_unlock(&module_mutex);
|
||||||
pr_err("unable to find module '%s'\n",
|
if (!mod)
|
||||||
dynrela->objname);
|
continue;
|
||||||
return -EINVAL;
|
|
||||||
}
|
src = kpatch_find_module_symbol(mod, dynrela->name);
|
||||||
src_addr = kpatch_find_module_symbol(mod, dynrela->name);
|
if (!src) {
|
||||||
if (!src_addr) {
|
|
||||||
pr_err("unable to find symbol '%s' in module '%s'\n",
|
pr_err("unable to find symbol '%s' in module '%s'\n",
|
||||||
dynrela->name, mod->name);
|
dynrela->name, mod->name);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dynrela->src = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (dynrela->type) {
|
switch (dynrela->type) {
|
||||||
case R_X86_64_PC32:
|
case R_X86_64_PC32:
|
||||||
loc = dynrela->dest;
|
loc = dynrela->dest;
|
||||||
val = (u32)(src_addr + dynrela->addend -
|
val = (u32)(dynrela->src + dynrela->addend -
|
||||||
dynrela->dest);
|
dynrela->dest);
|
||||||
size = 4;
|
size = 4;
|
||||||
break;
|
break;
|
||||||
case R_X86_64_32S:
|
case R_X86_64_32S:
|
||||||
loc = dynrela->dest;
|
loc = dynrela->dest;
|
||||||
val = (s32)src_addr + dynrela->addend;
|
val = (s32)dynrela->src + dynrela->addend;
|
||||||
size = 4;
|
size = 4;
|
||||||
break;
|
break;
|
||||||
case R_X86_64_64:
|
case R_X86_64_64:
|
||||||
loc = dynrela->dest;
|
loc = dynrela->dest;
|
||||||
val = src_addr;
|
val = dynrela->src;
|
||||||
size = 8;
|
size = 8;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printk("unsupported rela type %ld for source %s (0x%lx <- 0x%lx) at index %d\n",
|
printk("unsupported rela type %ld for source %s (0x%lx <- 0x%lx) at index %d\n",
|
||||||
dynrela->type, dynrela->name,
|
dynrela->type, dynrela->name,
|
||||||
dynrela->dest, src_addr, i);
|
dynrela->dest, dynrela->src, i);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,29 +623,96 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kpatch_calculate_old_addr(struct kpatch_func *func)
|
static void kpatch_calculate_old_addr(struct kpatch_func *func)
|
||||||
{
|
{
|
||||||
struct module *module;
|
struct module *module;
|
||||||
|
|
||||||
if (!strcmp(func->patch->objname, "vmlinux")) {
|
if (!strcmp(func->patch->objname, "vmlinux")) {
|
||||||
func->old_addr = func->patch->old_offset;
|
func->old_addr = func->patch->old_offset;
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the module hasn't been loaded yet, we'll patch it later in
|
||||||
|
* kpatch_module_notify().
|
||||||
|
*/
|
||||||
|
mutex_lock(&module_mutex);
|
||||||
module = find_module(func->patch->objname);
|
module = find_module(func->patch->objname);
|
||||||
if (!module) {
|
if (!module) {
|
||||||
pr_err("patch contains code for a module that is not loaded\n");
|
mutex_unlock(&module_mutex);
|
||||||
return -EINVAL;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!try_module_get(module)) {
|
/* should never fail because we have the mutex */
|
||||||
pr_err("failed to reference module\n");
|
WARN_ON(!try_module_get(module));
|
||||||
return -EINVAL;
|
mutex_unlock(&module_mutex);
|
||||||
}
|
|
||||||
|
|
||||||
func->mod = module;
|
func->mod = module;
|
||||||
func->old_addr = (unsigned long)module->module_core +
|
func->old_addr = (unsigned long)module->module_core +
|
||||||
func->patch->old_offset;
|
func->patch->old_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kpatch_module_notify(struct notifier_block *nb, unsigned long action,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct module *mod = data;
|
||||||
|
struct kpatch_module *kpmod;
|
||||||
|
int printed = 0, ret = 0, i;
|
||||||
|
|
||||||
|
if (action != MODULE_STATE_COMING)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
down(&kpatch_mutex);
|
||||||
|
|
||||||
|
list_for_each_entry(kpmod, &kpmod_list, list) {
|
||||||
|
if (!kpmod->pending)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* temporarily clear pending, can be set again below */
|
||||||
|
kpmod->pending = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < kpmod->patches_nr; i++) {
|
||||||
|
struct kpatch_func *func = &kpmod->funcs[i];
|
||||||
|
|
||||||
|
/* check if it has already been patched */
|
||||||
|
if (func->old_addr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* calculate actual old location (refs if module) */
|
||||||
|
kpatch_calculate_old_addr(func);
|
||||||
|
|
||||||
|
if (!func->old_addr) {
|
||||||
|
kpmod->pending = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!printed) {
|
||||||
|
pr_notice("patching newly loaded module '%s'\n",
|
||||||
|
mod->name);
|
||||||
|
printed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = kpatch_ftrace_add_func(func->old_addr);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* add to the global func hash */
|
||||||
|
hash_add_rcu(kpatch_func_hash, &func->node,
|
||||||
|
func->old_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write any newly valid relocations */
|
||||||
|
ret = kpatch_write_relocations(kpmod);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
up(&kpatch_mutex);
|
||||||
|
|
||||||
|
/* no way to stop the module load on error */
|
||||||
|
WARN(ret, "error (%d) patching newly loaded module '%s'\n", ret,
|
||||||
|
mod->name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -616,30 +725,22 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
|||||||
if (!kpmod->mod || !kpmod->patches || !num_funcs)
|
if (!kpmod->mod || !kpmod->patches || !num_funcs)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
kpmod->enabled = false;
|
|
||||||
|
|
||||||
kpmod->internal = kmalloc(sizeof(*kpmod->internal), GFP_KERNEL);
|
|
||||||
if (!kpmod->internal)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
funcs = kzalloc(sizeof(*funcs) * num_funcs, GFP_KERNEL);
|
funcs = kzalloc(sizeof(*funcs) * num_funcs, GFP_KERNEL);
|
||||||
if (!funcs) {
|
if (!funcs)
|
||||||
kfree(kpmod->internal);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
kpmod->internal->funcs = funcs;
|
|
||||||
|
|
||||||
down(&kpatch_mutex);
|
down(&kpatch_mutex);
|
||||||
|
|
||||||
|
kpmod->enabled = false;
|
||||||
|
kpmod->pending = 0;
|
||||||
|
kpmod->funcs = funcs;
|
||||||
|
list_add_tail(&kpmod->list, &kpmod_list);
|
||||||
|
|
||||||
if (!try_module_get(kpmod->mod)) {
|
if (!try_module_get(kpmod->mod)) {
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto err_up;
|
goto err_up;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = kpatch_write_relocations(kpmod);
|
|
||||||
if (ret)
|
|
||||||
goto err_put;
|
|
||||||
|
|
||||||
for (i = 0; i < num_funcs; i++) {
|
for (i = 0; i < num_funcs; i++) {
|
||||||
func = &funcs[i];
|
func = &funcs[i];
|
||||||
|
|
||||||
@ -648,42 +749,31 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
|||||||
func->patch = &kpmod->patches[i];
|
func->patch = &kpmod->patches[i];
|
||||||
|
|
||||||
/* calculate actual old location (refs if module) */
|
/* calculate actual old location (refs if module) */
|
||||||
ret = kpatch_calculate_old_addr(func);
|
kpatch_calculate_old_addr(func);
|
||||||
if (ret)
|
|
||||||
goto err_rollback;
|
/* check if patch depends on a future loaded module */
|
||||||
|
if (!func->old_addr) {
|
||||||
|
kpmod->pending = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ret = kpatch_verify_symbol_match(func->patch->name,
|
ret = kpatch_verify_symbol_match(func->patch->name,
|
||||||
func->old_addr);
|
func->old_addr);
|
||||||
if (ret)
|
|
||||||
goto err_rollback;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If any other modules have also patched this function, it
|
|
||||||
* already has an ftrace handler.
|
|
||||||
*/
|
|
||||||
if (kpatch_get_func(func->old_addr))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Add an ftrace handler for this function. */
|
|
||||||
ret = ftrace_set_filter_ip(&kpatch_ftrace_ops,
|
|
||||||
func->old_addr, 0, 0);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("can't set ftrace filter at address 0x%lx\n",
|
num_funcs = i;
|
||||||
func->old_addr);
|
goto err_rollback;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = kpatch_ftrace_add_func(func->old_addr);
|
||||||
|
if (ret) {
|
||||||
num_funcs = i;
|
num_funcs = i;
|
||||||
goto err_rollback;
|
goto err_rollback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register the ftrace handler if it hasn't been done already. */
|
ret = kpatch_write_relocations(kpmod);
|
||||||
if (!kpatch_num_registered) {
|
if (ret)
|
||||||
ret = register_ftrace_function(&kpatch_ftrace_ops);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("can't register ftrace handler\n");
|
|
||||||
goto err_rollback;
|
goto err_rollback;
|
||||||
}
|
|
||||||
}
|
|
||||||
kpatch_num_registered++;
|
|
||||||
|
|
||||||
if (replace)
|
if (replace)
|
||||||
hash_for_each_rcu(kpatch_func_hash, i, func, node)
|
hash_for_each_rcu(kpatch_func_hash, i, func, node)
|
||||||
@ -705,21 +795,29 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
|||||||
* the ftrace filter, and disable the owning patch module so that it
|
* the ftrace filter, and disable the owning patch module so that it
|
||||||
* can be removed.
|
* can be removed.
|
||||||
*/
|
*/
|
||||||
if (!ret && replace)
|
if (!ret && replace) {
|
||||||
|
struct kpatch_module *kpmod2, *safe;
|
||||||
|
|
||||||
hash_for_each_rcu(kpatch_func_hash, i, func, node) {
|
hash_for_each_rcu(kpatch_func_hash, i, func, node) {
|
||||||
if (func->op != KPATCH_OP_UNPATCH)
|
if (func->op != KPATCH_OP_UNPATCH)
|
||||||
continue;
|
continue;
|
||||||
hash_del_rcu(&func->node);
|
hash_del_rcu(&func->node);
|
||||||
kpatch_remove_funcs_from_filter(func, 1);
|
WARN_ON(kpatch_ftrace_remove_func(func->old_addr));
|
||||||
if (func->kpmod->enabled) {
|
}
|
||||||
kpatch_num_registered--;
|
|
||||||
func->kpmod->enabled = false;
|
list_for_each_entry_safe(kpmod2, safe, &kpmod_list, list) {
|
||||||
pr_notice("unloaded patch module \"%s\"\n",
|
if (kpmod == kpmod2)
|
||||||
func->kpmod->mod->name);
|
continue;
|
||||||
module_put(func->kpmod->mod);
|
|
||||||
|
kpmod2->enabled = false;
|
||||||
|
pr_notice("unloaded patch module '%s'\n",
|
||||||
|
kpmod2->mod->name);
|
||||||
|
module_put(kpmod2->mod);
|
||||||
|
list_del(&kpmod2->list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* memory barrier between func hash and state write */
|
/* memory barrier between func hash and state write */
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
|
|
||||||
@ -736,7 +834,7 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
|||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_unregister;
|
goto err_ops;
|
||||||
|
|
||||||
for (i = 0; i < num_funcs; i++)
|
for (i = 0; i < num_funcs; i++)
|
||||||
funcs[i].op = KPATCH_OP_NONE;
|
funcs[i].op = KPATCH_OP_NONE;
|
||||||
@ -745,41 +843,33 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
|||||||
pr_notice_once("tainting kernel with TAINT_USER\n");
|
pr_notice_once("tainting kernel with TAINT_USER\n");
|
||||||
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
|
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
|
||||||
|
|
||||||
pr_notice("loaded patch module \"%s\"\n", kpmod->mod->name);
|
pr_notice("loaded patch module '%s'\n", kpmod->mod->name);
|
||||||
|
|
||||||
kpmod->enabled = true;
|
kpmod->enabled = true;
|
||||||
|
|
||||||
up(&kpatch_mutex);
|
up(&kpatch_mutex);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_unregister:
|
err_ops:
|
||||||
if (replace)
|
if (replace)
|
||||||
hash_for_each_rcu(kpatch_func_hash, i, func, node)
|
hash_for_each_rcu(kpatch_func_hash, i, func, node)
|
||||||
func->op = KPATCH_OP_NONE;
|
func->op = KPATCH_OP_NONE;
|
||||||
if (kpatch_num_registered == 1) {
|
|
||||||
int ret2 = unregister_ftrace_function(&kpatch_ftrace_ops);
|
|
||||||
if (ret2) {
|
|
||||||
pr_err("ftrace unregister failed (%d)\n", ret2);
|
|
||||||
goto err_rollback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kpatch_num_registered--;
|
|
||||||
err_rollback:
|
err_rollback:
|
||||||
kpatch_put_modules(funcs, num_funcs);
|
for (i = 0; i < num_funcs; i++)
|
||||||
kpatch_remove_funcs_from_filter(funcs, num_funcs);
|
kpatch_ftrace_remove_func(funcs[i].old_addr);
|
||||||
err_put:
|
kpatch_put_modules(funcs, kpmod->patches_nr);
|
||||||
module_put(kpmod->mod);
|
module_put(kpmod->mod);
|
||||||
err_up:
|
err_up:
|
||||||
|
list_del(&kpmod->list);
|
||||||
up(&kpatch_mutex);
|
up(&kpatch_mutex);
|
||||||
kfree(kpmod->internal->funcs);
|
kfree(kpmod->funcs);
|
||||||
kfree(kpmod->internal);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(kpatch_register);
|
EXPORT_SYMBOL(kpatch_register);
|
||||||
|
|
||||||
int kpatch_unregister(struct kpatch_module *kpmod)
|
int kpatch_unregister(struct kpatch_module *kpmod)
|
||||||
{
|
{
|
||||||
struct kpatch_func *funcs = kpmod->internal->funcs;
|
struct kpatch_func *funcs = kpmod->funcs;
|
||||||
int num_funcs = kpmod->patches_nr;
|
int num_funcs = kpmod->patches_nr;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
@ -816,25 +906,26 @@ int kpatch_unregister(struct kpatch_module *kpmod)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
kpatch_num_registered--;
|
for (i = 0; i < num_funcs; i++) {
|
||||||
if (!kpatch_num_registered) {
|
if (!funcs[i].old_addr)
|
||||||
ret = unregister_ftrace_function(&kpatch_ftrace_ops);
|
continue;
|
||||||
|
ret = kpatch_ftrace_remove_func(funcs[i].old_addr);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
WARN(1, "can't unregister ftrace handler");
|
WARN(1, "can't unregister ftrace for address 0x%lx\n",
|
||||||
kpatch_num_registered++;
|
funcs[i].old_addr);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kpatch_put_modules(funcs, num_funcs);
|
kpatch_put_modules(funcs, num_funcs);
|
||||||
kpatch_remove_funcs_from_filter(funcs, num_funcs);
|
|
||||||
|
|
||||||
kfree(kpmod->internal->funcs);
|
kfree(kpmod->funcs);
|
||||||
kfree(kpmod->internal);
|
|
||||||
|
|
||||||
pr_notice("unloaded patch module \"%s\"\n", kpmod->mod->name);
|
pr_notice("unloaded patch module '%s'\n", kpmod->mod->name);
|
||||||
|
|
||||||
kpmod->enabled = false;
|
kpmod->enabled = false;
|
||||||
module_put(kpmod->mod);
|
module_put(kpmod->mod);
|
||||||
|
list_del(&kpmod->list);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
up(&kpatch_mutex);
|
up(&kpatch_mutex);
|
||||||
@ -842,23 +933,44 @@ out:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(kpatch_unregister);
|
EXPORT_SYMBOL(kpatch_unregister);
|
||||||
|
|
||||||
|
|
||||||
|
static struct notifier_block kpatch_module_nb = {
|
||||||
|
.notifier_call = kpatch_module_notify,
|
||||||
|
.priority = INT_MIN, /* called last */
|
||||||
|
};
|
||||||
|
|
||||||
static int kpatch_init(void)
|
static int kpatch_init(void)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
kpatch_root_kobj = kobject_create_and_add("kpatch", kernel_kobj);
|
kpatch_root_kobj = kobject_create_and_add("kpatch", kernel_kobj);
|
||||||
if (!kpatch_root_kobj)
|
if (!kpatch_root_kobj)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
kpatch_patches_kobj = kobject_create_and_add("patches",
|
kpatch_patches_kobj = kobject_create_and_add("patches",
|
||||||
kpatch_root_kobj);
|
kpatch_root_kobj);
|
||||||
if (!kpatch_patches_kobj)
|
if (!kpatch_patches_kobj) {
|
||||||
return -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
goto err_root_kobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = register_module_notifier(&kpatch_module_nb);
|
||||||
|
if (ret)
|
||||||
|
goto err_patches_kobj;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_patches_kobj:
|
||||||
|
kobject_put(kpatch_patches_kobj);
|
||||||
|
err_root_kobj:
|
||||||
|
kobject_put(kpatch_root_kobj);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kpatch_exit(void)
|
static void kpatch_exit(void)
|
||||||
{
|
{
|
||||||
WARN_ON(kpatch_num_registered != 0);
|
WARN_ON(kpatch_num_patched != 0);
|
||||||
|
WARN_ON(unregister_module_notifier(&kpatch_module_nb));
|
||||||
kobject_put(kpatch_patches_kobj);
|
kobject_put(kpatch_patches_kobj);
|
||||||
kobject_put(kpatch_root_kobj);
|
kobject_put(kpatch_root_kobj);
|
||||||
}
|
}
|
||||||
|
@ -46,16 +46,19 @@ struct kpatch_dynrela {
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
||||||
struct kpatch_internal;
|
|
||||||
|
|
||||||
struct kpatch_module {
|
struct kpatch_module {
|
||||||
|
/* public */
|
||||||
struct module *mod;
|
struct module *mod;
|
||||||
struct kpatch_patch *patches;
|
struct kpatch_patch *patches;
|
||||||
struct kpatch_dynrela *dynrelas;
|
struct kpatch_dynrela *dynrelas;
|
||||||
int patches_nr;
|
int patches_nr;
|
||||||
int dynrelas_nr;
|
int dynrelas_nr;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
struct kpatch_internal *internal; /* used internally by core module */
|
|
||||||
|
/* private */
|
||||||
|
struct list_head list;
|
||||||
|
struct kpatch_func *funcs;
|
||||||
|
bool pending;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct kobject *kpatch_patches_kobj;
|
extern struct kobject *kpatch_patches_kobj;
|
||||||
|
@ -1660,7 +1660,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
|
|||||||
sec->sh.sh_type = SHT_PROGBITS;
|
sec->sh.sh_type = SHT_PROGBITS;
|
||||||
sec->sh.sh_entsize = sizeof(*dynrelas);
|
sec->sh.sh_entsize = sizeof(*dynrelas);
|
||||||
sec->sh.sh_addralign = 8;
|
sec->sh.sh_addralign = 8;
|
||||||
sec->sh.sh_flags = SHF_ALLOC;
|
sec->sh.sh_flags = SHF_ALLOC | SHF_WRITE;
|
||||||
|
|
||||||
/* create .rela.kpatch.dynrelas*/
|
/* create .rela.kpatch.dynrelas*/
|
||||||
|
|
||||||
@ -1734,7 +1734,11 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
|
|||||||
rela->sym->name, result.value, result.size);
|
rela->sym->name, result.value, result.size);
|
||||||
|
|
||||||
/* dest filed in by rela entry below */
|
/* dest filed in by rela entry below */
|
||||||
|
if (!strcmp(objname, "vmlinux"))
|
||||||
dynrelas[index].src = result.value;
|
dynrelas[index].src = result.value;
|
||||||
|
else
|
||||||
|
/* for modules, src is discovered at runtime */
|
||||||
|
dynrelas[index].src = 0;
|
||||||
dynrelas[index].addend = rela->addend;
|
dynrelas[index].addend = rela->addend;
|
||||||
dynrelas[index].type = rela->type;
|
dynrelas[index].type = rela->type;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user