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

module: switch to execmem API for remapping as RW and restoring ROX

Instead of using writable copy for module text sections, temporarily remap
the memory allocated from execmem's ROX cache as writable and restore its
ROX permissions after the module is formed.

This will allow removing nasty games with writable copy in alternatives
patching on x86.

Signed-off-by: "Mike Rapoport (Microsoft)" <rppt@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20250126074733.1384926-7-rppt@kernel.org

authored by

Mike Rapoport (Microsoft) and committed by
Peter Zijlstra
c287c072 05e555b8

+27 -72
+1 -7
include/linux/module.h
··· 367 367 368 368 struct module_memory { 369 369 void *base; 370 - void *rw_copy; 371 370 bool is_rox; 372 371 unsigned int size; 373 372 ··· 768 769 769 770 void set_module_sig_enforced(void); 770 771 771 - void *__module_writable_address(struct module *mod, void *loc); 772 - 773 772 static inline void *module_writable_address(struct module *mod, void *loc) 774 773 { 775 - if (!IS_ENABLED(CONFIG_ARCH_HAS_EXECMEM_ROX) || !mod || 776 - mod->state != MODULE_STATE_UNFORMED) 777 - return loc; 778 - return __module_writable_address(mod, loc); 774 + return loc; 779 775 } 780 776 781 777 #else /* !CONFIG_MODULES... */
-4
include/linux/moduleloader.h
··· 108 108 const Elf_Shdr *sechdrs, 109 109 struct module *mod); 110 110 111 - int module_post_finalize(const Elf_Ehdr *hdr, 112 - const Elf_Shdr *sechdrs, 113 - struct module *mod); 114 - 115 111 #ifdef CONFIG_MODULES 116 112 void flush_module_init_free_work(void); 117 113 #else
+21 -57
kernel/module/main.c
··· 1221 1221 { 1222 1222 } 1223 1223 1224 - void *__module_writable_address(struct module *mod, void *loc) 1225 - { 1226 - for_class_mod_mem_type(type, text) { 1227 - struct module_memory *mem = &mod->mem[type]; 1228 - 1229 - if (loc >= mem->base && loc < mem->base + mem->size) 1230 - return loc + (mem->rw_copy - mem->base); 1231 - } 1232 - 1233 - return loc; 1234 - } 1235 - 1236 1224 static int module_memory_alloc(struct module *mod, enum mod_mem_type type) 1237 1225 { 1238 1226 unsigned int size = PAGE_ALIGN(mod->mem[type].size); ··· 1238 1250 if (!ptr) 1239 1251 return -ENOMEM; 1240 1252 1241 - mod->mem[type].base = ptr; 1242 - 1243 1253 if (execmem_is_rox(execmem_type)) { 1244 - ptr = vzalloc(size); 1254 + int err = execmem_make_temp_rw(ptr, size); 1245 1255 1246 - if (!ptr) { 1247 - execmem_free(mod->mem[type].base); 1256 + if (err) { 1257 + execmem_free(ptr); 1248 1258 return -ENOMEM; 1249 1259 } 1250 1260 1251 - mod->mem[type].rw_copy = ptr; 1252 1261 mod->mem[type].is_rox = true; 1253 - } else { 1254 - mod->mem[type].rw_copy = mod->mem[type].base; 1255 - memset(mod->mem[type].base, 0, size); 1256 1262 } 1257 1263 1258 1264 /* ··· 1262 1280 */ 1263 1281 kmemleak_not_leak(ptr); 1264 1282 1283 + memset(ptr, 0, size); 1284 + mod->mem[type].base = ptr; 1285 + 1265 1286 return 0; 1287 + } 1288 + 1289 + static void module_memory_restore_rox(struct module *mod) 1290 + { 1291 + for_class_mod_mem_type(type, text) { 1292 + struct module_memory *mem = &mod->mem[type]; 1293 + 1294 + if (mem->is_rox) 1295 + execmem_restore_rox(mem->base, mem->size); 1296 + } 1266 1297 } 1267 1298 1268 1299 static void module_memory_free(struct module *mod, enum mod_mem_type type) 1269 1300 { 1270 1301 struct module_memory *mem = &mod->mem[type]; 1271 - 1272 - if (mem->is_rox) 1273 - vfree(mem->rw_copy); 1274 1302 1275 1303 execmem_free(mem->base); 1276 1304 } ··· 2634 2642 for_each_mod_mem_type(type) { 2635 2643 if (!mod->mem[type].size) { 2636 2644 mod->mem[type].base = NULL; 2637 - mod->mem[type].rw_copy = NULL; 2638 2645 continue; 2639 2646 } 2640 2647 ··· 2650 2659 void *dest; 2651 2660 Elf_Shdr *shdr = &info->sechdrs[i]; 2652 2661 const char *sname; 2653 - unsigned long addr; 2654 2662 2655 2663 if (!(shdr->sh_flags & SHF_ALLOC)) 2656 2664 continue; ··· 2670 2680 ret = PTR_ERR(dest); 2671 2681 goto out_err; 2672 2682 } 2673 - addr = (unsigned long)dest; 2674 2683 codetag_section_found = true; 2675 2684 } else { 2676 2685 enum mod_mem_type type = shdr->sh_entsize >> SH_ENTSIZE_TYPE_SHIFT; 2677 2686 unsigned long offset = shdr->sh_entsize & SH_ENTSIZE_OFFSET_MASK; 2678 2687 2679 - addr = (unsigned long)mod->mem[type].base + offset; 2680 - dest = mod->mem[type].rw_copy + offset; 2688 + dest = mod->mem[type].base + offset; 2681 2689 } 2682 2690 2683 2691 if (shdr->sh_type != SHT_NOBITS) { ··· 2698 2710 * users of info can keep taking advantage and using the newly 2699 2711 * minted official memory area. 2700 2712 */ 2701 - shdr->sh_addr = addr; 2713 + shdr->sh_addr = (unsigned long)dest; 2702 2714 pr_debug("\t0x%lx 0x%.8lx %s\n", (long)shdr->sh_addr, 2703 2715 (long)shdr->sh_size, info->secstrings + shdr->sh_name); 2704 2716 } 2705 2717 2706 2718 return 0; 2707 2719 out_err: 2720 + module_memory_restore_rox(mod); 2708 2721 for (t--; t >= 0; t--) 2709 2722 module_memory_free(mod, t); 2710 2723 if (codetag_section_found) ··· 2852 2863 return 0; 2853 2864 } 2854 2865 2855 - int __weak module_post_finalize(const Elf_Ehdr *hdr, 2856 - const Elf_Shdr *sechdrs, 2857 - struct module *me) 2858 - { 2859 - return 0; 2860 - } 2861 - 2862 2866 static int post_relocation(struct module *mod, const struct load_info *info) 2863 2867 { 2864 - int ret; 2865 - 2866 2868 /* Sort exception table now relocations are done. */ 2867 2869 sort_extable(mod->extable, mod->extable + mod->num_exentries); 2868 2870 ··· 2865 2885 add_kallsyms(mod, info); 2866 2886 2867 2887 /* Arch-specific module finalizing. */ 2868 - ret = module_finalize(info->hdr, info->sechdrs, mod); 2869 - if (ret) 2870 - return ret; 2871 - 2872 - for_each_mod_mem_type(type) { 2873 - struct module_memory *mem = &mod->mem[type]; 2874 - 2875 - if (mem->is_rox) { 2876 - if (!execmem_update_copy(mem->base, mem->rw_copy, 2877 - mem->size)) 2878 - return -ENOMEM; 2879 - 2880 - vfree(mem->rw_copy); 2881 - mem->rw_copy = NULL; 2882 - } 2883 - } 2884 - 2885 - return module_post_finalize(info->hdr, info->sechdrs, mod); 2888 + return module_finalize(info->hdr, info->sechdrs, mod); 2886 2889 } 2887 2890 2888 2891 /* Call module constructors. */ ··· 3462 3499 mod->mem[type].size); 3463 3500 } 3464 3501 3502 + module_memory_restore_rox(mod); 3465 3503 module_deallocate(mod, info); 3466 3504 free_copy: 3467 3505 /*
+5 -4
kernel/module/strict_rwx.c
··· 9 9 #include <linux/mm.h> 10 10 #include <linux/vmalloc.h> 11 11 #include <linux/set_memory.h> 12 + #include <linux/execmem.h> 12 13 #include "internal.h" 13 14 14 15 static int module_set_memory(const struct module *mod, enum mod_mem_type type, ··· 33 32 int module_enable_text_rox(const struct module *mod) 34 33 { 35 34 for_class_mod_mem_type(type, text) { 35 + const struct module_memory *mem = &mod->mem[type]; 36 36 int ret; 37 37 38 - if (mod->mem[type].is_rox) 39 - continue; 40 - 41 - if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) 38 + if (mem->is_rox) 39 + ret = execmem_restore_rox(mem->base, mem->size); 40 + else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) 42 41 ret = module_set_memory(mod, type, set_memory_rox); 43 42 else 44 43 ret = module_set_memory(mod, type, set_memory_x);