Skip to content

Commit 7c8e2bd

Browse files
jpoimboeJiri Kosina
authored andcommitted
livepatch: Apply vmlinux-specific KLP relocations early
KLP relocations are livepatch-specific relocations which are applied to a KLP module's text or data. They exist for two reasons: 1) Unexported symbols: replacement functions often need to access unexported symbols (e.g. static functions), which "normal" relocations don't allow. 2) Late module patching: this is the ability for a KLP module to bypass normal module dependencies, such that the KLP module can be loaded *before* a to-be-patched module. This means that relocations which need to access symbols in the to-be-patched module might need to be applied to the KLP module well after it has been loaded. Non-late-patched KLP relocations are applied from the KLP module's init function. That usually works fine, unless the patched code wants to use alternatives, paravirt patching, jump tables, or some other special section which needs relocations. Then we run into ordering issues and crashes. In order for those special sections to work properly, the KLP relocations should be applied *before* the special section init code runs, such as apply_paravirt(), apply_alternatives(), or jump_label_apply_nops(). You might think the obvious solution would be to move the KLP relocation initialization earlier, but it's not necessarily that simple. The problem is the above-mentioned late module patching, for which KLP relocations can get applied well after the KLP module is loaded. To "fix" this issue in the past, we created .klp.arch sections: .klp.arch.{module}..altinstructions .klp.arch.{module}..parainstructions Those sections allow KLP late module patching code to call apply_paravirt() and apply_alternatives() after the module-specific KLP relocations (.klp.rela.{module}.{section}) have been applied. But that has a lot of drawbacks, including code complexity, the need for arch-specific code, and the (per-arch) danger that we missed some special section -- for example the __jump_table section which is used for jump labels. It turns out there's a simpler and more functional approach. There are two kinds of KLP relocation sections: 1) vmlinux-specific KLP relocation sections .klp.rela.vmlinux.{sec} These are relocations (applied to the KLP module) which reference unexported vmlinux symbols. 2) module-specific KLP relocation sections .klp.rela.{module}.{sec}: These are relocations (applied to the KLP module) which reference unexported or exported module symbols. Up until now, these have been treated the same. However, they're inherently different. Because of late module patching, module-specific KLP relocations can be applied very late, thus they can create the ordering headaches described above. But vmlinux-specific KLP relocations don't have that problem. There's nothing to prevent them from being applied earlier. So apply them at the same time as normal relocations, when the KLP module is being loaded. This means that for vmlinux-specific KLP relocations, we no longer have any ordering issues. vmlinux-referencing jump labels, alternatives, and paravirt patching will work automatically, without the need for the .klp.arch hacks. All that said, for module-specific KLP relocations, the ordering problems still exist and we *do* still need .klp.arch. Or do we? Stay tuned. Suggested-by: Peter Zijlstra <[email protected]> Signed-off-by: Josh Poimboeuf <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]> Acked-by: Joe Lawrence <[email protected]> Acked-by: Miroslav Benes <[email protected]> Acked-by: Jessica Yu <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent dcf550e commit 7c8e2bd

File tree

3 files changed

+106
-55
lines changed

3 files changed

+106
-55
lines changed

include/linux/livepatch.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@ void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor);
234234
struct klp_state *klp_get_state(struct klp_patch *patch, unsigned long id);
235235
struct klp_state *klp_get_prev_state(unsigned long id);
236236

237+
int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
238+
const char *shstrtab, const char *strtab,
239+
unsigned int symindex, unsigned int secindex,
240+
const char *objname);
241+
237242
#else /* !CONFIG_LIVEPATCH */
238243

239244
static inline int klp_module_coming(struct module *mod) { return 0; }
@@ -242,6 +247,15 @@ static inline bool klp_patch_pending(struct task_struct *task) { return false; }
242247
static inline void klp_update_patch_state(struct task_struct *task) {}
243248
static inline void klp_copy_process(struct task_struct *child) {}
244249

250+
static inline
251+
int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
252+
const char *shstrtab, const char *strtab,
253+
unsigned int symindex, unsigned int secindex,
254+
const char *objname)
255+
{
256+
return 0;
257+
}
258+
245259
#endif /* CONFIG_LIVEPATCH */
246260

247261
#endif /* _LINUX_LIVEPATCH_H_ */

kernel/livepatch/core.c

