Re: [PATCH v2 2/9] livepatch: Apply vmlinux-specific KLP relocations early

From: Josh Poimboeuf
Date: Mon Apr 20 2020 - 15:11:30 EST


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);