Message ID | 20241009124352.3105119-2-ardb+git@google.com (mailing list archive) |
---|---|
State | Mainlined |
Commit | 577c134d311b9b94598d7a0c86be1f431f823003 |
Headers | show |
Series | [v2] x86/stackprotector: Work around strict Clang TLS symbol requirements | expand |
On Wed, Oct 09, 2024 at 02:43:53PM +0200, Ard Biesheuvel wrote: > However, if a non-TLS definition of the symbol in question is visible in > the same compilation unit (which amounts to the whole of vmlinux if LTO > is enabled), it will drop the per-CPU prefix and emit a load from a > bogus address. I take this to mean that x86 32-bit kernels built with the stack protector and using Clang LTO will crash very quickly?
On Mon, 14 Oct 2024 at 22:59, Kees Cook <kees@kernel.org> wrote: > > On Wed, Oct 09, 2024 at 02:43:53PM +0200, Ard Biesheuvel wrote: > > However, if a non-TLS definition of the symbol in question is visible in > > the same compilation unit (which amounts to the whole of vmlinux if LTO > > is enabled), it will drop the per-CPU prefix and emit a load from a > > bogus address. > > I take this to mean that x86 32-bit kernels built with the stack > protector and using Clang LTO will crash very quickly? > Yeah. The linked issue is not quite clear, but it does suggest things are pretty broken in that case.
On Tue, Oct 15, 2024 at 12:56:57PM +0200, Ard Biesheuvel wrote: > On Mon, 14 Oct 2024 at 22:59, Kees Cook <kees@kernel.org> wrote: > > > > On Wed, Oct 09, 2024 at 02:43:53PM +0200, Ard Biesheuvel wrote: > > > However, if a non-TLS definition of the symbol in question is visible in > > > the same compilation unit (which amounts to the whole of vmlinux if LTO > > > is enabled), it will drop the per-CPU prefix and emit a load from a > > > bogus address. > > > > I take this to mean that x86 32-bit kernels built with the stack > > protector and using Clang LTO will crash very quickly? > > > > Yeah. The linked issue is not quite clear, but it does suggest things > are pretty broken in that case. Yeah, i386_defconfig with CONFIG_LTO_CLANG_FULL=y explodes on boot for me without this change: [ 0.000000] Linux version 6.12.0-rc3-00044-g2f87d0916ce0 (nathan@thelio-3990X) (ClangBuiltLinux clang version 19.1.2 (https://github.com/llvm/llvm-project.git 7ba7d8e2f7b6445b60679da826210cdde29eaf8b), ClangBuiltLinux LLD 19.1.2 (https://github.com/llvm/llvm-project.git 7ba7d8e2f7b6445b60679da826210cdde29eaf8b)) #1 SMP PREEMPT_DYNAMIC Tue Oct 15 19:00:21 MST 2024 ... [ 0.631002] Freeing unused kernel image (initmem) memory: 936K [ 0.631613] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: free_initmem+0x95/0x98 [ 0.632606] CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.12.0-rc3-00044-g2f87d0916ce0 #1 [ 0.633467] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014 [ 0.634583] Call Trace: [ 0.634837] panic+0xd4/0x2cc [ 0.635146] ? _vdso_rng_data+0xd80/0xd80 [ 0.635550] ? _vdso_rng_data+0xd80/0xd80 [ 0.635965] ? rest_init+0xb0/0xb0 [ 0.636312] __stack_chk_fail+0x10/0x10 [ 0.636701] ? free_initmem+0x95/0x98 [ 0.637074] free_initmem+0x95/0x98 [ 0.637434] ? _vdso_rng_data+0xd80/0xd80 [ 0.637838] ? rest_init+0xb0/0xb0 [ 0.638196] kernel_init+0x42/0x1e4 [ 0.638558] ret_from_fork+0x2b/0x40 [ 0.638922] ret_from_fork_asm+0x12/0x18 [ 0.639331] entry_INT80_32+0x108/0x108 [ 0.639864] Kernel Offset: disabled [ 0.640224] ---[ end Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: free_initmem+0x95/0x98 ]--- I can confirm that this patch resolves that issue for me and LKDTM's REPORT_STACK_CANARY test passes with that configuration. Reviewed-by: Nathan Chancellor <nathan@kernel.org> Tested-by: Nathan Chancellor <nathan@kernel.org> I presume the '#ifndef CONFIG_X86_64' in arch/x86/entry/entry.S is present because only X86_32 uses '-mstack-protector-guard-reg='? I assume that will disappear when X86_64 supports this option (IIRC that was the plan)? Cheers, Nathan
On Wed, 16 Oct 2024 at 04:10, Nathan Chancellor <nathan@kernel.org> wrote: > > On Tue, Oct 15, 2024 at 12:56:57PM +0200, Ard Biesheuvel wrote: > > On Mon, 14 Oct 2024 at 22:59, Kees Cook <kees@kernel.org> wrote: > > > > > > On Wed, Oct 09, 2024 at 02:43:53PM +0200, Ard Biesheuvel wrote: > > > > However, if a non-TLS definition of the symbol in question is visible in > > > > the same compilation unit (which amounts to the whole of vmlinux if LTO > > > > is enabled), it will drop the per-CPU prefix and emit a load from a > > > > bogus address. > > > > > > I take this to mean that x86 32-bit kernels built with the stack > > > protector and using Clang LTO will crash very quickly? > > > > > > > Yeah. The linked issue is not quite clear, but it does suggest things > > are pretty broken in that case. > > Yeah, i386_defconfig with CONFIG_LTO_CLANG_FULL=y explodes on boot for > me without this change: > > [ 0.000000] Linux version 6.12.0-rc3-00044-g2f87d0916ce0 (nathan@thelio-3990X) (ClangBuiltLinux clang version 19.1.2 (https://github.com/llvm/llvm-project.git 7ba7d8e2f7b6445b60679da826210cdde29eaf8b), ClangBuiltLinux LLD 19.1.2 (https://github.com/llvm/llvm-project.git 7ba7d8e2f7b6445b60679da826210cdde29eaf8b)) #1 SMP PREEMPT_DYNAMIC Tue Oct 15 19:00:21 MST 2024 > ... > [ 0.631002] Freeing unused kernel image (initmem) memory: 936K > [ 0.631613] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: free_initmem+0x95/0x98 > [ 0.632606] CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.12.0-rc3-00044-g2f87d0916ce0 #1 > [ 0.633467] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014 > [ 0.634583] Call Trace: > [ 0.634837] panic+0xd4/0x2cc > [ 0.635146] ? _vdso_rng_data+0xd80/0xd80 > [ 0.635550] ? _vdso_rng_data+0xd80/0xd80 > [ 0.635965] ? rest_init+0xb0/0xb0 > [ 0.636312] __stack_chk_fail+0x10/0x10 > [ 0.636701] ? free_initmem+0x95/0x98 > [ 0.637074] free_initmem+0x95/0x98 > [ 0.637434] ? _vdso_rng_data+0xd80/0xd80 > [ 0.637838] ? rest_init+0xb0/0xb0 > [ 0.638196] kernel_init+0x42/0x1e4 > [ 0.638558] ret_from_fork+0x2b/0x40 > [ 0.638922] ret_from_fork_asm+0x12/0x18 > [ 0.639331] entry_INT80_32+0x108/0x108 > [ 0.639864] Kernel Offset: disabled > [ 0.640224] ---[ end Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: free_initmem+0x95/0x98 ]--- > > I can confirm that this patch resolves that issue for me and LKDTM's > REPORT_STACK_CANARY test passes with that configuration. > > Reviewed-by: Nathan Chancellor <nathan@kernel.org> > Tested-by: Nathan Chancellor <nathan@kernel.org> > Thanks. > I presume the '#ifndef CONFIG_X86_64' in arch/x86/entry/entry.S is > present because only X86_32 uses '-mstack-protector-guard-reg='? I > assume that will disappear when X86_64 supports this option (IIRC that > was the plan)? > Yes, I noticed this issue while enabling '-mstack-protector-guard-reg=' for x86_64, but i386 is already broken.
diff --git a/arch/x86/Makefile b/arch/x86/Makefile index cd75e78a06c1..5b773b34768d 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -142,9 +142,10 @@ ifeq ($(CONFIG_X86_32),y) ifeq ($(CONFIG_STACKPROTECTOR),y) ifeq ($(CONFIG_SMP),y) - KBUILD_CFLAGS += -mstack-protector-guard-reg=fs -mstack-protector-guard-symbol=__stack_chk_guard + KBUILD_CFLAGS += -mstack-protector-guard-reg=fs \ + -mstack-protector-guard-symbol=__ref_stack_chk_guard else - KBUILD_CFLAGS += -mstack-protector-guard=global + KBUILD_CFLAGS += -mstack-protector-guard=global endif endif else diff --git a/arch/x86/entry/entry.S b/arch/x86/entry/entry.S index d9feadffa972..a503e6d535f8 100644 --- a/arch/x86/entry/entry.S +++ b/arch/x86/entry/entry.S @@ -46,3 +46,19 @@ EXPORT_SYMBOL_GPL(mds_verw_sel); .popsection THUNK warn_thunk_thunk, __warn_thunk + +#ifndef CONFIG_X86_64 +/* + * Clang's implementation of TLS stack cookies requires the variable in + * question to be a TLS variable. If the variable happens to be defined as an + * ordinary variable with external linkage in the same compilation unit (which + * amounts to the whole of vmlinux with LTO enabled), Clang will drop the + * segment register prefix from the references, resulting in broken code. Work + * around this by avoiding the symbol used in -mstack-protector-guard-symbol= + * entirely in the C code, and use an alias emitted by the linker script + * instead. + */ +#ifdef CONFIG_STACKPROTECTOR +EXPORT_SYMBOL(__ref_stack_chk_guard); +#endif +#endif diff --git a/arch/x86/include/asm/asm-prototypes.h b/arch/x86/include/asm/asm-prototypes.h index 25466c4d2134..3674006e3974 100644 --- a/arch/x86/include/asm/asm-prototypes.h +++ b/arch/x86/include/asm/asm-prototypes.h @@ -20,3 +20,6 @@ extern void cmpxchg8b_emu(void); #endif +#if defined(__GENKSYMS__) && defined(CONFIG_STACKPROTECTOR) +extern unsigned long __ref_stack_chk_guard; +#endif diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 07a34d723505..ba83f54dfaa8 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -2085,8 +2085,10 @@ void syscall_init(void) #ifdef CONFIG_STACKPROTECTOR DEFINE_PER_CPU(unsigned long, __stack_chk_guard); +#ifndef CONFIG_SMP EXPORT_PER_CPU_SYMBOL(__stack_chk_guard); #endif +#endif #endif /* CONFIG_X86_64 */ diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index 2b7c8c14c6fd..a80ad2bf8da4 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -490,6 +490,9 @@ SECTIONS . = ASSERT((_end - LOAD_OFFSET <= KERNEL_IMAGE_SIZE), "kernel image bigger than KERNEL_IMAGE_SIZE"); +/* needed for Clang - see arch/x86/entry/entry.S */ +PROVIDE(__ref_stack_chk_guard = __stack_chk_guard); + #ifdef CONFIG_X86_64 /* * Per-cpu symbols which need to be offset from __per_cpu_load