On Mon, Apr 20, 2020 at 03:01:41PM -0400, Joe Lawrence wrote: > > > ... apply_relocations() is also iterating over the section headers (the > > > diff context doesn't show it here, but i is an incrementing index over > > > sechdrs[]). > > > > > > So if there is more than one KLP relocation section, we'll process them > > > multiple times. At least the x86 relocation code will detect this and > > > fail the module load with an invalid relocation (existing value not > > > zero). > > > > Ah, yes, good catch! > > > > The same test case passed with a small modification to push the foreach > KLP section part to a kernel/livepatch/core.c local function and > exposing the klp_resolve_symbols() + apply_relocate_add() for a given > section to kernel/module.c. Something like following... I came up with something very similar, though I named them klp_apply_object_relocs() and klp_apply_section_relocs() and changed the argument order a bit (module first). Since it sounds like you have a test, could you try this one? diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 533359e48c39..fb1a3de39726 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -231,10 +231,10 @@ void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor); struct klp_state *klp_get_state(struct klp_patch *patch, unsigned long id); struct klp_state *klp_get_prev_state(unsigned long id); -int klp_write_relocations(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, - const char *shstrtab, const char *strtab, - unsigned int symindex, struct module *pmod, - const char *objname); +int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, + const char *shstrtab, const char *strtab, + unsigned int symindex, unsigned int secindex, + const char *objname); #else /* !CONFIG_LIVEPATCH */ @@ -245,10 +245,10 @@ 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(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, - const char *shstrtab, const char *strtab, - unsigned int symindex, struct module *pmod, - const char *objname) +int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, + const char *shstrtab, const char *strtab, + unsigned int symindex, unsigned int secindex, + const char *objname); { return 0; } diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 3ff886b911ae..89c5cb962c54 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -285,49 +285,37 @@ static int klp_resolve_symbols(Elf64_Shdr *sechdrs, const char *strtab, * the to-be-patched module to be loaded and patched sometime *after* the * klp module is loaded. */ -int klp_write_relocations(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, - const char *shstrtab, const char *strtab, - unsigned int symndx, struct module *pmod, - const char *objname) +int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, + const char *shstrtab, const char *strtab, + unsigned int symndx, unsigned int secndx, + const char *objname) { - int i, cnt, ret = 0; + int cnt, ret; char sec_objname[MODULE_NAME_LEN]; - Elf_Shdr *sec; + Elf_Shdr *sec = sechdrs + secndx; - /* For each klp relocation section */ - for (i = 1; i < ehdr->e_shnum; i++) { - sec = sechdrs + i; - if (!(sec->sh_flags & SHF_RELA_LIVEPATCH)) - continue; - - /* - * Format: .klp.rela.sec_objname.section_name - * See comment in klp_resolve_symbols() for an explanation - * of the selected field width value. - */ - cnt = sscanf(shstrtab + sec->sh_name, ".klp.rela.%55[^.]", - sec_objname); - if (cnt != 1) { - pr_err("section %s has an incorrectly formatted name\n", - shstrtab + sec->sh_name); - ret = -EINVAL; - break; - } - - if (strcmp(objname ? objname : "vmlinux", sec_objname)) - continue; + /* + * Format: .klp.rela.sec_objname.section_name + * See comment in klp_resolve_symbols() for an explanation + * of the selected field width value. + */ + cnt = sscanf(shstrtab + sec->sh_name, ".klp.rela.%55[^.]", + sec_objname); + if (cnt != 1) { + pr_err("section %s has an incorrectly formatted name\n", + shstrtab + sec->sh_name); + return -EINVAL; + } - ret = klp_resolve_symbols(sechdrs, strtab, symndx, sec, - sec_objname); - if (ret) - break; + if (strcmp(objname ? objname : "vmlinux", sec_objname)) + return 0; - ret = apply_relocate_add(sechdrs, strtab, symndx, i, pmod); - if (ret) - break; - } + ret = klp_resolve_symbols(sechdrs, strtab, symndx, sec, + sec_objname); + if (ret) + return ret; - return ret; + return apply_relocate_add(sechdrs, strtab, symndx, secndx, pmod); } /* @@ -758,13 +746,34 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func) func->old_sympos ? func->old_sympos : 1); } +int klp_apply_object_relocs(struct klp_patch *patch, struct klp_object *obj) +{ + int i, ret; + struct klp_modinfo *info = patch->mod->klp_info; + + for (i = 1; i < info->hdr.e_shnum; i++) { + Elf_Shdr *sec = info->sechdrs + i; + + if (!(sec->sh_flags & SHF_RELA_LIVEPATCH)) + continue; + + ret = klp_apply_section_relocs(patch->mod, info->sechdrs, + info->secstrings, + patch->mod->core_kallsyms.strtab, + info->symndx, i, obj->name); + if (ret) + return ret; + } + + return 0; +} + /* parts of the initialization that is done only when the object is loaded */ static int klp_init_object_loaded(struct klp_patch *patch, struct klp_object *obj) { struct klp_func *func; int ret; - struct klp_modinfo *info = patch->mod->klp_info; if (klp_is_module(obj)) { /* @@ -773,11 +782,7 @@ static int klp_init_object_loaded(struct klp_patch *patch, * written earlier during the initialization of the klp module * itself. */ - ret = klp_write_relocations(&info->hdr, info->sechdrs, - info->secstrings, - patch->mod->core_kallsyms.strtab, - info->symndx, patch->mod, - obj->name); + ret = klp_apply_object_relocs(patch, obj); if (ret) return ret; } diff --git a/kernel/module.c b/kernel/module.c index b1d30ad67e82..3ba024afe379 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2322,10 +2322,11 @@ static int apply_relocations(struct module *mod, const struct load_info *info) continue; if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) - err = klp_write_relocations(info->hdr, info->sechdrs, - info->secstrings, - info->strtab, - info->index.sym, mod, NULL); + err = klp_apply_section_relocs(mod, info->sechdrs, + info->secstrings, + info->strtab, + info->index.sym, i, + NULL); else if (info->sechdrs[i].sh_type == SHT_REL) err = apply_relocate(info->sechdrs, info->strtab, info->index.sym, i, mod);