History

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.

Josh Poimboeuf committed 6 years ago

Commit 7c8e2bd1 parent dcf550e3 files · +106 −55Browse files

Unified · Split

3 files
  1. include/linux/livepatch.h +14 −0
  2. kernel/livepatch/core.c +86 −51
  3. kernel/module.c +6 −4
include/linux/livepatch.h+14−0
@@ -234,6 +234,11 @@ void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor); · 233 unmodified lines
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; } · 2 unmodified lines
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+86−51
@@ -191,12 +191,12 @@ static int klp_find_object_symbol(const char *objname, const char *name, · 190 unmodified lines
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) · 13 unmodified lines
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) · 23 unmodified lines
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, · 430 unmodified lines
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, · 2 unmodified lines
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+6−4
@@ -2334,11 +2334,13 @@ static int apply_relocations(struct module *mod, const struct load_info *info) · 2333 unmodified lines
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)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t