]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - kernel/livepatch/core.c
livepatch: function,sympos scheme in livepatch sysfs directory
[mirror_ubuntu-artful-kernel.git] / kernel / livepatch / core.c
index 6e5344112419ca43ace3474466db35d1692752ab..94893e844e445c18d1ac51db328217ff888511df 100644 (file)
@@ -135,13 +135,8 @@ struct klp_find_arg {
        const char *objname;
        const char *name;
        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 pos;
 };
 
 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))
                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->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 klp_find_object_symbol(const char *objname, const char *name,
-                                 unsigned long *addr)
+                                 unsigned long sympos, unsigned long *addr)
 {
        struct klp_find_arg args = {
                .objname = objname,
                .name = name,
                .addr = 0,
-               .count = 0
+               .count = 0,
+               .pos = sympos,
        };
 
        mutex_lock(&module_mutex);
        kallsyms_on_each_symbol(klp_find_callback, &args);
        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);
-       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",
                       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;
                return 0;
        }
@@ -197,66 +203,6 @@ static int klp_find_object_symbol(const char *objname, const char *name,
        return -EINVAL;
 }
 
-struct klp_verify_args {
-       const char *name;
-       const unsigned long addr;
-};
-
-static int klp_verify_callback(void *data, const char *name,
-                              struct module *mod, unsigned long addr)
-{
-       struct klp_verify_args *args = data;
-
-       if (!mod &&
-           !strcmp(args->name, name) &&
-           args->addr == addr)
-               return 1;
-
-       return 0;
-}
-
-static int klp_verify_vmlinux_symbol(const char *name, unsigned long addr)
-{
-       struct klp_verify_args args = {
-               .name = name,
-               .addr = addr,
-       };
-       int ret;
-
-       mutex_lock(&module_mutex);
-       ret = kallsyms_on_each_symbol(klp_verify_callback, &args);
-       mutex_unlock(&module_mutex);
-
-       if (!ret) {
-               pr_err("symbol '%s' not found at specified address 0x%016lx, kernel mismatch?\n",
-                       name, addr);
-               return -EINVAL;
-       }
-
-       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
  * object is either vmlinux or the kmod being patched).
@@ -276,14 +222,18 @@ static int klp_find_external_symbol(struct module *pmod, const char *name,
        }
        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,
                                        struct klp_object *obj)
 {
        int ret;
+       unsigned long val;
        struct klp_reloc *reloc;
 
        if (WARN_ON(!klp_is_object_loaded(obj)))
@@ -293,29 +243,27 @@ static int klp_write_object_relocations(struct module *pmod,
                return -EINVAL;
 
        for (reloc = obj->relocs; reloc->name; reloc++) {
-               if (!klp_is_module(obj)) {
-                       ret = klp_verify_vmlinux_symbol(reloc->name,
-                                                       reloc->val);
-                       if (ret)
-                               return ret;
-               } else {
-                       /* module, reloc->val needs to be discovered */
-                       if (reloc->external)
-                               ret = klp_find_external_symbol(pmod,
-                                                              reloc->name,
-                                                              &reloc->val);
-                       else
-                               ret = klp_find_object_symbol(obj->mod->name,
-                                                            reloc->name,
-                                                            &reloc->val);
-                       if (ret)
-                               return ret;
-               }
+               /* discover the address of the referenced symbol */
+               if (reloc->external) {
+                       if (reloc->sympos > 0) {
+                               pr_err("non-zero sympos for external reloc symbol '%s' is not supported\n",
+                                      reloc->name);
+                               return -EINVAL;
+                       }
+                       ret = klp_find_external_symbol(pmod, reloc->name, &val);
+               } else
+                       ret = klp_find_object_symbol(obj->name,
+                                                    reloc->name,
+                                                    reloc->sympos,
+                                                    &val);
+               if (ret)
+                       return ret;
+
                ret = klp_write_module_reloc(pmod, reloc->type, reloc->loc,
-                                            reloc->val + reloc->addend);
+                                            val + reloc->addend);
                if (ret) {
                        pr_err("relocation failed for symbol '%s' at 0x%016lx (%d)\n",
-                              reloc->name, reloc->val, ret);
+                              reloc->name, val, ret);
                        return ret;
                }
        }
@@ -587,7 +535,7 @@ EXPORT_SYMBOL_GPL(klp_enable_patch);
  * /sys/kernel/livepatch/<patch>
  * /sys/kernel/livepatch/<patch>/enabled
  * /sys/kernel/livepatch/<patch>/<object>
- * /sys/kernel/livepatch/<patch>/<object>/<func>
+ * /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
  */
 
 static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
@@ -732,8 +680,14 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
        INIT_LIST_HEAD(&func->stack_node);
        func->state = KLP_DISABLED;
 
+       /* The format for the sysfs directory is <function,sympos> where sympos
+        * is the nth occurrence of this symbol in kallsyms for the patched
+        * object. If the user selects 0 for old_sympos, then 1 will be used
+        * since a unique symbol will be the first occurrence.
+        */
        return kobject_init_and_add(&func->kobj, &klp_ktype_func,
-                                   &obj->kobj, "%s", func->old_name);
+                                   &obj->kobj, "%s,%lu", func->old_name,
+                                   func->old_sympos ? func->old_sympos : 1);
 }
 
 /* parts of the initialization that is done only when the object is loaded */
@@ -750,7 +704,9 @@ static int klp_init_object_loaded(struct klp_patch *patch,
        }
 
        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)
                        return ret;
        }