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

livepatch: Cleanup module page permission changes

Calling set_memory_rw() and set_memory_ro() for every iteration of the
loop in klp_write_object_relocations() is messy, inefficient, and
error-prone.

Change all the read-only pages to read-write before the loop and convert
them back to read-only again afterwards.

Suggested-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Josh Poimboeuf and committed by
Jiri Kosina
b56b36ee fc284d63

+13 -28
+2 -23
arch/x86/kernel/livepatch.c
··· 20 20 21 21 #include <linux/module.h> 22 22 #include <linux/uaccess.h> 23 - #include <asm/cacheflush.h> 24 - #include <asm/page_types.h> 25 23 #include <asm/elf.h> 26 24 #include <asm/livepatch.h> 27 25 ··· 36 38 int klp_write_module_reloc(struct module *mod, unsigned long type, 37 39 unsigned long loc, unsigned long value) 38 40 { 39 - int ret, numpages, size = 4; 40 - bool readonly; 41 + size_t size = 4; 41 42 unsigned long val; 42 43 unsigned long core = (unsigned long)mod->core_layout.base; 43 44 unsigned long core_size = mod->core_layout.size; ··· 66 69 /* loc does not point to any symbol inside the module */ 67 70 return -EINVAL; 68 71 69 - readonly = false; 70 - 71 - #ifdef CONFIG_DEBUG_SET_MODULE_RONX 72 - if (loc < core + mod->core_layout.ro_size) 73 - readonly = true; 74 - #endif 75 - 76 - /* determine if the relocation spans a page boundary */ 77 - numpages = ((loc & PAGE_MASK) == ((loc + size) & PAGE_MASK)) ? 1 : 2; 78 - 79 - if (readonly) 80 - set_memory_rw(loc & PAGE_MASK, numpages); 81 - 82 - ret = probe_kernel_write((void *)loc, &val, size); 83 - 84 - if (readonly) 85 - set_memory_ro(loc & PAGE_MASK, numpages); 86 - 87 - return ret; 72 + return probe_kernel_write((void *)loc, &val, size); 88 73 }
+11 -5
kernel/livepatch/core.c
··· 28 28 #include <linux/list.h> 29 29 #include <linux/kallsyms.h> 30 30 #include <linux/livepatch.h> 31 + #include <asm/cacheflush.h> 31 32 32 33 /** 33 34 * struct klp_ops - structure for tracking registered ftrace ops structs ··· 233 232 static int klp_write_object_relocations(struct module *pmod, 234 233 struct klp_object *obj) 235 234 { 236 - int ret; 235 + int ret = 0; 237 236 unsigned long val; 238 237 struct klp_reloc *reloc; 239 238 ··· 243 242 if (WARN_ON(!obj->relocs)) 244 243 return -EINVAL; 245 244 245 + module_disable_ro(pmod); 246 + 246 247 for (reloc = obj->relocs; reloc->name; reloc++) { 247 248 /* discover the address of the referenced symbol */ 248 249 if (reloc->external) { 249 250 if (reloc->sympos > 0) { 250 251 pr_err("non-zero sympos for external reloc symbol '%s' is not supported\n", 251 252 reloc->name); 252 - return -EINVAL; 253 + ret = -EINVAL; 254 + goto out; 253 255 } 254 256 ret = klp_find_external_symbol(pmod, reloc->name, &val); 255 257 } else ··· 261 257 reloc->sympos, 262 258 &val); 263 259 if (ret) 264 - return ret; 260 + goto out; 265 261 266 262 ret = klp_write_module_reloc(pmod, reloc->type, reloc->loc, 267 263 val + reloc->addend); 268 264 if (ret) { 269 265 pr_err("relocation failed for symbol '%s' at 0x%016lx (%d)\n", 270 266 reloc->name, val, ret); 271 - return ret; 267 + goto out; 272 268 } 273 269 } 274 270 275 - return 0; 271 + out: 272 + module_enable_ro(pmod); 273 + return ret; 276 274 } 277 275 278 276 static void notrace klp_ftrace_handler(unsigned long ip,