| From: "Mike Rapoport (Microsoft)" <rppt@kernel.org> |
| Subject: x86/module: prepare module loading for ROX allocations of text |
| Date: Wed, 23 Oct 2024 19:27:09 +0300 |
| |
| When module text memory will be allocated with ROX permissions, the memory |
| at the actual address where the module will live will contain invalid |
| instructions and there will be a writable copy that contains the actual |
| module code. |
| |
| Update relocations and alternatives patching to deal with it. |
| |
| [rppt@kernel.org: fix writable address in cfi_rewrite_endbr()] |
| Link: https://lkml.kernel.org/r/ZysRwR29Ji8CcbXc@kernel.org |
| Link: https://lkml.kernel.org/r/20241023162711.2579610-7-rppt@kernel.org |
| Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org> |
| Tested-by: kdevops <kdevops@lists.linux.dev> |
| Tested-by: Nathan Chancellor <nathan@kernel.org> |
| Cc: Andreas Larsson <andreas@gaisler.com> |
| Cc: Andy Lutomirski <luto@kernel.org> |
| Cc: Ard Biesheuvel <ardb@kernel.org> |
| Cc: Arnd Bergmann <arnd@arndb.de> |
| Cc: Borislav Petkov (AMD) <bp@alien8.de> |
| Cc: Brian Cain <bcain@quicinc.com> |
| Cc: Catalin Marinas <catalin.marinas@arm.com> |
| Cc: Christophe Leroy <christophe.leroy@csgroup.eu> |
| Cc: Christoph Hellwig <hch@lst.de> |
| Cc: Dave Hansen <dave.hansen@linux.intel.com> |
| Cc: Dinh Nguyen <dinguyen@kernel.org> |
| Cc: Geert Uytterhoeven <geert@linux-m68k.org> |
| Cc: Guo Ren <guoren@kernel.org> |
| Cc: Helge Deller <deller@gmx.de> |
| Cc: Huacai Chen <chenhuacai@kernel.org> |
| Cc: Ingo Molnar <mingo@redhat.com> |
| Cc: Johannes Berg <johannes@sipsolutions.net> |
| Cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> |
| Cc: Kent Overstreet <kent.overstreet@linux.dev> |
| Cc: Liam R. Howlett <Liam.Howlett@Oracle.com> |
| Cc: Luis Chamberlain <mcgrof@kernel.org> |
| Cc: Mark Rutland <mark.rutland@arm.com> |
| Cc: Masami Hiramatsu (Google) <mhiramat@kernel.org> |
| Cc: Matt Turner <mattst88@gmail.com> |
| Cc: Max Filippov <jcmvbkbc@gmail.com> |
| Cc: Michael Ellerman <mpe@ellerman.id.au> |
| Cc: Michal Simek <monstr@monstr.eu> |
| Cc: Oleg Nesterov <oleg@redhat.com> |
| Cc: Palmer Dabbelt <palmer@dabbelt.com> |
| Cc: Peter Zijlstra <peterz@infradead.org> |
| Cc: Richard Weinberger <richard@nod.at> |
| Cc: Russell King <linux@armlinux.org.uk> |
| Cc: Song Liu <song@kernel.org> |
| Cc: Stafford Horne <shorne@gmail.com> |
| Cc: Steven Rostedt (Google) <rostedt@goodmis.org> |
| Cc: Suren Baghdasaryan <surenb@google.com> |
| Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de> |
| Cc: Thomas Gleixner <tglx@linutronix.de> |
| Cc: Uladzislau Rezki (Sony) <urezki@gmail.com> |
| Cc: Vineet Gupta <vgupta@kernel.org> |
| Cc: Will Deacon <will@kernel.org> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| arch/um/kernel/um_arch.c | 11 - |
| arch/x86/entry/vdso/vma.c | 3 |
| arch/x86/include/asm/alternative.h | 14 +- |
| arch/x86/kernel/alternative.c | 181 +++++++++++++++------------ |
| arch/x86/kernel/ftrace.c | 30 ++-- |
| arch/x86/kernel/module.c | 45 ++++-- |
| 6 files changed, 167 insertions(+), 117 deletions(-) |
| |
| --- a/arch/um/kernel/um_arch.c~x86-module-prepare-module-loading-for-rox-allocations-of-text |
| +++ a/arch/um/kernel/um_arch.c |
| @@ -435,24 +435,25 @@ void __init arch_cpu_finalize_init(void) |
| os_check_bugs(); |
| } |
| |
| -void apply_seal_endbr(s32 *start, s32 *end) |
| +void apply_seal_endbr(s32 *start, s32 *end, struct module *mod) |
| { |
| } |
| |
| -void apply_retpolines(s32 *start, s32 *end) |
| +void apply_retpolines(s32 *start, s32 *end, struct module *mod) |
| { |
| } |
| |
| -void apply_returns(s32 *start, s32 *end) |
| +void apply_returns(s32 *start, s32 *end, struct module *mod) |
| { |
| } |
| |
| void apply_fineibt(s32 *start_retpoline, s32 *end_retpoline, |
| - s32 *start_cfi, s32 *end_cfi) |
| + s32 *start_cfi, s32 *end_cfi, struct module *mod) |
| { |
| } |
| |
| -void apply_alternatives(struct alt_instr *start, struct alt_instr *end) |
| +void apply_alternatives(struct alt_instr *start, struct alt_instr *end, |
| + struct module *mod) |
| { |
| } |
| |
| --- a/arch/x86/entry/vdso/vma.c~x86-module-prepare-module-loading-for-rox-allocations-of-text |
| +++ a/arch/x86/entry/vdso/vma.c |
| @@ -54,7 +54,8 @@ int __init init_vdso_image(const struct |
| |
| apply_alternatives((struct alt_instr *)(image->data + image->alt), |
| (struct alt_instr *)(image->data + image->alt + |
| - image->alt_len)); |
| + image->alt_len), |
| + NULL); |
| |
| return 0; |
| } |
| --- a/arch/x86/include/asm/alternative.h~x86-module-prepare-module-loading-for-rox-allocations-of-text |
| +++ a/arch/x86/include/asm/alternative.h |
| @@ -96,16 +96,16 @@ extern struct alt_instr __alt_instructio |
| * instructions were patched in already: |
| */ |
| extern int alternatives_patched; |
| +struct module; |
| |
| extern void alternative_instructions(void); |
| -extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); |
| -extern void apply_retpolines(s32 *start, s32 *end); |
| -extern void apply_returns(s32 *start, s32 *end); |
| -extern void apply_seal_endbr(s32 *start, s32 *end); |
| +extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end, |
| + struct module *mod); |
| +extern void apply_retpolines(s32 *start, s32 *end, struct module *mod); |
| +extern void apply_returns(s32 *start, s32 *end, struct module *mod); |
| +extern void apply_seal_endbr(s32 *start, s32 *end, struct module *mod); |
| extern void apply_fineibt(s32 *start_retpoline, s32 *end_retpoine, |
| - s32 *start_cfi, s32 *end_cfi); |
| - |
| -struct module; |
| + s32 *start_cfi, s32 *end_cfi, struct module *mod); |
| |
| struct callthunk_sites { |
| s32 *call_start, *call_end; |
| --- a/arch/x86/kernel/alternative.c~x86-module-prepare-module-loading-for-rox-allocations-of-text |
| +++ a/arch/x86/kernel/alternative.c |
| @@ -392,8 +392,10 @@ EXPORT_SYMBOL(BUG_func); |
| * Rewrite the "call BUG_func" replacement to point to the target of the |
| * indirect pv_ops call "call *disp(%ip)". |
| */ |
| -static int alt_replace_call(u8 *instr, u8 *insn_buff, struct alt_instr *a) |
| +static int alt_replace_call(u8 *instr, u8 *insn_buff, struct alt_instr *a, |
| + struct module *mod) |
| { |
| + u8 *wr_instr = module_writable_address(mod, instr); |
| void *target, *bug = &BUG_func; |
| s32 disp; |
| |
| @@ -403,14 +405,14 @@ static int alt_replace_call(u8 *instr, u |
| } |
| |
| if (a->instrlen != 6 || |
| - instr[0] != CALL_RIP_REL_OPCODE || |
| - instr[1] != CALL_RIP_REL_MODRM) { |
| + wr_instr[0] != CALL_RIP_REL_OPCODE || |
| + wr_instr[1] != CALL_RIP_REL_MODRM) { |
| pr_err("ALT_FLAG_DIRECT_CALL set for unrecognized indirect call\n"); |
| BUG(); |
| } |
| |
| /* Skip CALL_RIP_REL_OPCODE and CALL_RIP_REL_MODRM */ |
| - disp = *(s32 *)(instr + 2); |
| + disp = *(s32 *)(wr_instr + 2); |
| #ifdef CONFIG_X86_64 |
| /* ff 15 00 00 00 00 call *0x0(%rip) */ |
| /* target address is stored at "next instruction + disp". */ |
| @@ -448,7 +450,8 @@ static inline u8 * instr_va(struct alt_i |
| * to refetch changed I$ lines. |
| */ |
| void __init_or_module noinline apply_alternatives(struct alt_instr *start, |
| - struct alt_instr *end) |
| + struct alt_instr *end, |
| + struct module *mod) |
| { |
| u8 insn_buff[MAX_PATCH_LEN]; |
| u8 *instr, *replacement; |
| @@ -477,6 +480,7 @@ void __init_or_module noinline apply_alt |
| */ |
| for (a = start; a < end; a++) { |
| int insn_buff_sz = 0; |
| + u8 *wr_instr, *wr_replacement; |
| |
| /* |
| * In case of nested ALTERNATIVE()s the outer alternative might |
| @@ -490,7 +494,11 @@ void __init_or_module noinline apply_alt |
| } |
| |
| instr = instr_va(a); |
| + wr_instr = module_writable_address(mod, instr); |
| + |
| replacement = (u8 *)&a->repl_offset + a->repl_offset; |
| + wr_replacement = module_writable_address(mod, replacement); |
| + |
| BUG_ON(a->instrlen > sizeof(insn_buff)); |
| BUG_ON(a->cpuid >= (NCAPINTS + NBUGINTS) * 32); |
| |
| @@ -501,9 +509,9 @@ void __init_or_module noinline apply_alt |
| * patch if feature is *NOT* present. |
| */ |
| if (!boot_cpu_has(a->cpuid) == !(a->flags & ALT_FLAG_NOT)) { |
| - memcpy(insn_buff, instr, a->instrlen); |
| + memcpy(insn_buff, wr_instr, a->instrlen); |
| optimize_nops(instr, insn_buff, a->instrlen); |
| - text_poke_early(instr, insn_buff, a->instrlen); |
| + text_poke_early(wr_instr, insn_buff, a->instrlen); |
| continue; |
| } |
| |
| @@ -513,11 +521,12 @@ void __init_or_module noinline apply_alt |
| instr, instr, a->instrlen, |
| replacement, a->replacementlen, a->flags); |
| |
| - memcpy(insn_buff, replacement, a->replacementlen); |
| + memcpy(insn_buff, wr_replacement, a->replacementlen); |
| insn_buff_sz = a->replacementlen; |
| |
| if (a->flags & ALT_FLAG_DIRECT_CALL) { |
| - insn_buff_sz = alt_replace_call(instr, insn_buff, a); |
| + insn_buff_sz = alt_replace_call(instr, insn_buff, a, |
| + mod); |
| if (insn_buff_sz < 0) |
| continue; |
| } |
| @@ -527,11 +536,11 @@ void __init_or_module noinline apply_alt |
| |
| apply_relocation(insn_buff, instr, a->instrlen, replacement, a->replacementlen); |
| |
| - DUMP_BYTES(ALT, instr, a->instrlen, "%px: old_insn: ", instr); |
| + DUMP_BYTES(ALT, wr_instr, a->instrlen, "%px: old_insn: ", instr); |
| DUMP_BYTES(ALT, replacement, a->replacementlen, "%px: rpl_insn: ", replacement); |
| DUMP_BYTES(ALT, insn_buff, insn_buff_sz, "%px: final_insn: ", instr); |
| |
| - text_poke_early(instr, insn_buff, insn_buff_sz); |
| + text_poke_early(wr_instr, insn_buff, insn_buff_sz); |
| } |
| |
| kasan_enable_current(); |
| @@ -722,18 +731,20 @@ static int patch_retpoline(void *addr, s |
| /* |
| * Generated by 'objtool --retpoline'. |
| */ |
| -void __init_or_module noinline apply_retpolines(s32 *start, s32 *end) |
| +void __init_or_module noinline apply_retpolines(s32 *start, s32 *end, |
| + struct module *mod) |
| { |
| s32 *s; |
| |
| for (s = start; s < end; s++) { |
| void *addr = (void *)s + *s; |
| + void *wr_addr = module_writable_address(mod, addr); |
| struct insn insn; |
| int len, ret; |
| u8 bytes[16]; |
| u8 op1, op2; |
| |
| - ret = insn_decode_kernel(&insn, addr); |
| + ret = insn_decode_kernel(&insn, wr_addr); |
| if (WARN_ON_ONCE(ret < 0)) |
| continue; |
| |
| @@ -761,9 +772,9 @@ void __init_or_module noinline apply_ret |
| len = patch_retpoline(addr, &insn, bytes); |
| if (len == insn.length) { |
| optimize_nops(addr, bytes, len); |
| - DUMP_BYTES(RETPOLINE, ((u8*)addr), len, "%px: orig: ", addr); |
| + DUMP_BYTES(RETPOLINE, ((u8*)wr_addr), len, "%px: orig: ", addr); |
| DUMP_BYTES(RETPOLINE, ((u8*)bytes), len, "%px: repl: ", addr); |
| - text_poke_early(addr, bytes, len); |
| + text_poke_early(wr_addr, bytes, len); |
| } |
| } |
| } |
| @@ -799,7 +810,8 @@ static int patch_return(void *addr, stru |
| return i; |
| } |
| |
| -void __init_or_module noinline apply_returns(s32 *start, s32 *end) |
| +void __init_or_module noinline apply_returns(s32 *start, s32 *end, |
| + struct module *mod) |
| { |
| s32 *s; |
| |
| @@ -808,12 +820,13 @@ void __init_or_module noinline apply_ret |
| |
| for (s = start; s < end; s++) { |
| void *dest = NULL, *addr = (void *)s + *s; |
| + void *wr_addr = module_writable_address(mod, addr); |
| struct insn insn; |
| int len, ret; |
| u8 bytes[16]; |
| u8 op; |
| |
| - ret = insn_decode_kernel(&insn, addr); |
| + ret = insn_decode_kernel(&insn, wr_addr); |
| if (WARN_ON_ONCE(ret < 0)) |
| continue; |
| |
| @@ -833,32 +846,35 @@ void __init_or_module noinline apply_ret |
| |
| len = patch_return(addr, &insn, bytes); |
| if (len == insn.length) { |
| - DUMP_BYTES(RET, ((u8*)addr), len, "%px: orig: ", addr); |
| + DUMP_BYTES(RET, ((u8*)wr_addr), len, "%px: orig: ", addr); |
| DUMP_BYTES(RET, ((u8*)bytes), len, "%px: repl: ", addr); |
| - text_poke_early(addr, bytes, len); |
| + text_poke_early(wr_addr, bytes, len); |
| } |
| } |
| } |
| #else |
| -void __init_or_module noinline apply_returns(s32 *start, s32 *end) { } |
| +void __init_or_module noinline apply_returns(s32 *start, s32 *end, |
| + struct module *mod) { } |
| #endif /* CONFIG_MITIGATION_RETHUNK */ |
| |
| #else /* !CONFIG_MITIGATION_RETPOLINE || !CONFIG_OBJTOOL */ |
| |
| -void __init_or_module noinline apply_retpolines(s32 *start, s32 *end) { } |
| -void __init_or_module noinline apply_returns(s32 *start, s32 *end) { } |
| +void __init_or_module noinline apply_retpolines(s32 *start, s32 *end, |
| + struct module *mod) { } |
| +void __init_or_module noinline apply_returns(s32 *start, s32 *end, |
| + struct module *mod) { } |
| |
| #endif /* CONFIG_MITIGATION_RETPOLINE && CONFIG_OBJTOOL */ |
| |
| #ifdef CONFIG_X86_KERNEL_IBT |
| |
| -static void poison_cfi(void *addr); |
| +static void poison_cfi(void *addr, void *wr_addr); |
| |
| -static void __init_or_module poison_endbr(void *addr, bool warn) |
| +static void __init_or_module poison_endbr(void *addr, void *wr_addr, bool warn) |
| { |
| u32 endbr, poison = gen_endbr_poison(); |
| |
| - if (WARN_ON_ONCE(get_kernel_nofault(endbr, addr))) |
| + if (WARN_ON_ONCE(get_kernel_nofault(endbr, wr_addr))) |
| return; |
| |
| if (!is_endbr(endbr)) { |
| @@ -873,7 +889,7 @@ static void __init_or_module poison_endb |
| */ |
| DUMP_BYTES(ENDBR, ((u8*)addr), 4, "%px: orig: ", addr); |
| DUMP_BYTES(ENDBR, ((u8*)&poison), 4, "%px: repl: ", addr); |
| - text_poke_early(addr, &poison, 4); |
| + text_poke_early(wr_addr, &poison, 4); |
| } |
| |
| /* |
| @@ -882,22 +898,23 @@ static void __init_or_module poison_endb |
| * Seal the functions for indirect calls by clobbering the ENDBR instructions |
| * and the kCFI hash value. |
| */ |
| -void __init_or_module noinline apply_seal_endbr(s32 *start, s32 *end) |
| +void __init_or_module noinline apply_seal_endbr(s32 *start, s32 *end, struct module *mod) |
| { |
| s32 *s; |
| |
| for (s = start; s < end; s++) { |
| void *addr = (void *)s + *s; |
| + void *wr_addr = module_writable_address(mod, addr); |
| |
| - poison_endbr(addr, true); |
| + poison_endbr(addr, wr_addr, true); |
| if (IS_ENABLED(CONFIG_FINEIBT)) |
| - poison_cfi(addr - 16); |
| + poison_cfi(addr - 16, wr_addr - 16); |
| } |
| } |
| |
| #else |
| |
| -void __init_or_module apply_seal_endbr(s32 *start, s32 *end) { } |
| +void __init_or_module apply_seal_endbr(s32 *start, s32 *end, struct module *mod) { } |
| |
| #endif /* CONFIG_X86_KERNEL_IBT */ |
| |
| @@ -1119,7 +1136,7 @@ static u32 decode_caller_hash(void *addr |
| } |
| |
| /* .retpoline_sites */ |
| -static int cfi_disable_callers(s32 *start, s32 *end) |
| +static int cfi_disable_callers(s32 *start, s32 *end, struct module *mod) |
| { |
| /* |
| * Disable kCFI by patching in a JMP.d8, this leaves the hash immediate |
| @@ -1131,20 +1148,23 @@ static int cfi_disable_callers(s32 *star |
| |
| for (s = start; s < end; s++) { |
| void *addr = (void *)s + *s; |
| + void *wr_addr; |
| u32 hash; |
| |
| addr -= fineibt_caller_size; |
| - hash = decode_caller_hash(addr); |
| + wr_addr = module_writable_address(mod, addr); |
| + hash = decode_caller_hash(wr_addr); |
| + |
| if (!hash) /* nocfi callers */ |
| continue; |
| |
| - text_poke_early(addr, jmp, 2); |
| + text_poke_early(wr_addr, jmp, 2); |
| } |
| |
| return 0; |
| } |
| |
| -static int cfi_enable_callers(s32 *start, s32 *end) |
| +static int cfi_enable_callers(s32 *start, s32 *end, struct module *mod) |
| { |
| /* |
| * Re-enable kCFI, undo what cfi_disable_callers() did. |
| @@ -1154,106 +1174,115 @@ static int cfi_enable_callers(s32 *start |
| |
| for (s = start; s < end; s++) { |
| void *addr = (void *)s + *s; |
| + void *wr_addr; |
| u32 hash; |
| |
| addr -= fineibt_caller_size; |
| - hash = decode_caller_hash(addr); |
| + wr_addr = module_writable_address(mod, addr); |
| + hash = decode_caller_hash(wr_addr); |
| if (!hash) /* nocfi callers */ |
| continue; |
| |
| - text_poke_early(addr, mov, 2); |
| + text_poke_early(wr_addr, mov, 2); |
| } |
| |
| return 0; |
| } |
| |
| /* .cfi_sites */ |
| -static int cfi_rand_preamble(s32 *start, s32 *end) |
| +static int cfi_rand_preamble(s32 *start, s32 *end, struct module *mod) |
| { |
| s32 *s; |
| |
| for (s = start; s < end; s++) { |
| void *addr = (void *)s + *s; |
| + void *wr_addr = module_writable_address(mod, addr); |
| u32 hash; |
| |
| - hash = decode_preamble_hash(addr); |
| + hash = decode_preamble_hash(wr_addr); |
| if (WARN(!hash, "no CFI hash found at: %pS %px %*ph\n", |
| addr, addr, 5, addr)) |
| return -EINVAL; |
| |
| hash = cfi_rehash(hash); |
| - text_poke_early(addr + 1, &hash, 4); |
| + text_poke_early(wr_addr + 1, &hash, 4); |
| } |
| |
| return 0; |
| } |
| |
| -static int cfi_rewrite_preamble(s32 *start, s32 *end) |
| +static int cfi_rewrite_preamble(s32 *start, s32 *end, struct module *mod) |
| { |
| s32 *s; |
| |
| for (s = start; s < end; s++) { |
| void *addr = (void *)s + *s; |
| + void *wr_addr = module_writable_address(mod, addr); |
| u32 hash; |
| |
| - hash = decode_preamble_hash(addr); |
| + hash = decode_preamble_hash(wr_addr); |
| if (WARN(!hash, "no CFI hash found at: %pS %px %*ph\n", |
| addr, addr, 5, addr)) |
| return -EINVAL; |
| |
| - text_poke_early(addr, fineibt_preamble_start, fineibt_preamble_size); |
| - WARN_ON(*(u32 *)(addr + fineibt_preamble_hash) != 0x12345678); |
| - text_poke_early(addr + fineibt_preamble_hash, &hash, 4); |
| + text_poke_early(wr_addr, fineibt_preamble_start, fineibt_preamble_size); |
| + WARN_ON(*(u32 *)(wr_addr + fineibt_preamble_hash) != 0x12345678); |
| + text_poke_early(wr_addr + fineibt_preamble_hash, &hash, 4); |
| } |
| |
| return 0; |
| } |
| |
| -static void cfi_rewrite_endbr(s32 *start, s32 *end) |
| +static void cfi_rewrite_endbr(s32 *start, s32 *end, struct module *mod) |
| { |
| s32 *s; |
| |
| for (s = start; s < end; s++) { |
| void *addr = (void *)s + *s; |
| + void *wr_addr = module_writable_address(mod, addr); |
| |
| - poison_endbr(addr+16, false); |
| + poison_endbr(addr + 16, wr_addr + 16, false); |
| } |
| } |
| |
| /* .retpoline_sites */ |
| -static int cfi_rand_callers(s32 *start, s32 *end) |
| +static int cfi_rand_callers(s32 *start, s32 *end, struct module *mod) |
| { |
| s32 *s; |
| |
| for (s = start; s < end; s++) { |
| void *addr = (void *)s + *s; |
| + void *wr_addr; |
| u32 hash; |
| |
| addr -= fineibt_caller_size; |
| - hash = decode_caller_hash(addr); |
| + wr_addr = module_writable_address(mod, addr); |
| + hash = decode_caller_hash(wr_addr); |
| if (hash) { |
| hash = -cfi_rehash(hash); |
| - text_poke_early(addr + 2, &hash, 4); |
| + text_poke_early(wr_addr + 2, &hash, 4); |
| } |
| } |
| |
| return 0; |
| } |
| |
| -static int cfi_rewrite_callers(s32 *start, s32 *end) |
| +static int cfi_rewrite_callers(s32 *start, s32 *end, struct module *mod) |
| { |
| s32 *s; |
| |
| for (s = start; s < end; s++) { |
| void *addr = (void *)s + *s; |
| + void *wr_addr; |
| u32 hash; |
| |
| addr -= fineibt_caller_size; |
| - hash = decode_caller_hash(addr); |
| + wr_addr = module_writable_address(mod, addr); |
| + hash = decode_caller_hash(wr_addr); |
| if (hash) { |
| - text_poke_early(addr, fineibt_caller_start, fineibt_caller_size); |
| - WARN_ON(*(u32 *)(addr + fineibt_caller_hash) != 0x12345678); |
| - text_poke_early(addr + fineibt_caller_hash, &hash, 4); |
| + text_poke_early(wr_addr, fineibt_caller_start, fineibt_caller_size); |
| + WARN_ON(*(u32 *)(wr_addr + fineibt_caller_hash) != 0x12345678); |
| + text_poke_early(wr_addr + fineibt_caller_hash, &hash, 4); |
| } |
| /* rely on apply_retpolines() */ |
| } |
| @@ -1262,8 +1291,9 @@ static int cfi_rewrite_callers(s32 *star |
| } |
| |
| static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline, |
| - s32 *start_cfi, s32 *end_cfi, bool builtin) |
| + s32 *start_cfi, s32 *end_cfi, struct module *mod) |
| { |
| + bool builtin = mod ? false : true; |
| int ret; |
| |
| if (WARN_ONCE(fineibt_preamble_size != 16, |
| @@ -1281,7 +1311,7 @@ static void __apply_fineibt(s32 *start_r |
| * rewrite them. This disables all CFI. If this succeeds but any of the |
| * later stages fails, we're without CFI. |
| */ |
| - ret = cfi_disable_callers(start_retpoline, end_retpoline); |
| + ret = cfi_disable_callers(start_retpoline, end_retpoline, mod); |
| if (ret) |
| goto err; |
| |
| @@ -1292,11 +1322,11 @@ static void __apply_fineibt(s32 *start_r |
| cfi_bpf_subprog_hash = cfi_rehash(cfi_bpf_subprog_hash); |
| } |
| |
| - ret = cfi_rand_preamble(start_cfi, end_cfi); |
| + ret = cfi_rand_preamble(start_cfi, end_cfi, mod); |
| if (ret) |
| goto err; |
| |
| - ret = cfi_rand_callers(start_retpoline, end_retpoline); |
| + ret = cfi_rand_callers(start_retpoline, end_retpoline, mod); |
| if (ret) |
| goto err; |
| } |
| @@ -1308,7 +1338,7 @@ static void __apply_fineibt(s32 *start_r |
| return; |
| |
| case CFI_KCFI: |
| - ret = cfi_enable_callers(start_retpoline, end_retpoline); |
| + ret = cfi_enable_callers(start_retpoline, end_retpoline, mod); |
| if (ret) |
| goto err; |
| |
| @@ -1318,17 +1348,17 @@ static void __apply_fineibt(s32 *start_r |
| |
| case CFI_FINEIBT: |
| /* place the FineIBT preamble at func()-16 */ |
| - ret = cfi_rewrite_preamble(start_cfi, end_cfi); |
| + ret = cfi_rewrite_preamble(start_cfi, end_cfi, mod); |
| if (ret) |
| goto err; |
| |
| /* rewrite the callers to target func()-16 */ |
| - ret = cfi_rewrite_callers(start_retpoline, end_retpoline); |
| + ret = cfi_rewrite_callers(start_retpoline, end_retpoline, mod); |
| if (ret) |
| goto err; |
| |
| /* now that nobody targets func()+0, remove ENDBR there */ |
| - cfi_rewrite_endbr(start_cfi, end_cfi); |
| + cfi_rewrite_endbr(start_cfi, end_cfi, mod); |
| |
| if (builtin) |
| pr_info("Using FineIBT CFI\n"); |
| @@ -1347,7 +1377,7 @@ static inline void poison_hash(void *add |
| *(u32 *)addr = 0; |
| } |
| |
| -static void poison_cfi(void *addr) |
| +static void poison_cfi(void *addr, void *wr_addr) |
| { |
| switch (cfi_mode) { |
| case CFI_FINEIBT: |
| @@ -1359,8 +1389,8 @@ static void poison_cfi(void *addr) |
| * ud2 |
| * 1: nop |
| */ |
| - poison_endbr(addr, false); |
| - poison_hash(addr + fineibt_preamble_hash); |
| + poison_endbr(addr, wr_addr, false); |
| + poison_hash(wr_addr + fineibt_preamble_hash); |
| break; |
| |
| case CFI_KCFI: |
| @@ -1369,7 +1399,7 @@ static void poison_cfi(void *addr) |
| * movl $0, %eax |
| * .skip 11, 0x90 |
| */ |
| - poison_hash(addr + 1); |
| + poison_hash(wr_addr + 1); |
| break; |
| |
| default: |
| @@ -1380,22 +1410,21 @@ static void poison_cfi(void *addr) |
| #else |
| |
| static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline, |
| - s32 *start_cfi, s32 *end_cfi, bool builtin) |
| + s32 *start_cfi, s32 *end_cfi, struct module *mod) |
| { |
| } |
| |
| #ifdef CONFIG_X86_KERNEL_IBT |
| -static void poison_cfi(void *addr) { } |
| +static void poison_cfi(void *addr, void *wr_addr) { } |
| #endif |
| |
| #endif |
| |
| void apply_fineibt(s32 *start_retpoline, s32 *end_retpoline, |
| - s32 *start_cfi, s32 *end_cfi) |
| + s32 *start_cfi, s32 *end_cfi, struct module *mod) |
| { |
| return __apply_fineibt(start_retpoline, end_retpoline, |
| - start_cfi, end_cfi, |
| - /* .builtin = */ false); |
| + start_cfi, end_cfi, mod); |
| } |
| |
| #ifdef CONFIG_SMP |
| @@ -1692,16 +1721,16 @@ void __init alternative_instructions(voi |
| paravirt_set_cap(); |
| |
| __apply_fineibt(__retpoline_sites, __retpoline_sites_end, |
| - __cfi_sites, __cfi_sites_end, true); |
| + __cfi_sites, __cfi_sites_end, NULL); |
| |
| /* |
| * Rewrite the retpolines, must be done before alternatives since |
| * those can rewrite the retpoline thunks. |
| */ |
| - apply_retpolines(__retpoline_sites, __retpoline_sites_end); |
| - apply_returns(__return_sites, __return_sites_end); |
| + apply_retpolines(__retpoline_sites, __retpoline_sites_end, NULL); |
| + apply_returns(__return_sites, __return_sites_end, NULL); |
| |
| - apply_alternatives(__alt_instructions, __alt_instructions_end); |
| + apply_alternatives(__alt_instructions, __alt_instructions_end, NULL); |
| |
| /* |
| * Now all calls are established. Apply the call thunks if |
| @@ -1712,7 +1741,7 @@ void __init alternative_instructions(voi |
| /* |
| * Seal all functions that do not have their address taken. |
| */ |
| - apply_seal_endbr(__ibt_endbr_seal, __ibt_endbr_seal_end); |
| + apply_seal_endbr(__ibt_endbr_seal, __ibt_endbr_seal_end, NULL); |
| |
| #ifdef CONFIG_SMP |
| /* Patch to UP if other cpus not imminent. */ |
| --- a/arch/x86/kernel/ftrace.c~x86-module-prepare-module-loading-for-rox-allocations-of-text |
| +++ a/arch/x86/kernel/ftrace.c |
| @@ -118,10 +118,13 @@ ftrace_modify_code_direct(unsigned long |
| return ret; |
| |
| /* replace the text with the new text */ |
| - if (ftrace_poke_late) |
| + if (ftrace_poke_late) { |
| text_poke_queue((void *)ip, new_code, MCOUNT_INSN_SIZE, NULL); |
| - else |
| - text_poke_early((void *)ip, new_code, MCOUNT_INSN_SIZE); |
| + } else { |
| + mutex_lock(&text_mutex); |
| + text_poke((void *)ip, new_code, MCOUNT_INSN_SIZE); |
| + mutex_unlock(&text_mutex); |
| + } |
| return 0; |
| } |
| |
| @@ -318,7 +321,7 @@ create_trampoline(struct ftrace_ops *ops |
| unsigned const char op_ref[] = { 0x48, 0x8b, 0x15 }; |
| unsigned const char retq[] = { RET_INSN_OPCODE, INT3_INSN_OPCODE }; |
| union ftrace_op_code_union op_ptr; |
| - int ret; |
| + void *ret; |
| |
| if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) { |
| start_offset = (unsigned long)ftrace_regs_caller; |
| @@ -349,15 +352,15 @@ create_trampoline(struct ftrace_ops *ops |
| npages = DIV_ROUND_UP(*tramp_size, PAGE_SIZE); |
| |
| /* Copy ftrace_caller onto the trampoline memory */ |
| - ret = copy_from_kernel_nofault(trampoline, (void *)start_offset, size); |
| - if (WARN_ON(ret < 0)) |
| + ret = text_poke_copy(trampoline, (void *)start_offset, size); |
| + if (WARN_ON(!ret)) |
| goto fail; |
| |
| ip = trampoline + size; |
| if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) |
| __text_gen_insn(ip, JMP32_INSN_OPCODE, ip, x86_return_thunk, JMP32_INSN_SIZE); |
| else |
| - memcpy(ip, retq, sizeof(retq)); |
| + text_poke_copy(ip, retq, sizeof(retq)); |
| |
| /* No need to test direct calls on created trampolines */ |
| if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) { |
| @@ -365,8 +368,7 @@ create_trampoline(struct ftrace_ops *ops |
| ip = trampoline + (jmp_offset - start_offset); |
| if (WARN_ON(*(char *)ip != 0x75)) |
| goto fail; |
| - ret = copy_from_kernel_nofault(ip, x86_nops[2], 2); |
| - if (ret < 0) |
| + if (!text_poke_copy(ip, x86_nops[2], 2)) |
| goto fail; |
| } |
| |
| @@ -379,7 +381,7 @@ create_trampoline(struct ftrace_ops *ops |
| */ |
| |
| ptr = (unsigned long *)(trampoline + size + RET_SIZE); |
| - *ptr = (unsigned long)ops; |
| + text_poke_copy(ptr, &ops, sizeof(unsigned long)); |
| |
| op_offset -= start_offset; |
| memcpy(&op_ptr, trampoline + op_offset, OP_REF_SIZE); |
| @@ -395,7 +397,7 @@ create_trampoline(struct ftrace_ops *ops |
| op_ptr.offset = offset; |
| |
| /* put in the new offset to the ftrace_ops */ |
| - memcpy(trampoline + op_offset, &op_ptr, OP_REF_SIZE); |
| + text_poke_copy(trampoline + op_offset, &op_ptr, OP_REF_SIZE); |
| |
| /* put in the call to the function */ |
| mutex_lock(&text_mutex); |
| @@ -405,9 +407,9 @@ create_trampoline(struct ftrace_ops *ops |
| * the depth accounting before the call already. |
| */ |
| dest = ftrace_ops_get_func(ops); |
| - memcpy(trampoline + call_offset, |
| - text_gen_insn(CALL_INSN_OPCODE, trampoline + call_offset, dest), |
| - CALL_INSN_SIZE); |
| + text_poke_copy_locked(trampoline + call_offset, |
| + text_gen_insn(CALL_INSN_OPCODE, trampoline + call_offset, dest), |
| + CALL_INSN_SIZE, false); |
| mutex_unlock(&text_mutex); |
| |
| /* ALLOC_TRAMP flags lets us know we created it */ |
| --- a/arch/x86/kernel/module.c~x86-module-prepare-module-loading-for-rox-allocations-of-text |
| +++ a/arch/x86/kernel/module.c |
| @@ -146,18 +146,21 @@ static int __write_relocate_add(Elf64_Sh |
| } |
| |
| if (apply) { |
| - if (memcmp(loc, &zero, size)) { |
| + void *wr_loc = module_writable_address(me, loc); |
| + |
| + if (memcmp(wr_loc, &zero, size)) { |
| pr_err("x86/modules: Invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n", |
| (int)ELF64_R_TYPE(rel[i].r_info), loc, val); |
| return -ENOEXEC; |
| } |
| - write(loc, &val, size); |
| + write(wr_loc, &val, size); |
| } else { |
| if (memcmp(loc, &val, size)) { |
| pr_warn("x86/modules: Invalid relocation target, existing value does not match expected value for type %d, loc %p, val %Lx\n", |
| (int)ELF64_R_TYPE(rel[i].r_info), loc, val); |
| return -ENOEXEC; |
| } |
| + /* FIXME: needs care for ROX module allocations */ |
| write(loc, &zero, size); |
| } |
| } |
| @@ -224,7 +227,7 @@ int module_finalize(const Elf_Ehdr *hdr, |
| const Elf_Shdr *sechdrs, |
| struct module *me) |
| { |
| - const Elf_Shdr *s, *alt = NULL, *locks = NULL, |
| + const Elf_Shdr *s, *alt = NULL, |
| *orc = NULL, *orc_ip = NULL, |
| *retpolines = NULL, *returns = NULL, *ibt_endbr = NULL, |
| *calls = NULL, *cfi = NULL; |
| @@ -233,8 +236,6 @@ int module_finalize(const Elf_Ehdr *hdr, |
| for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { |
| if (!strcmp(".altinstructions", secstrings + s->sh_name)) |
| alt = s; |
| - if (!strcmp(".smp_locks", secstrings + s->sh_name)) |
| - locks = s; |
| if (!strcmp(".orc_unwind", secstrings + s->sh_name)) |
| orc = s; |
| if (!strcmp(".orc_unwind_ip", secstrings + s->sh_name)) |
| @@ -265,20 +266,20 @@ int module_finalize(const Elf_Ehdr *hdr, |
| csize = cfi->sh_size; |
| } |
| |
| - apply_fineibt(rseg, rseg + rsize, cseg, cseg + csize); |
| + apply_fineibt(rseg, rseg + rsize, cseg, cseg + csize, me); |
| } |
| if (retpolines) { |
| void *rseg = (void *)retpolines->sh_addr; |
| - apply_retpolines(rseg, rseg + retpolines->sh_size); |
| + apply_retpolines(rseg, rseg + retpolines->sh_size, me); |
| } |
| if (returns) { |
| void *rseg = (void *)returns->sh_addr; |
| - apply_returns(rseg, rseg + returns->sh_size); |
| + apply_returns(rseg, rseg + returns->sh_size, me); |
| } |
| if (alt) { |
| /* patch .altinstructions */ |
| void *aseg = (void *)alt->sh_addr; |
| - apply_alternatives(aseg, aseg + alt->sh_size); |
| + apply_alternatives(aseg, aseg + alt->sh_size, me); |
| } |
| if (calls || alt) { |
| struct callthunk_sites cs = {}; |
| @@ -297,8 +298,28 @@ int module_finalize(const Elf_Ehdr *hdr, |
| } |
| if (ibt_endbr) { |
| void *iseg = (void *)ibt_endbr->sh_addr; |
| - apply_seal_endbr(iseg, iseg + ibt_endbr->sh_size); |
| + apply_seal_endbr(iseg, iseg + ibt_endbr->sh_size, me); |
| } |
| + |
| + if (orc && orc_ip) |
| + unwind_module_init(me, (void *)orc_ip->sh_addr, orc_ip->sh_size, |
| + (void *)orc->sh_addr, orc->sh_size); |
| + |
| + return 0; |
| +} |
| + |
| +int module_post_finalize(const Elf_Ehdr *hdr, |
| + const Elf_Shdr *sechdrs, |
| + struct module *me) |
| +{ |
| + const Elf_Shdr *s, *locks = NULL; |
| + char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; |
| + |
| + for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { |
| + if (!strcmp(".smp_locks", secstrings + s->sh_name)) |
| + locks = s; |
| + } |
| + |
| if (locks) { |
| void *lseg = (void *)locks->sh_addr; |
| void *text = me->mem[MOD_TEXT].base; |
| @@ -308,10 +329,6 @@ int module_finalize(const Elf_Ehdr *hdr, |
| text, text_end); |
| } |
| |
| - if (orc && orc_ip) |
| - unwind_module_init(me, (void *)orc_ip->sh_addr, orc_ip->sh_size, |
| - (void *)orc->sh_addr, orc->sh_size); |
| - |
| return 0; |
| } |
| |
| _ |