Commit b2b018ef authored by Chris J Arges's avatar Chris J Arges Committed by Jiri Kosina

livepatch: add old_sympos as disambiguator field to klp_func

Currently, patching objects with duplicate symbol names fail because the
creation of the sysfs function directory collides with the previous
attempt. Appending old_addr to the function name is problematic as it
reveals the address of the function being patch to a normal user. Using
the symbol's occurrence in kallsyms to postfix the function name in the
sysfs directory solves the issue of having consistent unique names and
ensuring that the address is not exposed to a normal user.

In addition, using the symbol position as the user's method to disambiguate
symbols instead of addr allows for disambiguating symbols in modules as
well for both function addresses and for relocs. This also simplifies much
of the code. Special handling for kASLR is no longer needed and can be
removed. The klp_find_verify_func_addr function can be replaced by
klp_find_object_symbol, and klp_verify_vmlinux_symbol and its callback can
be removed completely.

In cases of duplicate symbols, old_sympos will be used to disambiguate
instead of old_addr. By default old_sympos will be 0, and patching will
only succeed if the symbol is unique. Specifying a positive value will
ensure that occurrence of the symbol in kallsyms for the patched object
will be used for patching if it is valid.

In addition, make old_addr an internal structure field not to be specified
by the user. Finally, remove klp_find_verify_func_addr as it can be
replaced by klp_find_object_symbol directly.

