Merge pull request #620 from joe-lawrence/617_KASLR_support

Add CONFIG_RANDOMIZE_BASE KASLR support
This commit is contained in:
Jessica Yu 2016-11-29 13:31:02 -08:00 committed by GitHub
commit 6fa5360c22
3 changed files with 84 additions and 86 deletions

View File

@ -75,8 +75,11 @@ struct kpatch_backtrace_args {
};
struct kpatch_kallsyms_args {
const char *objname;
const char *name;
unsigned long addr;
unsigned long count;
unsigned long pos;
};
/* this is a double loop, use goto instead of break */
@ -582,58 +585,75 @@ static int kpatch_kallsyms_callback(void *data, const char *name,
unsigned long addr)
{
struct kpatch_kallsyms_args *args = data;
bool vmlinux = !strcmp(args->objname, "vmlinux");
if (args->addr == addr && !strcmp(args->name, name))
if ((mod && vmlinux) || (!mod && !vmlinux))
return 0;
if (strcmp(args->name, name))
return 0;
if (!vmlinux && strcmp(args->objname, mod->name))
return 0;
args->addr = addr;
args->count++;
/*
* Finish the search when the symbol is found for the desired position
* or the position is not defined for a non-unique symbol.
*/
if ((args->pos && (args->count == args->pos)) ||
(!args->pos && (args->count > 1))) {
return 1;
return 0;
}
static int kpatch_verify_symbol_match(const char *name, unsigned long addr)
{
int ret;
struct kpatch_kallsyms_args args = {
.name = name,
.addr = addr,
};
ret = kallsyms_on_each_symbol(kpatch_kallsyms_callback, &args);
if (!ret) {
pr_err("base kernel mismatch for symbol '%s'\n", name);
pr_err("expected address was 0x%016lx\n", addr);
return -EINVAL;
}
return 0;
}
static unsigned long kpatch_find_module_symbol(struct module *mod,
const char *name)
static int kpatch_find_object_symbol(const char *objname, const char *name,
unsigned long sympos, unsigned long *addr)
{
char buf[KSYM_SYMBOL_LEN];
struct kpatch_kallsyms_args args = {
.objname = objname,
.name = name,
.addr = 0,
.count = 0,
.pos = sympos,
};
/* check total string length for overrun */
if (strlen(mod->name) + strlen(name) + 1 >= KSYM_SYMBOL_LEN) {
pr_err("buffer overrun finding symbol '%s' in module '%s'\n",
name, mod->name);
mutex_lock(&module_mutex);
kallsyms_on_each_symbol(kpatch_kallsyms_callback, &args);
mutex_unlock(&module_mutex);
/*
* Ensure an address was found. If sympos is 0, ensure symbol is unique;
* otherwise ensure the symbol position count matches sympos.
*/
if (args.addr == 0)
pr_err("symbol '%s' not found in symbol table\n", name);
else if (args.count > 1 && sympos == 0) {
pr_err("unresolvable ambiguity for symbol '%s' in object '%s'\n",
name, objname);
} else if (sympos != args.count && sympos > 0) {
pr_err("symbol position %lu for symbol '%s' in object '%s' not found\n",
sympos, name, objname);
} else {
*addr = args.addr;
return 0;
}
/* encode symbol name as "mod->name:name" */
strcpy(buf, mod->name);
strcat(buf, ":");
strcat(buf, name);
return kallsyms_lookup_name(buf);
*addr = 0;
return -EINVAL;
}
/*
* External symbols are located outside the parent object (where the parent
* object is either vmlinux or the kmod being patched).
*/
static unsigned long kpatch_find_external_symbol(struct kpatch_module *kpmod,
const char *name)
static int kpatch_find_external_symbol(const char *objname, const char *name,
unsigned long sympos, unsigned long *addr)
{
const struct kernel_symbol *sym;
@ -641,11 +661,13 @@ static unsigned long kpatch_find_external_symbol(struct kpatch_module *kpmod,
preempt_disable();
sym = find_symbol(name, NULL, NULL, true, true);
preempt_enable();
if (sym)
return sym->value;
if (sym) {
*addr = sym->value;
return 0;
}
/* otherwise check if it's in another .o within the patch module */
return kpatch_find_module_symbol(kpmod->mod, name);
return kpatch_find_object_symbol(objname, name, sympos, addr);
}
static int kpatch_write_relocations(struct kpatch_module *kpmod,
@ -661,31 +683,21 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod,
unsigned long core = (unsigned long)kpmod->mod->module_core;
unsigned long core_size = kpmod->mod->core_size;
#endif
unsigned long src;
list_for_each_entry(dynrela, &object->dynrelas, list) {
if (!strcmp(object->name, "vmlinux")) {
ret = kpatch_verify_symbol_match(dynrela->name,
dynrela->src);
if (ret)
return ret;
} else {
/* module, dynrela->src needs to be discovered */
if (dynrela->external)
src = kpatch_find_external_symbol(kpmod,
dynrela->name);
else
src = kpatch_find_module_symbol(object->mod,
dynrela->name);
if (!src) {
pr_err("unable to find symbol '%s'\n",
dynrela->name);
return -EINVAL;
}
dynrela->src = src;
if (dynrela->external)
ret = kpatch_find_external_symbol(kpmod->mod->name,
dynrela->name,
dynrela->sympos,
&dynrela->src);
else
ret = kpatch_find_object_symbol(object->name,
dynrela->name,
dynrela->sympos,
&dynrela->src);
if (ret) {
pr_err("unable to find symbol '%s'\n", dynrela->name);
return ret;
}
switch (dynrela->type) {
@ -824,25 +836,14 @@ static int kpatch_link_object(struct kpatch_module *kpmod,
list_for_each_entry(func, &object->funcs, list) {
/* calculate actual old location */
if (vmlinux) {
ret = kpatch_verify_symbol_match(func->name,
func->old_addr);
if (ret) {
func_err = func;
goto err_ftrace;
}
} else {
unsigned long old_addr;
old_addr = kpatch_find_module_symbol(mod, func->name);
if (!old_addr) {
pr_err("unable to find symbol '%s' in module '%s\n",
func->name, mod->name);
func_err = func;
ret = -EINVAL;
goto err_ftrace;
}
func->old_addr = old_addr;
/* lookup the old location */
ret = kpatch_find_object_symbol(object->name,
func->name,
func->sympos,
&func->old_addr);
if (ret) {
func_err = func;
goto err_ftrace;
}
/* add to ftrace filter and register handler if needed */

View File

@ -38,6 +38,7 @@ struct kpatch_func {
unsigned long new_size;
unsigned long old_addr;
unsigned long old_size;
unsigned long sympos;
const char *name;
struct list_head list;
int force;
@ -51,6 +52,7 @@ struct kpatch_dynrela {
unsigned long dest;
unsigned long src;
unsigned long type;
unsigned long sympos;
const char *name;
int addend;
int external;

View File

@ -271,13 +271,8 @@ static int patch_make_funcs_list(struct list_head *objects)
func->new_addr = p_func->new_addr;
func->new_size = p_func->new_size;
if (!strcmp("vmlinux", object->name))
func->old_addr = p_func->old_addr;
else
func->old_addr = 0x0;
func->old_size = p_func->old_size;
func->sympos = p_func->sympos;
func->name = p_func->name;
func->force = patch_is_func_forced(func->new_addr);
list_add_tail(&func->list, &object->funcs);
@ -315,8 +310,8 @@ static int patch_make_dynrelas_list(struct list_head *objects)
return -ENOMEM;
dynrela->dest = p_dynrela->dest;
dynrela->src = p_dynrela->src;
dynrela->type = p_dynrela->type;
dynrela->sympos = p_dynrela->sympos;
dynrela->name = p_dynrela->name;
dynrela->external = p_dynrela->external;
dynrela->addend = p_dynrela->addend;