Lines changed: 86 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,12 @@ static int klp_find_object_symbol(const char *objname, const char *name,
191191
return -EINVAL;
192192
}
193193

194-
static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod)
194+
static int klp_resolve_symbols(Elf64_Shdr *sechdrs, const char *strtab,
195+
unsigned int symndx, Elf_Shdr *relasec)
195196
{
196197
int i, cnt, vmlinux, ret;
197198
char objname[MODULE_NAME_LEN];
198199
char symname[KSYM_NAME_LEN];
199-
char *strtab = pmod->core_kallsyms.strtab;
200200
Elf_Rela *relas;
201201
Elf_Sym *sym;
202202
unsigned long sympos, addr;
@@ -216,7 +216,7 @@ static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod)
216216
relas = (Elf_Rela *) relasec->sh_addr;
217217
/* For each rela in this klp relocation section */
218218
for (i = 0; i < relasec->sh_size / sizeof(Elf_Rela); i++) {
219-
sym = pmod->core_kallsyms.symtab + ELF_R_SYM(relas[i].r_info);
219+
sym = (Elf64_Sym *)sechdrs[symndx].sh_addr + ELF_R_SYM(relas[i].r_info);
220220
if (sym->st_shndx != SHN_LIVEPATCH) {
221221
pr_err("symbol %s is not marked as a livepatch symbol\n",
222222
strtab + sym->st_name);
@@ -246,54 +246,59 @@ static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod)
246246
return 0;
247247
}
248248

249-
static int klp_write_object_relocations(struct module *pmod,
250-
struct klp_object *obj)
249+
/*
250+
* At a high-level, there are two types of klp relocation sections: those which
251+
* reference symbols which live in vmlinux; and those which reference symbols
252+
* which live in other modules. This function is called for both types:
253+
*
254+
* 1) When a klp module itself loads, the module code calls this function to
255+
* write vmlinux-specific klp relocations (.klp.rela.vmlinux.* sections).
256+
* These relocations are written to the klp module text to allow the patched
257+
* code/data to reference unexported vmlinux symbols. They're written as
258+
* early as possible to ensure that other module init code (.e.g.,
259+
* jump_label_apply_nops) can access any unexported vmlinux symbols which
260+
* might be referenced by the klp module's special sections.
261+
*
262+
* 2) When a to-be-patched module loads -- or is already loaded when a
263+
* corresponding klp module loads -- klp code calls this function to write
264+
* module-specific klp relocations (.klp.rela.{module}.* sections). These
265+
* are written to the klp module text to allow the patched code/data to
266+
* reference symbols which live in the to-be-patched module or one of its
267+
* module dependencies. Exported symbols are supported, in addition to
268+
* unexported symbols, in order to enable late module patching, which allows
269+
* the to-be-patched module to be loaded and patched sometime *after* the
270+
* klp module is loaded.
271+
*/
272+
int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
273+
const char *shstrtab, const char *strtab,
274+
unsigned int symndx, unsigned int secndx,
275+
const char *objname)
251276
{
252-
int i, cnt, ret = 0;
253-
const char *objname, *secname;
277+
int cnt, ret;
254278
char sec_objname[MODULE_NAME_LEN];
255-
Elf_Shdr *sec;
279+
Elf_Shdr *sec = sechdrs + secndx;
256280

257-
if (WARN_ON(!klp_is_object_loaded(obj)))
281+
/*
282+
* Format: .klp.rela.sec_objname.section_name
283+
* See comment in klp_resolve_symbols() for an explanation
284+
* of the selected field width value.
285+
*/
286+
cnt = sscanf(shstrtab + sec->sh_name, ".klp.rela.%55[^.]",
287+
sec_objname);
288+
if (cnt != 1) {
289+
pr_err("section %s has an incorrectly formatted name\n",
290+
shstrtab + sec->sh_name);
258291
return -EINVAL;
292+
}
259293

260-
objname = klp_is_module(obj) ? obj->name : "vmlinux";
261-
262-
/* For each klp relocation section */
263-
for (i = 1; i < pmod->klp_info->hdr.e_shnum; i++) {
264-
sec = pmod->klp_info->sechdrs + i;
265-
secname = pmod->klp_info->secstrings + sec->sh_name;
266-
if (!(sec->sh_flags & SHF_RELA_LIVEPATCH))
267-
continue;
268-
269-
/*
270-
* Format: .klp.rela.sec_objname.section_name
271-
* See comment in klp_resolve_symbols() for an explanation
272-
* of the selected field width value.
273-
*/
274-
cnt = sscanf(secname, ".klp.rela.%55[^.]", sec_objname);
275-
if (cnt != 1) {
276-
pr_err("section %s has an incorrectly formatted name\n",
277-
secname);
278-
ret = -EINVAL;
279-
break;
280-
}
281-
282-
if (strcmp(objname, sec_objname))
283-
continue;
284-
285-
ret = klp_resolve_symbols(sec, pmod);
286-
if (ret)
287-
break;
294+
if (strcmp(objname ? objname : "vmlinux", sec_objname))
295+
return 0;
288296

289-
ret = apply_relocate_add(pmod->klp_info->sechdrs,
290-
pmod->core_kallsyms.strtab,
291-
pmod->klp_info->symndx, i, pmod);
292-
if (ret)
293-
break;
294-
}
297+
ret = klp_resolve_symbols(sechdrs, strtab, symndx, sec);
298+
if (ret)
299+
return ret;
295300

296-
return ret;
301+
return apply_relocate_add(sechdrs, strtab, symndx, secndx, pmod);
297302
}
298303

299304
/*
@@ -730,6 +735,28 @@ void __weak arch_klp_init_object_loaded(struct klp_patch *patch,
730735
{
731736
}
732737

738+
int klp_apply_object_relocs(struct klp_patch *patch, struct klp_object *obj)
739+
{
740+
int i, ret;
741+
struct klp_modinfo *info = patch->mod->klp_info;
742+
743+
for (i = 1; i < info->hdr.e_shnum; i++) {
744+
Elf_Shdr *sec = info->sechdrs + i;
745+
746+
if (!(sec->sh_flags & SHF_RELA_LIVEPATCH))
747+
continue;
748+
749+
ret = klp_apply_section_relocs(patch->mod, info->sechdrs,
750+
info->secstrings,
751+
patch->mod->core_kallsyms.strtab,
752+
info->symndx, i, obj->name);
753+
if (ret)
754+
return ret;
755+
}
756+
757+
return 0;
758+
}
759+
733760
/* parts of the initialization that is done only when the object is loaded */
734761
static int klp_init_object_loaded(struct klp_patch *patch,
735762
struct klp_object *obj)
@@ -738,18 +765,26 @@ static int klp_init_object_loaded(struct klp_patch *patch,
738765
int ret;
739766

740767
mutex_lock(&text_mutex);
741-
742768
module_disable_ro(patch->mod);
743-
ret = klp_write_object_relocations(patch->mod, obj);
744-
if (ret) {
745-
module_enable_ro(patch->mod, true);
746-
mutex_unlock(&text_mutex);
747-
return ret;
769+
770+
if (klp_is_module(obj)) {
771+
/*
772+
* Only write module-specific relocations here
773+
* (.klp.rela.{module}.*). vmlinux-specific relocations were
774+
* written earlier during the initialization of the klp module
775+
* itself.
776+
*/
777+
ret = klp_apply_object_relocs(patch, obj);
778+
if (ret) {
779+
module_enable_ro(patch->mod, true);
780+
mutex_unlock(&text_mutex);
781+
return ret;
782+
}
748783
}
749784

750785
arch_klp_init_object_loaded(patch, obj);
751-
module_enable_ro(patch->mod, true);
752786

787+
module_enable_ro(patch->mod, true);
753788
mutex_unlock(&text_mutex);
754789

755790
klp_for_each_func(obj, func) {

kernel/module.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,11 +2334,13 @@ static int apply_relocations(struct module *mod, const struct load_info *info)
23342334
if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC))
23352335
continue;
23362336

2337-
/* Livepatch relocation sections are applied by livepatch */
23382337
if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH)
2339-
continue;
2340-
2341-
if (info->sechdrs[i].sh_type == SHT_REL)
2338+
err = klp_apply_section_relocs(mod, info->sechdrs,
2339+
info->secstrings,
2340+
info->strtab,
2341+
info->index.sym, i,
2342+
NULL);
2343+
else if (info->sechdrs[i].sh_type == SHT_REL)
23422344
err = apply_relocate(info->sechdrs, info->strtab,
23432345
info->index.sym, i, mod);
23442346
else if (info->sechdrs[i].sh_type == SHT_RELA)

0 commit comments

Comments
 (0)