Support for symbol position disambiguation for relocations is added in the
next patch in this series.
Signed-off-by: default avatarChris J Arges <chris.j.arges@canonical.com>
Reviewed-by: default avatarPetr Mladek <pmladek@suse.com>
Acked-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent a3d66b5a
...@@ -37,8 +37,9 @@ enum klp_state { ...@@ -37,8 +37,9 @@ enum klp_state {
* struct klp_func - function structure for live patching * struct klp_func - function structure for live patching
* @old_name: name of the function to be patched * @old_name: name of the function to be patched
* @new_func: pointer to the patched function code * @new_func: pointer to the patched function code
* @old_addr: a hint conveying at what address the old function * @old_sympos: a hint indicating which symbol position the old function
* can be found (optional, vmlinux patches only) * can be found (optional)
* @old_addr: the address of the function being patched
* @kobj: kobject for sysfs resources * @kobj: kobject for sysfs resources
* @state: tracks function-level patch application state * @state: tracks function-level patch application state
* @stack_node: list node for klp_ops func_stack list * @stack_node: list node for klp_ops func_stack list
...@@ -48,16 +49,16 @@ struct klp_func { ...@@ -48,16 +49,16 @@ struct klp_func {
const char *old_name; const char *old_name;
void *new_func; void *new_func;
/* /*
* The old_addr field is optional and can be used to resolve * The old_sympos field is optional and can be used to resolve
* duplicate symbol names in the vmlinux object. If this * duplicate symbol names in livepatch objects. If this field is zero,
* information is not present, the symbol is located by name * it is expected the symbol is unique, otherwise patching fails. If
* with kallsyms. If the name is not unique and old_addr is * this value is greater than zero then that occurrence of the symbol
* not provided, the patch application fails as there is no * in kallsyms for the given object is used.
* way to resolve the ambiguity.
*/ */
unsigned long old_addr; unsigned long old_sympos;
/* internal */ /* internal */
unsigned long old_addr;
struct kobject kobj; struct kobject kobj;
enum klp_state state; enum klp_state state;
struct list_head stack_node; struct list_head stack_node;
......
...@@ -135,13 +135,8 @@ struct klp_find_arg { ...@@ -135,13 +135,8 @@ struct klp_find_arg {
const char *objname; const char *objname;
const char *name; const char *name;
unsigned long addr; unsigned long addr;
/*
* If count == 0, the symbol was not found. If count == 1, a unique
* match was found and addr is set. If count > 1, there is
* unresolvable ambiguity among "count" number of symbols with the same
* name in the same object.
*/
unsigned long count; unsigned long count;
unsigned long pos;
}; };
static int klp_find_callback(void *data, const char *name, static int klp_find_callback(void *data, const char *name,
...@@ -158,37 +153,48 @@ static int klp_find_callback(void *data, const char *name, ...@@ -158,37 +153,48 @@ static int klp_find_callback(void *data, const char *name,
if (args->objname && strcmp(args->objname, mod->name)) if (args->objname && strcmp(args->objname, mod->name))
return 0; return 0;
/*
* args->addr might be overwritten if another match is found
* but klp_find_object_symbol() handles this and only returns the
* addr if count == 1.
*/
args->addr = addr; args->addr = addr;
args->count++; 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; return 0;
} }
static int klp_find_object_symbol(const char *objname, const char *name, static int klp_find_object_symbol(const char *objname, const char *name,
unsigned long *addr) unsigned long sympos, unsigned long *addr)
{ {
struct klp_find_arg args = { struct klp_find_arg args = {
.objname = objname, .objname = objname,
.name = name, .name = name,
.addr = 0, .addr = 0,
.count = 0 .count = 0,
.pos = sympos,
}; };
mutex_lock(&module_mutex); mutex_lock(&module_mutex);
kallsyms_on_each_symbol(klp_find_callback, &args); kallsyms_on_each_symbol(klp_find_callback, &args);
mutex_unlock(&module_mutex); mutex_unlock(&module_mutex);
if (args.count == 0) /*
* 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); pr_err("symbol '%s' not found in symbol table\n", name);
else if (args.count > 1) else if (args.count > 1 && sympos == 0) {
pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n", pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n",
args.count, name, objname); args.count, name, objname);
else { } else if (sympos != args.count && sympos > 0) {
pr_err("symbol position %lu for symbol '%s' in object '%s' not found\n",
sympos, name, objname ? objname : "vmlinux");
} else {
*addr = args.addr; *addr = args.addr;
return 0; return 0;
} }
...@@ -236,27 +242,6 @@ static int klp_verify_vmlinux_symbol(const char *name, unsigned long addr) ...@@ -236,27 +242,6 @@ static int klp_verify_vmlinux_symbol(const char *name, unsigned long addr)
return 0; return 0;
} }
static int klp_find_verify_func_addr(struct klp_object *obj,
struct klp_func *func)
{
int ret;
#if defined(CONFIG_RANDOMIZE_BASE)
/* If KASLR has been enabled, adjust old_addr accordingly */
if (kaslr_enabled() && func->old_addr)
func->old_addr += kaslr_offset();
#endif
if (!func->old_addr || klp_is_module(obj))
ret = klp_find_object_symbol(obj->name, func->old_name,
&func->old_addr);
else
ret = klp_verify_vmlinux_symbol(func->old_name,
func->old_addr);
return ret;
}
/* /*
* external symbols are located outside the parent object (where the parent * external symbols are located outside the parent object (where the parent
* object is either vmlinux or the kmod being patched). * object is either vmlinux or the kmod being patched).
...@@ -276,8 +261,11 @@ static int klp_find_external_symbol(struct module *pmod, const char *name, ...@@ -276,8 +261,11 @@ static int klp_find_external_symbol(struct module *pmod, const char *name,
} }
preempt_enable(); preempt_enable();
/* otherwise check if it's in another .o within the patch module */ /*
return klp_find_object_symbol(pmod->name, name, addr); * Check if it's in another .o within the patch module. This also
* checks that the external symbol is unique.
*/
return klp_find_object_symbol(pmod->name, name, 0, addr);
} }
static int klp_write_object_relocations(struct module *pmod, static int klp_write_object_relocations(struct module *pmod,
...@@ -313,7 +301,7 @@ static int klp_write_object_relocations(struct module *pmod, ...@@ -313,7 +301,7 @@ static int klp_write_object_relocations(struct module *pmod,
else else
ret = klp_find_object_symbol(obj->mod->name, ret = klp_find_object_symbol(obj->mod->name,
reloc->name, reloc->name,
&reloc->val); 0, &reloc->val);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -756,7 +744,9 @@ static int klp_init_object_loaded(struct klp_patch *patch, ...@@ -756,7 +744,9 @@ static int klp_init_object_loaded(struct klp_patch *patch,
} }
klp_for_each_func(obj, func) { klp_for_each_func(obj, func) {
ret = klp_find_verify_func_addr(obj, func); ret = klp_find_object_symbol(obj->name, func->old_name,
func->old_sympos,
&func->old_addr);
if (ret) if (ret)
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment