Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

livepatch: reuse module loader code to write relocations

Reuse module loader code to write relocations, thereby eliminating the need
for architecture specific relocation code in livepatch. Specifically, reuse
the apply_relocate_add() function in the module loader to write relocations
instead of duplicating functionality in livepatch's arch-dependent
klp_write_module_reloc() function.

In order to accomplish this, livepatch modules manage their own relocation
sections (marked with the SHF_RELA_LIVEPATCH section flag) and
livepatch-specific symbols (marked with SHN_LIVEPATCH symbol section
index). To apply livepatch relocation sections, livepatch symbols
referenced by relocs are resolved and then apply_relocate_add() is called
to apply those relocations.

In addition, remove x86 livepatch relocation code and the s390
klp_write_module_reloc() function stub. They are no longer needed since
relocation work has been offloaded to module loader.

Lastly, mark the module as a livepatch module so that the module loader
canappropriately identify and initialize it.

Signed-off-by: Jessica Yu <jeyu@redhat.com>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Acked-by: Heiko Carstens <heiko.carstens@de.ibm.com> # for s390 changes
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Jessica Yu and committed by
Jiri Kosina
425595a7 f31e0960

+96 -155
-7
arch/s390/include/asm/livepatch.h
··· 24 24 return 0; 25 25 } 26 26 27 - static inline int klp_write_module_reloc(struct module *mod, unsigned long 28 - type, unsigned long loc, unsigned long value) 29 - { 30 - /* not supported yet */ 31 - return -ENOSYS; 32 - } 33 - 34 27 static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) 35 28 { 36 29 regs->psw.addr = ip;
-2
arch/x86/include/asm/livepatch.h
··· 32 32 #endif 33 33 return 0; 34 34 } 35 - int klp_write_module_reloc(struct module *mod, unsigned long type, 36 - unsigned long loc, unsigned long value); 37 35 38 36 static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) 39 37 {
-1
arch/x86/kernel/Makefile
··· 67 67 obj-y += apic/ 68 68 obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups_32.o 69 69 obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o 70 - obj-$(CONFIG_LIVEPATCH) += livepatch.o 71 70 obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o 72 71 obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o 73 72 obj-$(CONFIG_X86_TSC) += trace_clock.o
-70
arch/x86/kernel/livepatch.c
··· 1 - /* 2 - * livepatch.c - x86-specific Kernel Live Patching Core 3 - * 4 - * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> 5 - * Copyright (C) 2014 SUSE 6 - * 7 - * This program is free software; you can redistribute it and/or 8 - * modify it under the terms of the GNU General Public License 9 - * as published by the Free Software Foundation; either version 2 10 - * of the License, or (at your option) any later version. 11 - * 12 - * This program is distributed in the hope that it will be useful, 13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 - * GNU General Public License for more details. 16 - * 17 - * You should have received a copy of the GNU General Public License 18 - * along with this program; if not, see <http://www.gnu.org/licenses/>. 19 - */ 20 - 21 - #include <linux/module.h> 22 - #include <linux/uaccess.h> 23 - #include <asm/elf.h> 24 - #include <asm/livepatch.h> 25 - 26 - /** 27 - * klp_write_module_reloc() - write a relocation in a module 28 - * @mod: module in which the section to be modified is found 29 - * @type: ELF relocation type (see asm/elf.h) 30 - * @loc: address that the relocation should be written to 31 - * @value: relocation value (sym address + addend) 32 - * 33 - * This function writes a relocation to the specified location for 34 - * a particular module. 35 - */ 36 - int klp_write_module_reloc(struct module *mod, unsigned long type, 37 - unsigned long loc, unsigned long value) 38 - { 39 - size_t size = 4; 40 - unsigned long val; 41 - unsigned long core = (unsigned long)mod->core_layout.base; 42 - unsigned long core_size = mod->core_layout.size; 43 - 44 - switch (type) { 45 - case R_X86_64_NONE: 46 - return 0; 47 - case R_X86_64_64: 48 - val = value; 49 - size = 8; 50 - break; 51 - case R_X86_64_32: 52 - val = (u32)value; 53 - break; 54 - case R_X86_64_32S: 55 - val = (s32)value; 56 - break; 57 - case R_X86_64_PC32: 58 - val = (u32)(value - loc); 59 - break; 60 - default: 61 - /* unsupported relocation type */ 62 - return -EINVAL; 63 - } 64 - 65 - if (loc < core || loc >= core + core_size) 66 - /* loc does not point to any symbol inside the module */ 67 - return -EINVAL; 68 - 69 - return probe_kernel_write((void *)loc, &val, size); 70 - }
-20
include/linux/livepatch.h
··· 65 65 }; 66 66 67 67 /** 68 - * struct klp_reloc - relocation structure for live patching 69 - * @loc: address where the relocation will be written 70 - * @sympos: position in kallsyms to disambiguate symbols (optional) 71 - * @type: ELF relocation type 72 - * @name: name of the referenced symbol (for lookup/verification) 73 - * @addend: offset from the referenced symbol 74 - * @external: symbol is either exported or within the live patch module itself 75 - */ 76 - struct klp_reloc { 77 - unsigned long loc; 78 - unsigned long sympos; 79 - unsigned long type; 80 - const char *name; 81 - int addend; 82 - int external; 83 - }; 84 - 85 - /** 86 68 * struct klp_object - kernel object structure for live patching 87 69 * @name: module name (or NULL for vmlinux) 88 - * @relocs: relocation entries to be applied at load time 89 70 * @funcs: function entries for functions to be patched in the object 90 71 * @kobj: kobject for sysfs resources 91 72 * @mod: kernel module associated with the patched object ··· 76 95 struct klp_object { 77 96 /* external */ 78 97 const char *name; 79 - struct klp_reloc *relocs; 80 98 struct klp_func *funcs; 81 99 82 100 /* internal */
+95 -55
kernel/livepatch/core.c
··· 28 28 #include <linux/list.h> 29 29 #include <linux/kallsyms.h> 30 30 #include <linux/livepatch.h> 31 + #include <linux/elf.h> 32 + #include <linux/moduleloader.h> 31 33 #include <asm/cacheflush.h> 32 34 33 35 /** ··· 206 204 return -EINVAL; 207 205 } 208 206 209 - /* 210 - * external symbols are located outside the parent object (where the parent 211 - * object is either vmlinux or the kmod being patched). 212 - */ 213 - static int klp_find_external_symbol(struct module *pmod, const char *name, 214 - unsigned long *addr) 207 + static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod) 215 208 { 216 - const struct kernel_symbol *sym; 217 - 218 - /* first, check if it's an exported symbol */ 219 - preempt_disable(); 220 - sym = find_symbol(name, NULL, NULL, true, true); 221 - if (sym) { 222 - *addr = sym->value; 223 - preempt_enable(); 224 - return 0; 225 - } 226 - preempt_enable(); 209 + int i, cnt, vmlinux, ret; 210 + char objname[MODULE_NAME_LEN]; 211 + char symname[KSYM_NAME_LEN]; 212 + char *strtab = pmod->core_kallsyms.strtab; 213 + Elf_Rela *relas; 214 + Elf_Sym *sym; 215 + unsigned long sympos, addr; 227 216 228 217 /* 229 - * Check if it's in another .o within the patch module. This also 230 - * checks that the external symbol is unique. 218 + * Since the field widths for objname and symname in the sscanf() 219 + * call are hard-coded and correspond to MODULE_NAME_LEN and 220 + * KSYM_NAME_LEN respectively, we must make sure that MODULE_NAME_LEN 221 + * and KSYM_NAME_LEN have the values we expect them to have. 222 + * 223 + * Because the value of MODULE_NAME_LEN can differ among architectures, 224 + * we use the smallest/strictest upper bound possible (56, based on 225 + * the current definition of MODULE_NAME_LEN) to prevent overflows. 231 226 */ 232 - return klp_find_object_symbol(pmod->name, name, 0, addr); 227 + BUILD_BUG_ON(MODULE_NAME_LEN < 56 || KSYM_NAME_LEN != 128); 228 + 229 + relas = (Elf_Rela *) relasec->sh_addr; 230 + /* For each rela in this klp relocation section */ 231 + for (i = 0; i < relasec->sh_size / sizeof(Elf_Rela); i++) { 232 + sym = pmod->core_kallsyms.symtab + ELF_R_SYM(relas[i].r_info); 233 + if (sym->st_shndx != SHN_LIVEPATCH) { 234 + pr_err("symbol %s is not marked as a livepatch symbol", 235 + strtab + sym->st_name); 236 + return -EINVAL; 237 + } 238 + 239 + /* Format: .klp.sym.objname.symname,sympos */ 240 + cnt = sscanf(strtab + sym->st_name, 241 + ".klp.sym.%55[^.].%127[^,],%lu", 242 + objname, symname, &sympos); 243 + if (cnt != 3) { 244 + pr_err("symbol %s has an incorrectly formatted name", 245 + strtab + sym->st_name); 246 + return -EINVAL; 247 + } 248 + 249 + /* klp_find_object_symbol() treats a NULL objname as vmlinux */ 250 + vmlinux = !strcmp(objname, "vmlinux"); 251 + ret = klp_find_object_symbol(vmlinux ? NULL : objname, 252 + symname, sympos, &addr); 253 + if (ret) 254 + return ret; 255 + 256 + sym->st_value = addr; 257 + } 258 + 259 + return 0; 233 260 } 234 261 235 262 static int klp_write_object_relocations(struct module *pmod, 236 263 struct klp_object *obj) 237 264 { 238 - int ret = 0; 239 - unsigned long val; 240 - struct klp_reloc *reloc; 265 + int i, cnt, ret = 0; 266 + const char *objname, *secname; 267 + char sec_objname[MODULE_NAME_LEN]; 268 + Elf_Shdr *sec; 241 269 242 270 if (WARN_ON(!klp_is_object_loaded(obj))) 243 271 return -EINVAL; 244 272 245 - if (WARN_ON(!obj->relocs)) 246 - return -EINVAL; 273 + objname = klp_is_module(obj) ? obj->name : "vmlinux"; 247 274 248 275 module_disable_ro(pmod); 276 + /* For each klp relocation section */ 277 + for (i = 1; i < pmod->klp_info->hdr.e_shnum; i++) { 278 + sec = pmod->klp_info->sechdrs + i; 279 + secname = pmod->klp_info->secstrings + sec->sh_name; 280 + if (!(sec->sh_flags & SHF_RELA_LIVEPATCH)) 281 + continue; 249 282 250 - for (reloc = obj->relocs; reloc->name; reloc++) { 251 - /* discover the address of the referenced symbol */ 252 - if (reloc->external) { 253 - if (reloc->sympos > 0) { 254 - pr_err("non-zero sympos for external reloc symbol '%s' is not supported\n", 255 - reloc->name); 256 - ret = -EINVAL; 257 - goto out; 258 - } 259 - ret = klp_find_external_symbol(pmod, reloc->name, &val); 260 - } else 261 - ret = klp_find_object_symbol(obj->name, 262 - reloc->name, 263 - reloc->sympos, 264 - &val); 265 - if (ret) 266 - goto out; 267 - 268 - ret = klp_write_module_reloc(pmod, reloc->type, reloc->loc, 269 - val + reloc->addend); 270 - if (ret) { 271 - pr_err("relocation failed for symbol '%s' at 0x%016lx (%d)\n", 272 - reloc->name, val, ret); 273 - goto out; 283 + /* 284 + * Format: .klp.rela.sec_objname.section_name 285 + * See comment in klp_resolve_symbols() for an explanation 286 + * of the selected field width value. 287 + */ 288 + cnt = sscanf(secname, ".klp.rela.%55[^.]", sec_objname); 289 + if (cnt != 1) { 290 + pr_err("section %s has an incorrectly formatted name", 291 + secname); 292 + ret = -EINVAL; 293 + break; 274 294 } 295 + 296 + if (strcmp(objname, sec_objname)) 297 + continue; 298 + 299 + ret = klp_resolve_symbols(sec, pmod); 300 + if (ret) 301 + break; 302 + 303 + ret = apply_relocate_add(pmod->klp_info->sechdrs, 304 + pmod->core_kallsyms.strtab, 305 + pmod->klp_info->symndx, i, pmod); 306 + if (ret) 307 + break; 275 308 } 276 309 277 - out: 278 310 module_enable_ro(pmod); 279 311 return ret; 280 312 } ··· 739 703 struct klp_func *func; 740 704 int ret; 741 705 742 - if (obj->relocs) { 743 - ret = klp_write_object_relocations(patch->mod, obj); 744 - if (ret) 745 - return ret; 746 - } 706 + ret = klp_write_object_relocations(patch->mod, obj); 707 + if (ret) 708 + return ret; 747 709 748 710 klp_for_each_func(obj, func) { 749 711 ret = klp_find_object_symbol(obj->name, func->old_name, ··· 875 841 int klp_register_patch(struct klp_patch *patch) 876 842 { 877 843 int ret; 844 + 845 + if (!is_livepatch_module(patch->mod)) { 846 + pr_err("module %s is not marked as a livepatch module", 847 + patch->mod->name); 848 + return -EINVAL; 849 + } 878 850 879 851 if (!klp_initialized()) 880 852 return -ENODEV;
+1
samples/livepatch/livepatch-sample.c
··· 89 89 module_init(livepatch_init); 90 90 module_exit(livepatch_exit); 91 91 MODULE_LICENSE("GPL"); 92 + MODULE_INFO(livepatch, "Y");