[RFC PATCH 1/2] Working klp resolve sym functions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Implement klp_resolve_symbol() and call it from the module loader.

If we see the module being loaded is a livepatch module, and the                                                     
symbol's shndx is SHN_UNDEF, we call klp_resolve_symbol_wait() to look up
the symbol ourselves. Recall that we use the kallsyms API to look up symbols
that are not exported and to find symbols specific to a module. We supply the
"objname" (i.e. name of module) for the kallsyms lookup through kpatch-build,
which will "tag" the right symbols.

For symbols that need to be specially resolved by livepatch (i.e. they are non-exported                              
local symbols) their names are appended with "tags" that contain their "objname."
This will be already done through the kpatch-build system through create-diff-object [1]
create-diff-object will no longer strip symbols and now keep the symbols associated
with the former dynrelas.

[1] https://github.com/flaming-toast/kpatch/blob/1dc8dddc514b59ee3d417c3b4e3ae890be223149/kpatch-build/create-diff-object.c#L2548 

Signed-off-by: Jessica Yu <jeyu@xxxxxxxxxx>
---
 kernel/livepatch/core.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++
 kernel/module.c         | 43 +++++++++++++++++++++++--
 2 files changed, 123 insertions(+), 3 deletions(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 3f9f1d6..7c1cd5c 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -195,6 +195,89 @@ static int klp_find_object_symbol(const char *objname, const char *name,
 	return -EINVAL;
 }
 
+/* Module loader stuff */
+/* Check if a symbol is tagged with form symname.kpatch.objname */
+static int klp_sym_tagged(const char *name)
+{
+	if (strstr(name, ".kpatch."))
+		return 1;
+
+	return 0;
+}
+
+/* Extract objname from tagged symbol name (symname.kpatch.objname)
+ * Destructive to @name
+ */
+static char *klp_extract_objname(const char *name)
+{
+	char *objname;
+
+	objname = strrchr(name, '.');
+	if (objname) {
+		*objname = '\0';
+		objname++;
+	}
+
+	return objname;
+}
+
+/* Extract real symbol name from tagged symbol name (symname.kpatch.objname)
+ * Destructive to @name
+ */
+static char *klp_extract_symname(const char *name)
+{
+	char *symname;
+
+	symname = strrchr(name, '.');
+	if (symname) {
+		*symname = '\0';
+		symname = (char *)name;
+	}
+
+	return symname;
+}
+
+unsigned long klp_resolve_symbol(struct module *mod,
+						  const char *name)
+{
+	const struct kernel_symbol *sym;
+	unsigned long addr;
+	char *objname;
+	char *symname;
+	int ret;
+
+	addr = 0;
+	objname = NULL;
+	symname = kstrdup(name, GFP_KERNEL); /* work with a copy */
+
+	/* Extract tag (symname.kpatch.objname) */
+	if (klp_sym_tagged(name)) {
+		objname = klp_extract_objname(symname);
+		symname = klp_extract_symname(symname);
+	}
+
+	sched_annotate_sleep();
+	mutex_lock(&module_mutex);
+	sym = find_symbol(symname, NULL, NULL, true, true);
+	if (sym)
+		addr = sym->value;
+	else { /* Attempt to find symbol in patch module or objname */
+		ret = klp_find_object_symbol(mod->name, symname, &addr);
+		if (ret) {
+			ret = klp_find_object_symbol(objname, symname, &addr);
+			if (ret)
+				goto unlock;
+		}
+	}
+
+unlock:
+	mutex_unlock(&module_mutex);
+	kfree(symname);
+	return addr; /* could be 0x0 */
+}
+
+/* end of module loader stuff */
+
 struct klp_verify_args {
 	const char *name;
 	const unsigned long addr;
diff --git a/kernel/module.c b/kernel/module.c
index ec53f59..41b7838 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -92,6 +92,10 @@
 /* If this is set, the section belongs in the init part of the module */
 #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
 
+#ifdef CONFIG_LIVEPATCH
+extern unsigned long klp_resolve_symbol(struct module *mod,
+						  const char *name);
+#endif
 /*
  * Mutex protects:
  * 1) List of modules (also safely readable with preempt_disable),
@@ -183,6 +187,8 @@ struct load_info {
 	} index;
 };
 
+static char *get_modinfo(struct load_info *info, const char *tag);
+
 /* We require a truly strong try_module_get(): 0 means failure due to
    ongoing or failed initialization etc. */
 static inline int strong_try_module_get(struct module *mod)
@@ -1257,6 +1263,23 @@ unlock:
 	return sym;
 }
 
+#ifdef CONFIG_LIVEPATCH
+static unsigned long
+klp_resolve_symbol_wait(struct module *mod,
+		    const char *name)
+{
+	unsigned long addr;
+
+	if (wait_event_interruptible_timeout(module_wq,
+			(addr = klp_resolve_symbol(mod, name)) != (long) 0x0, 30 * HZ) <= 0) {
+		pr_warn("%s: gave up waiting for init of module.\n",
+			mod->name);
+	}
+
+	return addr; /* could be 0x0 */
+}
+#endif
+
 static const struct kernel_symbol *
 resolve_symbol_wait(struct module *mod,
 		    const struct load_info *info,
@@ -1934,11 +1957,15 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
 {
 	Elf_Shdr *symsec = &info->sechdrs[info->index.sym];
 	Elf_Sym *sym = (void *)symsec->sh_addr;
+	unsigned long addr;
 	unsigned long secbase;
 	unsigned int i;
 	int ret = 0;
 	const struct kernel_symbol *ksym;
 
+	ksym = NULL;
+	addr = 0;
+
 	for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) {
 		const char *name = info->strtab + sym[i].st_name;
 
@@ -1963,15 +1990,25 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
 			break;
 
 		case SHN_UNDEF:
+#ifdef CONFIG_LIVEPATCH
+			if (get_modinfo((struct load_info *)info, "livepatch"))
+				addr = klp_resolve_symbol_wait(mod, name);
+			else
+				ksym = resolve_symbol_wait(mod, info, name);
+#else
 			ksym = resolve_symbol_wait(mod, info, name);
+#endif
 			/* Ok if resolved.  */
-			if (ksym && !IS_ERR(ksym)) {
-				sym[i].st_value = ksym->value;
+			if (ksym && !IS_ERR(ksym))
+				addr = ksym->value;
+
+			if (addr) {
+				sym[i].st_value = addr;
 				break;
 			}
 
 			/* Ok if weak.  */
-			if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK)
+			if (!addr && ELF_ST_BIND(sym[i].st_info) == STB_WEAK)
 				break;
 
 			pr_warn("%s: Unknown symbol %s (err %li)\n",
-- 
2.4.2

--
To unsubscribe from this list: send the line "unsubscribe live-patching" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux