fix activeness safety check when unpatching

When unpatching, the activeness safety logic should check for the new
function on the stack, not the old one.

Fixes #64.
This commit is contained in:
Josh Poimboeuf 2014-04-21 11:49:58 -05:00
parent 48cc3a409e
commit f3f39c0587
5 changed files with 28 additions and 5 deletions

View File

@ -118,12 +118,24 @@ void kpatch_backtrace_address_verify(void *data, unsigned long address,
return; return;
for (i = 0; i < kpmod->num_funcs; i++) { for (i = 0; i < kpmod->num_funcs; i++) {
struct kpatch_func *func = &kpmod->funcs[i]; unsigned long func_addr, func_size;
struct kpatch_func *func, *active_func;
if (address >= func->old_addr && func = &kpmod->funcs[i];
address < func->old_addr + func->old_size) { active_func = kpatch_get_func(func->old_addr);
if (!active_func) {
/* patching an unpatched func */
func_addr = func->old_addr;
func_size = func->old_size;
} else {
/* repatching or unpatching */
func_addr = active_func->new_addr;
func_size = active_func->new_size;
}
if (address >= func_addr && address < func_addr + func_size) {
pr_err("activeness safety check failed for function " pr_err("activeness safety check failed for function "
"at address 0x%lx\n", func->old_addr); "at address 0x%lx\n", func_addr);
args->ret = -EBUSY; args->ret = -EBUSY;
return; return;
} }

View File

@ -29,6 +29,7 @@
struct kpatch_func { struct kpatch_func {
unsigned long new_addr; unsigned long new_addr;
unsigned long new_size;
unsigned long old_addr; unsigned long old_addr;
unsigned long old_size; unsigned long old_size;
struct hlist_node node; struct hlist_node node;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2013 Josh Poimboeuf <jpoimboe@redhat.com> * Copyright (C) 2013-2014 Josh Poimboeuf <jpoimboe@redhat.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -48,6 +48,7 @@ static int __init patch_init(void)
kpmod.funcs[i].old_addr = patches[i].old_addr; kpmod.funcs[i].old_addr = patches[i].old_addr;
kpmod.funcs[i].old_size = patches[i].old_size; kpmod.funcs[i].old_size = patches[i].old_size;
kpmod.funcs[i].new_addr = patches[i].new_addr; kpmod.funcs[i].new_addr = patches[i].new_addr;
kpmod.funcs[i].new_size = patches[i].new_size;
} }
ret = kpatch_register(&kpmod); ret = kpatch_register(&kpmod);

View File

@ -26,6 +26,7 @@
struct kpatch_patch { struct kpatch_patch {
unsigned long new_addr; unsigned long new_addr;
unsigned long new_size;
unsigned long old_addr; unsigned long old_addr;
unsigned long old_size; unsigned long old_size;
}; };

View File

@ -315,10 +315,18 @@ int main(int argc, char **argv)
for_each_sym(&symlist, cur) { for_each_sym(&symlist, cur) {
if (cur->action != PATCH) if (cur->action != PATCH)
continue; continue;
patches_data[i].old_addr = cur->vm_addr; patches_data[i].old_addr = cur->vm_addr;
patches_data[i].old_size = cur->vm_len; patches_data[i].old_size = cur->vm_len;
patches_data[i].new_size = cur->sym.st_size;
/*
* Use a relocation to set patches_data[i].new_addr at module
* loading time.
*/
relas_data[i].r_offset = i * sizeof(struct kpatch_patch); relas_data[i].r_offset = i * sizeof(struct kpatch_patch);
relas_data[i].r_info = GELF_R_INFO(cur->index, R_X86_64_64); relas_data[i].r_info = GELF_R_INFO(cur->index, R_X86_64_64);
i++; i++;
} }