|
@@ -1973,6 +1973,83 @@ static void module_enable_nx(const struct module *mod) { }
|
|
|
static void module_disable_nx(const struct module *mod) { }
|
|
|
#endif
|
|
|
|
|
|
+#ifdef CONFIG_LIVEPATCH
|
|
|
+/*
|
|
|
+ * Persist Elf information about a module. Copy the Elf header,
|
|
|
+ * section header table, section string table, and symtab section
|
|
|
+ * index from info to mod->klp_info.
|
|
|
+ */
|
|
|
+static int copy_module_elf(struct module *mod, struct load_info *info)
|
|
|
+{
|
|
|
+ unsigned int size, symndx;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ size = sizeof(*mod->klp_info);
|
|
|
+ mod->klp_info = kmalloc(size, GFP_KERNEL);
|
|
|
+ if (mod->klp_info == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ /* Elf header */
|
|
|
+ size = sizeof(mod->klp_info->hdr);
|
|
|
+ memcpy(&mod->klp_info->hdr, info->hdr, size);
|
|
|
+
|
|
|
+ /* Elf section header table */
|
|
|
+ size = sizeof(*info->sechdrs) * info->hdr->e_shnum;
|
|
|
+ mod->klp_info->sechdrs = kmalloc(size, GFP_KERNEL);
|
|
|
+ if (mod->klp_info->sechdrs == NULL) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto free_info;
|
|
|
+ }
|
|
|
+ memcpy(mod->klp_info->sechdrs, info->sechdrs, size);
|
|
|
+
|
|
|
+ /* Elf section name string table */
|
|
|
+ size = info->sechdrs[info->hdr->e_shstrndx].sh_size;
|
|
|
+ mod->klp_info->secstrings = kmalloc(size, GFP_KERNEL);
|
|
|
+ if (mod->klp_info->secstrings == NULL) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto free_sechdrs;
|
|
|
+ }
|
|
|
+ memcpy(mod->klp_info->secstrings, info->secstrings, size);
|
|
|
+
|
|
|
+ /* Elf symbol section index */
|
|
|
+ symndx = info->index.sym;
|
|
|
+ mod->klp_info->symndx = symndx;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For livepatch modules, core_kallsyms.symtab is a complete
|
|
|
+ * copy of the original symbol table. Adjust sh_addr to point
|
|
|
+ * to core_kallsyms.symtab since the copy of the symtab in module
|
|
|
+ * init memory is freed at the end of do_init_module().
|
|
|
+ */
|
|
|
+ mod->klp_info->sechdrs[symndx].sh_addr = \
|
|
|
+ (unsigned long) mod->core_kallsyms.symtab;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+free_sechdrs:
|
|
|
+ kfree(mod->klp_info->sechdrs);
|
|
|
+free_info:
|
|
|
+ kfree(mod->klp_info);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void free_module_elf(struct module *mod)
|
|
|
+{
|
|
|
+ kfree(mod->klp_info->sechdrs);
|
|
|
+ kfree(mod->klp_info->secstrings);
|
|
|
+ kfree(mod->klp_info);
|
|
|
+}
|
|
|
+#else /* !CONFIG_LIVEPATCH */
|
|
|
+static int copy_module_elf(struct module *mod, struct load_info *info)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void free_module_elf(struct module *mod)
|
|
|
+{
|
|
|
+}
|
|
|
+#endif /* CONFIG_LIVEPATCH */
|
|
|
+
|
|
|
void __weak module_memfree(void *module_region)
|
|
|
{
|
|
|
vfree(module_region);
|
|
@@ -2011,6 +2088,9 @@ static void free_module(struct module *mod)
|
|
|
/* Free any allocated parameters. */
|
|
|
destroy_params(mod->kp, mod->num_kp);
|
|
|
|
|
|
+ if (is_livepatch_module(mod))
|
|
|
+ free_module_elf(mod);
|
|
|
+
|
|
|
/* Now we can delete it from the lists */
|
|
|
mutex_lock(&module_mutex);
|
|
|
/* Unlink carefully: kallsyms could be walking list. */
|
|
@@ -2126,6 +2206,10 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
|
|
|
(long)sym[i].st_value);
|
|
|
break;
|
|
|
|
|
|
+ case SHN_LIVEPATCH:
|
|
|
+ /* Livepatch symbols are resolved by livepatch */
|
|
|
+ break;
|
|
|
+
|
|
|
case SHN_UNDEF:
|
|
|
ksym = resolve_symbol_wait(mod, info, name);
|
|
|
/* Ok if resolved. */
|
|
@@ -2174,6 +2258,10 @@ static int apply_relocations(struct module *mod, const struct load_info *info)
|
|
|
if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC))
|
|
|
continue;
|
|
|
|
|
|
+ /* Livepatch relocation sections are applied by livepatch */
|
|
|
+ if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH)
|
|
|
+ continue;
|
|
|
+
|
|
|
if (info->sechdrs[i].sh_type == SHT_REL)
|
|
|
err = apply_relocate(info->sechdrs, info->strtab,
|
|
|
info->index.sym, i, mod);
|
|
@@ -2469,7 +2557,7 @@ static void layout_symtab(struct module *mod, struct load_info *info)
|
|
|
|
|
|
/* Compute total space required for the core symbols' strtab. */
|
|
|
for (ndst = i = 0; i < nsrc; i++) {
|
|
|
- if (i == 0 ||
|
|
|
+ if (i == 0 || is_livepatch_module(mod) ||
|
|
|
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum,
|
|
|
info->index.pcpu)) {
|
|
|
strtab_size += strlen(&info->strtab[src[i].st_name])+1;
|
|
@@ -2528,7 +2616,7 @@ static void add_kallsyms(struct module *mod, const struct load_info *info)
|
|
|
mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs;
|
|
|
src = mod->kallsyms->symtab;
|
|
|
for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) {
|
|
|
- if (i == 0 ||
|
|
|
+ if (i == 0 || is_livepatch_module(mod) ||
|
|
|
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum,
|
|
|
info->index.pcpu)) {
|
|
|
dst[ndst] = src[i];
|
|
@@ -2667,6 +2755,26 @@ static int copy_chunked_from_user(void *dst, const void __user *usrc, unsigned l
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_LIVEPATCH
|
|
|
+static int find_livepatch_modinfo(struct module *mod, struct load_info *info)
|
|
|
+{
|
|
|
+ mod->klp = get_modinfo(info, "livepatch") ? true : false;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#else /* !CONFIG_LIVEPATCH */
|
|
|
+static int find_livepatch_modinfo(struct module *mod, struct load_info *info)
|
|
|
+{
|
|
|
+ if (get_modinfo(info, "livepatch")) {
|
|
|
+ pr_err("%s: module is marked as livepatch module, but livepatch support is disabled",
|
|
|
+ mod->name);
|
|
|
+ return -ENOEXEC;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif /* CONFIG_LIVEPATCH */
|
|
|
+
|
|
|
/* Sets info->hdr and info->len. */
|
|
|
static int copy_module_from_user(const void __user *umod, unsigned long len,
|
|
|
struct load_info *info)
|
|
@@ -2821,6 +2929,10 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags)
|
|
|
"is unknown, you have been warned.\n", mod->name);
|
|
|
}
|
|
|
|
|
|
+ err = find_livepatch_modinfo(mod, info);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
/* Set up license info based on the info section */
|
|
|
set_license(mod, get_modinfo(info, "license"));
|
|
|
|
|
@@ -3494,6 +3606,12 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
|
|
if (err < 0)
|
|
|
goto coming_cleanup;
|
|
|
|
|
|
+ if (is_livepatch_module(mod)) {
|
|
|
+ err = copy_module_elf(mod, info);
|
|
|
+ if (err < 0)
|
|
|
+ goto sysfs_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
/* Get rid of temporary copy. */
|
|
|
free_copy(info);
|
|
|
|
|
@@ -3502,11 +3620,12 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
|
|
|
|
|
return do_init_module(mod);
|
|
|
|
|
|
+ sysfs_cleanup:
|
|
|
+ mod_sysfs_teardown(mod);
|
|
|
coming_cleanup:
|
|
|
blocking_notifier_call_chain(&module_notify_list,
|
|
|
MODULE_STATE_GOING, mod);
|
|
|
klp_module_going(mod);
|
|
|
-
|
|
|
bug_cleanup:
|
|
|
/* module_bug_cleanup needs module_mutex protection */
|
|
|
mutex_lock(&module_mutex);
|