diff options
-rw-r--r-- | include/linux/module.h | 25 | ||||
-rw-r--r-- | kernel/module.c | 125 |
2 files changed, 147 insertions, 3 deletions
diff --git a/include/linux/module.h b/include/linux/module.h index 2bb0c3085706..3daf2b3a09d2 100644 --- a/include/linux/module.h +++ b/include/linux/module.h | |||
@@ -330,6 +330,15 @@ struct mod_kallsyms { | |||
330 | char *strtab; | 330 | char *strtab; |
331 | }; | 331 | }; |
332 | 332 | ||
333 | #ifdef CONFIG_LIVEPATCH | ||
334 | struct klp_modinfo { | ||
335 | Elf_Ehdr hdr; | ||
336 | Elf_Shdr *sechdrs; | ||
337 | char *secstrings; | ||
338 | unsigned int symndx; | ||
339 | }; | ||
340 | #endif | ||
341 | |||
333 | struct module { | 342 | struct module { |
334 | enum module_state state; | 343 | enum module_state state; |
335 | 344 | ||
@@ -456,7 +465,11 @@ struct module { | |||
456 | #endif | 465 | #endif |
457 | 466 | ||
458 | #ifdef CONFIG_LIVEPATCH | 467 | #ifdef CONFIG_LIVEPATCH |
468 | bool klp; /* Is this a livepatch module? */ | ||
459 | bool klp_alive; | 469 | bool klp_alive; |
470 | |||
471 | /* Elf information */ | ||
472 | struct klp_modinfo *klp_info; | ||
460 | #endif | 473 | #endif |
461 | 474 | ||
462 | #ifdef CONFIG_MODULE_UNLOAD | 475 | #ifdef CONFIG_MODULE_UNLOAD |
@@ -630,6 +643,18 @@ static inline bool module_requested_async_probing(struct module *module) | |||
630 | return module && module->async_probe_requested; | 643 | return module && module->async_probe_requested; |
631 | } | 644 | } |
632 | 645 | ||
646 | #ifdef CONFIG_LIVEPATCH | ||
647 | static inline bool is_livepatch_module(struct module *mod) | ||
648 | { | ||
649 | return mod->klp; | ||
650 | } | ||
651 | #else /* !CONFIG_LIVEPATCH */ | ||
652 | static inline bool is_livepatch_module(struct module *mod) | ||
653 | { | ||
654 | return false; | ||
655 | } | ||
656 | #endif /* CONFIG_LIVEPATCH */ | ||
657 | |||
633 | #else /* !CONFIG_MODULES... */ | 658 | #else /* !CONFIG_MODULES... */ |
634 | 659 | ||
635 | /* Given an address, look for it in the exception tables. */ | 660 | /* Given an address, look for it in the exception tables. */ |
diff --git a/kernel/module.c b/kernel/module.c index 041200ca4a2d..5f71aa63ed2a 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -1973,6 +1973,83 @@ static void module_enable_nx(const struct module *mod) { } | |||
1973 | static void module_disable_nx(const struct module *mod) { } | 1973 | static void module_disable_nx(const struct module *mod) { } |
1974 | #endif | 1974 | #endif |
1975 | 1975 | ||
1976 | #ifdef CONFIG_LIVEPATCH | ||
1977 | /* | ||
1978 | * Persist Elf information about a module. Copy the Elf header, | ||
1979 | * section header table, section string table, and symtab section | ||
1980 | * index from info to mod->klp_info. | ||
1981 | */ | ||
1982 | static int copy_module_elf(struct module *mod, struct load_info *info) | ||
1983 | { | ||
1984 | unsigned int size, symndx; | ||
1985 | int ret; | ||
1986 | |||
1987 | size = sizeof(*mod->klp_info); | ||
1988 | mod->klp_info = kmalloc(size, GFP_KERNEL); | ||
1989 | if (mod->klp_info == NULL) | ||
1990 | return -ENOMEM; | ||
1991 | |||
1992 | /* Elf header */ | ||
1993 | size = sizeof(mod->klp_info->hdr); | ||
1994 | memcpy(&mod->klp_info->hdr, info->hdr, size); | ||
1995 | |||
1996 | /* Elf section header table */ | ||
1997 | size = sizeof(*info->sechdrs) * info->hdr->e_shnum; | ||
1998 | mod->klp_info->sechdrs = kmalloc(size, GFP_KERNEL); | ||
1999 | if (mod->klp_info->sechdrs == NULL) { | ||
2000 | ret = -ENOMEM; | ||
2001 | goto free_info; | ||
2002 | } | ||
2003 | memcpy(mod->klp_info->sechdrs, info->sechdrs, size); | ||
2004 | |||
2005 | /* Elf section name string table */ | ||
2006 | size = info->sechdrs[info->hdr->e_shstrndx].sh_size; | ||
2007 | mod->klp_info->secstrings = kmalloc(size, GFP_KERNEL); | ||
2008 | if (mod->klp_info->secstrings == NULL) { | ||
2009 | ret = -ENOMEM; | ||
2010 | goto free_sechdrs; | ||
2011 | } | ||
2012 | memcpy(mod->klp_info->secstrings, info->secstrings, size); | ||
2013 | |||
2014 | /* Elf symbol section index */ | ||
2015 | symndx = info->index.sym; | ||
2016 | mod->klp_info->symndx = symndx; | ||
2017 | |||
2018 | /* | ||
2019 | * For livepatch modules, core_kallsyms.symtab is a complete | ||
2020 | * copy of the original symbol table. Adjust sh_addr to point | ||
2021 | * to core_kallsyms.symtab since the copy of the symtab in module | ||
2022 | * init memory is freed at the end of do_init_module(). | ||
2023 | */ | ||
2024 | mod->klp_info->sechdrs[symndx].sh_addr = \ | ||
2025 | (unsigned long) mod->core_kallsyms.symtab; | ||
2026 | |||
2027 | return 0; | ||
2028 | |||
2029 | free_sechdrs: | ||
2030 | kfree(mod->klp_info->sechdrs); | ||
2031 | free_info: | ||
2032 | kfree(mod->klp_info); | ||
2033 | return ret; | ||
2034 | } | ||
2035 | |||
2036 | static void free_module_elf(struct module *mod) | ||
2037 | { | ||
2038 | kfree(mod->klp_info->sechdrs); | ||
2039 | kfree(mod->klp_info->secstrings); | ||
2040 | kfree(mod->klp_info); | ||
2041 | } | ||
2042 | #else /* !CONFIG_LIVEPATCH */ | ||
2043 | static int copy_module_elf(struct module *mod, struct load_info *info) | ||
2044 | { | ||
2045 | return 0; | ||
2046 | } | ||
2047 | |||
2048 | static void free_module_elf(struct module *mod) | ||
2049 | { | ||
2050 | } | ||
2051 | #endif /* CONFIG_LIVEPATCH */ | ||
2052 | |||
1976 | void __weak module_memfree(void *module_region) | 2053 | void __weak module_memfree(void *module_region) |
1977 | { | 2054 | { |
1978 | vfree(module_region); | 2055 | vfree(module_region); |
@@ -2011,6 +2088,9 @@ static void free_module(struct module *mod) | |||
2011 | /* Free any allocated parameters. */ | 2088 | /* Free any allocated parameters. */ |
2012 | destroy_params(mod->kp, mod->num_kp); | 2089 | destroy_params(mod->kp, mod->num_kp); |
2013 | 2090 | ||
2091 | if (is_livepatch_module(mod)) | ||
2092 | free_module_elf(mod); | ||
2093 | |||
2014 | /* Now we can delete it from the lists */ | 2094 | /* Now we can delete it from the lists */ |
2015 | mutex_lock(&module_mutex); | 2095 | mutex_lock(&module_mutex); |
2016 | /* Unlink carefully: kallsyms could be walking list. */ | 2096 | /* Unlink carefully: kallsyms could be walking list. */ |
@@ -2126,6 +2206,10 @@ static int simplify_symbols(struct module *mod, const struct load_info *info) | |||
2126 | (long)sym[i].st_value); | 2206 | (long)sym[i].st_value); |
2127 | break; | 2207 | break; |
2128 | 2208 | ||
2209 | case SHN_LIVEPATCH: | ||
2210 | /* Livepatch symbols are resolved by livepatch */ | ||
2211 | break; | ||
2212 | |||
2129 | case SHN_UNDEF: | 2213 | case SHN_UNDEF: |
2130 | ksym = resolve_symbol_wait(mod, info, name); | 2214 | ksym = resolve_symbol_wait(mod, info, name); |
2131 | /* Ok if resolved. */ | 2215 | /* Ok if resolved. */ |
@@ -2174,6 +2258,10 @@ static int apply_relocations(struct module *mod, const struct load_info *info) | |||
2174 | if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC)) | 2258 | if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC)) |
2175 | continue; | 2259 | continue; |
2176 | 2260 | ||
2261 | /* Livepatch relocation sections are applied by livepatch */ | ||
2262 | if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) | ||
2263 | continue; | ||
2264 | |||
2177 | if (info->sechdrs[i].sh_type == SHT_REL) | 2265 | if (info->sechdrs[i].sh_type == SHT_REL) |
2178 | err = apply_relocate(info->sechdrs, info->strtab, | 2266 | err = apply_relocate(info->sechdrs, info->strtab, |
2179 | info->index.sym, i, mod); | 2267 | info->index.sym, i, mod); |
@@ -2469,7 +2557,7 @@ static void layout_symtab(struct module *mod, struct load_info *info) | |||
2469 | 2557 | ||
2470 | /* Compute total space required for the core symbols' strtab. */ | 2558 | /* Compute total space required for the core symbols' strtab. */ |
2471 | for (ndst = i = 0; i < nsrc; i++) { | 2559 | for (ndst = i = 0; i < nsrc; i++) { |
2472 | if (i == 0 || | 2560 | if (i == 0 || is_livepatch_module(mod) || |
2473 | is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, | 2561 | is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, |
2474 | info->index.pcpu)) { | 2562 | info->index.pcpu)) { |
2475 | strtab_size += strlen(&info->strtab[src[i].st_name])+1; | 2563 | 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) | |||
2528 | mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs; | 2616 | mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs; |
2529 | src = mod->kallsyms->symtab; | 2617 | src = mod->kallsyms->symtab; |
2530 | for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) { | 2618 | for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) { |
2531 | if (i == 0 || | 2619 | if (i == 0 || is_livepatch_module(mod) || |
2532 | is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, | 2620 | is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, |
2533 | info->index.pcpu)) { | 2621 | info->index.pcpu)) { |
2534 | dst[ndst] = src[i]; | 2622 | dst[ndst] = src[i]; |
@@ -2667,6 +2755,26 @@ static int copy_chunked_from_user(void *dst, const void __user *usrc, unsigned l | |||
2667 | return 0; | 2755 | return 0; |
2668 | } | 2756 | } |
2669 | 2757 | ||
2758 | #ifdef CONFIG_LIVEPATCH | ||
2759 | static int find_livepatch_modinfo(struct module *mod, struct load_info *info) | ||
2760 | { | ||
2761 | mod->klp = get_modinfo(info, "livepatch") ? true : false; | ||
2762 | |||
2763 | return 0; | ||
2764 | } | ||
2765 | #else /* !CONFIG_LIVEPATCH */ | ||
2766 | static int find_livepatch_modinfo(struct module *mod, struct load_info *info) | ||
2767 | { | ||
2768 | if (get_modinfo(info, "livepatch")) { | ||
2769 | pr_err("%s: module is marked as livepatch module, but livepatch support is disabled", | ||
2770 | mod->name); | ||
2771 | return -ENOEXEC; | ||
2772 | } | ||
2773 | |||
2774 | return 0; | ||
2775 | } | ||
2776 | #endif /* CONFIG_LIVEPATCH */ | ||
2777 | |||
2670 | /* Sets info->hdr and info->len. */ | 2778 | /* Sets info->hdr and info->len. */ |
2671 | static int copy_module_from_user(const void __user *umod, unsigned long len, | 2779 | static int copy_module_from_user(const void __user *umod, unsigned long len, |
2672 | struct load_info *info) | 2780 | struct load_info *info) |
@@ -2821,6 +2929,10 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags) | |||
2821 | "is unknown, you have been warned.\n", mod->name); | 2929 | "is unknown, you have been warned.\n", mod->name); |
2822 | } | 2930 | } |
2823 | 2931 | ||
2932 | err = find_livepatch_modinfo(mod, info); | ||
2933 | if (err) | ||
2934 | return err; | ||
2935 | |||
2824 | /* Set up license info based on the info section */ | 2936 | /* Set up license info based on the info section */ |
2825 | set_license(mod, get_modinfo(info, "license")); | 2937 | set_license(mod, get_modinfo(info, "license")); |
2826 | 2938 | ||
@@ -3494,6 +3606,12 @@ static int load_module(struct load_info *info, const char __user *uargs, | |||
3494 | if (err < 0) | 3606 | if (err < 0) |
3495 | goto coming_cleanup; | 3607 | goto coming_cleanup; |
3496 | 3608 | ||
3609 | if (is_livepatch_module(mod)) { | ||
3610 | err = copy_module_elf(mod, info); | ||
3611 | if (err < 0) | ||
3612 | goto sysfs_cleanup; | ||
3613 | } | ||
3614 | |||
3497 | /* Get rid of temporary copy. */ | 3615 | /* Get rid of temporary copy. */ |
3498 | free_copy(info); | 3616 | free_copy(info); |
3499 | 3617 | ||
@@ -3502,11 +3620,12 @@ static int load_module(struct load_info *info, const char __user *uargs, | |||
3502 | 3620 | ||
3503 | return do_init_module(mod); | 3621 | return do_init_module(mod); |
3504 | 3622 | ||
3623 | sysfs_cleanup: | ||
3624 | mod_sysfs_teardown(mod); | ||
3505 | coming_cleanup: | 3625 | coming_cleanup: |
3506 | blocking_notifier_call_chain(&module_notify_list, | 3626 | blocking_notifier_call_chain(&module_notify_list, |
3507 | MODULE_STATE_GOING, mod); | 3627 | MODULE_STATE_GOING, mod); |
3508 | klp_module_going(mod); | 3628 | klp_module_going(mod); |
3509 | |||
3510 | bug_cleanup: | 3629 | bug_cleanup: |
3511 | /* module_bug_cleanup needs module_mutex protection */ | 3630 | /* module_bug_cleanup needs module_mutex protection */ |
3512 | mutex_lock(&module_mutex); | 3631 | mutex_lock(&module_mutex); |