On Fri, Oct 25, 2019 at 12:06:12PM +0200, Peter Zijlstra wrote: > On Fri, Oct 25, 2019 at 10:43:00AM +0200, Peter Zijlstra wrote: > > > But none of that explains why apply_alternatives() is also delayed. > > > > So I'm very tempted to just revert that patchset for doing it all > > wrong. > > And I've done just that. This includes Josh's validation patch, the > revert and my klp_appy_relocations_add() patches with the removal of > module_disable_ro(). > > Josh, can you test or give me clue on how to test? I need to run a few > errands today, but I'll try and have a poke either tonight or tomorrow. > > git://git.kernel.org/pub/scm/linux/kernel/git/peterz/queue.git x86/rwx I looked at this today. A few potential tweaks: - The new klp_apply_relocate_add() interface isn't needed. Instead apply_relocate_add() can use the module state to decide whether to text_poke(). - For robustness I think we need to apply vmlinux-specific klp relocations at the same time as normal relocations. Rough untested changes below. I still need to finish changing kpatch-build and then I'll need to do a LOT of testing. I can take over the livepatch-specific patches if you want. Or however you want to do it. diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index 7fc519b9b4e0..6a70213854f0 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c @@ -451,14 +451,11 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) { - return __apply_relocate_add(sechdrs, strtab, symindex, relsec, me, memwrite); -} + int ret; + bool early = me->state != MODULE_STATE_LIVE; -int klp_apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, - unsigned int symindex, unsigned int relsec, - struct module *me) -{ - return __apply_relocate_add(sechdrs, strtab, symindex, relsec, me, s390_kernel_write); + return __apply_relocate_add(sechdrs, strtab, symindex, relsec, me, + early ? memwrite : s390_kernel_write); } int module_finalize(const Elf_Ehdr *hdr, diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c index 5eee618a98c5..30174798ff79 100644 --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c @@ -222,20 +222,14 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, unsigned int symindex, unsigned int relsec, struct module *me) -{ - return __apply_relocate_add(sechdrs, strtab, symindex, relsec, me, memcpy); -} - -int klp_apply_relocate_add(Elf64_Shdr *sechdrs, - const char *strtab, - unsigned int symindex, - unsigned int relsec, - struct module *me) { int ret; + bool early = me->state != MODULE_STATE_LIVE; + + ret = __apply_relocate_add(sechdrs, strtab, symindex, relsec, me, + early ? memcpy : text_poke); - ret = __apply_relocate_add(sechdrs, strtab, symindex, relsec, me, text_poke); - if (!ret) + if (!ret && !early) text_poke_sync(); return ret; diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index cc18f945bdb2..b00170696db2 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -214,12 +214,7 @@ void *klp_shadow_get_or_alloc(void *obj, unsigned long id, void klp_shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor); void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor); - -extern int klp_apply_relocate_add(Elf64_Shdr *sechdrs, - const char *strtab, - unsigned int symindex, - unsigned int relsec, - struct module *me); +int klp_write_relocations(struct module *mod, struct klp_object *obj); #else /* !CONFIG_LIVEPATCH */ @@ -229,6 +224,12 @@ static inline bool klp_patch_pending(struct task_struct *task) { return false; } static inline void klp_update_patch_state(struct task_struct *task) {} static inline void klp_copy_process(struct task_struct *child) {} +static inline int klp_write_relocations(struct module *mod, + struct klp_object *obj) +{ + return 0; +} + #endif /* CONFIG_LIVEPATCH */ #endif /* _LINUX_LIVEPATCH_H_ */ diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 30395302a273..52eb91d0ee8d 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -256,27 +256,60 @@ static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod) return 0; } -int __weak klp_apply_relocate_add(Elf64_Shdr *sechdrs, - const char *strtab, - unsigned int symindex, - unsigned int relsec, - struct module *me) -{ - return apply_relocate_add(sechdrs, strtab, symindex, relsec, me); -} - -static int klp_write_object_relocations(struct module *pmod, - struct klp_object *obj) +/* + * This function is called for both vmlinux-specific and module-specific klp + * relocation sections: + * + * 1) When the klp module itself loads, the module code calls this function + * to write vmlinux-specific klp relocations. These relocations allow the + * patched code/data to reference unexported vmlinux symbols. They're + * written as early as possible to ensure that other module init code + * (.e.g., jump_label_apply_nops) can access any non-exported vmlinux + * symbols which might be referenced by the klp module's special sections. + * + * 2) When a to-be-patched module loads (or is already loaded when the + * klp module loads), klp code calls this function to write klp relocations + * which are specific to the module. These relocations allow the patched + * code/data to reference module symbols, both unexported and exported. + * They also enable late module patching, which means the to-be-patched + * module may be loaded *after* the klp module. + * + * The following restrictions apply to module-specific relocation sections: + * + * a) References to vmlinux symbols are not allowed. Otherwise there might + * be module init ordering issues, and crashes might occur in some of the + * other kernel patching components like paravirt patching or jump + * labels. All references to vmlinux symbols should use either normal + * relas (for exported symbols) or vmlinux-specific klp relas (for + * unexported symbols). This restriction is enforced in + * klp_resolve_symbols(). + * + * b) Relocations to special sections like __jump_table and .altinstructions + * aren't allowed. In other words, there should never be a + * .klp.rela.{module}.__jump_table section. This will definitely cause + * initialization ordering issues, as such special sections are processed + * during the loading of the klp module itself, *not* the to-be-patched + * module. This means that e.g., it's not currently possible to patch a + * module function which uses a static key jump label, if you want to + * have the replacement function also use the same static key. In this + * case, a non-static interface like static_key_enabled() can be used in + * the new function instead. + * + * On the other hand, a .klp.rela.vmlinux.__jump_table section is fine, + * as it can be resolved early enough during the load of the klp module, + * as described above. + */ +int klp_write_relocations(struct module *pmod, struct klp_object *obj) { int i, cnt, ret = 0; const char *objname, *secname; char sec_objname[MODULE_NAME_LEN]; Elf_Shdr *sec; - if (WARN_ON(!klp_is_object_loaded(obj))) + if (WARN_ON(obj && !klp_is_object_loaded(obj))) return -EINVAL; - objname = klp_is_module(obj) ? obj->name : "vmlinux"; + objname = obj ? obj->name : "vmlinux"; /* For each klp relocation section */ for (i = 1; i < pmod->klp_info->hdr.e_shnum; i++) { @@ -305,7 +338,7 @@ static int klp_write_object_relocations(struct module *pmod, if (ret) break; - ret = klp_apply_relocate_add(pmod->klp_info->sechdrs, + ret = apply_relocate_add(pmod->klp_info->sechdrs, pmod->core_kallsyms.strtab, pmod->klp_info->symndx, i, pmod); if (ret) @@ -733,16 +766,25 @@ static int klp_init_object_loaded(struct klp_patch *patch, struct klp_func *func; int ret; - mutex_lock(&text_mutex); + if (klp_is_module(obj)) { + + /* + * Only write module-specific relocations here. + * vmlinux-specific relocations were already written during the + * loading of the klp module. + */ + + mutex_lock(&text_mutex); + + ret = klp_write_relocations(patch->mod, obj); + if (ret) { + mutex_unlock(&text_mutex); + return ret; + } - ret = klp_write_object_relocations(patch->mod, obj); - if (ret) { mutex_unlock(&text_mutex); - return ret; } - mutex_unlock(&text_mutex); - klp_for_each_func(obj, func) { ret = klp_find_object_symbol(obj->name, func->old_name, func->old_sympos, diff --git a/kernel/module.c b/kernel/module.c index fe5bd382759c..ff4347385f05 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2327,11 +2327,9 @@ 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 = klp_write_relocations(mod, NULL); + else if (info->sechdrs[i].sh_type == SHT_REL) err = apply_relocate(info->sechdrs, info->strtab, info->index.sym, i, mod); else if (info->sechdrs[i].sh_type == SHT_RELA) @@ -3812,18 +3810,24 @@ static int load_module(struct load_info *info, const char __user *uargs, /* Set up MODINFO_ATTR fields */ setup_modinfo(mod, info); + if (is_livepatch_module(mod)) { + err = copy_module_elf(mod, info); + if (err < 0) + goto free_modinfo; + } + /* Fix up syms, so that st_value is a pointer to location. */ err = simplify_symbols(mod, info); if (err < 0) - goto free_modinfo; + goto free_elf_copy; err = apply_relocations(mod, info); if (err < 0) - goto free_modinfo; + goto free_elf_copy; err = post_relocation(mod, info); if (err < 0) - goto free_modinfo; + goto free_elf_copy; flush_module_icache(mod); @@ -3866,12 +3870,6 @@ 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); @@ -3880,8 +3878,6 @@ 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: mod->state = MODULE_STATE_GOING; destroy_params(mod->kp, mod->num_kp); @@ -3901,6 +3897,8 @@ static int load_module(struct load_info *info, const char __user *uargs, kfree(mod->args); free_arch_cleanup: module_arch_cleanup(mod); + free_elf_copy: + free_module_elf(mod); free_modinfo: free_modinfo(mod); free_unload: