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

x86/elf: Support a new ELF aux vector AT_MINSIGSTKSZ

Historically, signal.h defines MINSIGSTKSZ (2KB) and SIGSTKSZ (8KB), for
use by all architectures with sigaltstack(2). Over time, the hardware state
size grew, but these constants did not evolve. Today, literal use of these
constants on several architectures may result in signal stack overflow, and
thus user data corruption.

A few years ago, the ARM team addressed this issue by establishing
getauxval(AT_MINSIGSTKSZ). This enables the kernel to supply a value
at runtime that is an appropriate replacement on current and future
hardware.

Add getauxval(AT_MINSIGSTKSZ) support to x86, analogous to the support
added for ARM in

94b07c1f8c39 ("arm64: signal: Report signal frame size to userspace via auxv").

Also, include a documentation to describe x86-specific auxiliary vectors.

Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Len Brown <len.brown@intel.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20210518200320.17239-4-chang.seok.bae@intel.com

authored by

Chang S. Bae and committed by
Borislav Petkov
1c33bb05 939ef713

+65 -2
+53
Documentation/x86/elf_auxvec.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ================================== 4 + x86-specific ELF Auxiliary Vectors 5 + ================================== 6 + 7 + This document describes the semantics of the x86 auxiliary vectors. 8 + 9 + Introduction 10 + ============ 11 + 12 + ELF Auxiliary vectors enable the kernel to efficiently provide 13 + configuration-specific parameters to userspace. In this example, a program 14 + allocates an alternate stack based on the kernel-provided size:: 15 + 16 + #include <sys/auxv.h> 17 + #include <elf.h> 18 + #include <signal.h> 19 + #include <stdlib.h> 20 + #include <assert.h> 21 + #include <err.h> 22 + 23 + #ifndef AT_MINSIGSTKSZ 24 + #define AT_MINSIGSTKSZ 51 25 + #endif 26 + 27 + .... 28 + stack_t ss; 29 + 30 + ss.ss_sp = malloc(ss.ss_size); 31 + assert(ss.ss_sp); 32 + 33 + ss.ss_size = getauxval(AT_MINSIGSTKSZ) + SIGSTKSZ; 34 + ss.ss_flags = 0; 35 + 36 + if (sigaltstack(&ss, NULL)) 37 + err(1, "sigaltstack"); 38 + 39 + 40 + The exposed auxiliary vectors 41 + ============================= 42 + 43 + AT_SYSINFO is used for locating the vsyscall entry point. It is not 44 + exported on 64-bit mode. 45 + 46 + AT_SYSINFO_EHDR is the start address of the page containing the vDSO. 47 + 48 + AT_MINSIGSTKSZ denotes the minimum stack size required by the kernel to 49 + deliver a signal to user-space. AT_MINSIGSTKSZ comprehends the space 50 + consumed by the kernel to accommodate the user context for the current 51 + hardware configuration. It does not comprehend subsequent user-space stack 52 + consumption, which must be added by the user. (e.g. Above, user-space adds 53 + SIGSTKSZ to AT_MINSIGSTKSZ.)
+1
Documentation/x86/index.rst
··· 35 35 sva 36 36 sgx 37 37 features 38 + elf_auxvec
+4
arch/x86/include/asm/elf.h
··· 312 312 NEW_AUX_ENT(AT_SYSINFO, VDSO_ENTRY); \ 313 313 NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE); \ 314 314 } \ 315 + NEW_AUX_ENT(AT_MINSIGSTKSZ, get_sigframe_size()); \ 315 316 } while (0) 316 317 317 318 /* ··· 329 328 extern unsigned long task_size_64bit(int full_addr_space); 330 329 extern unsigned long get_mmap_base(int is_legacy); 331 330 extern bool mmap_address_hint_valid(unsigned long addr, unsigned long len); 331 + extern unsigned long get_sigframe_size(void); 332 332 333 333 #ifdef CONFIG_X86_32 334 334 ··· 351 349 if (vdso64_enabled) \ 352 350 NEW_AUX_ENT(AT_SYSINFO_EHDR, \ 353 351 (unsigned long __force)current->mm->context.vdso); \ 352 + NEW_AUX_ENT(AT_MINSIGSTKSZ, get_sigframe_size()); \ 354 353 } while (0) 355 354 356 355 /* As a historical oddity, the x32 and x86_64 vDSOs are controlled together. */ ··· 360 357 if (vdso64_enabled) \ 361 358 NEW_AUX_ENT(AT_SYSINFO_EHDR, \ 362 359 (unsigned long __force)current->mm->context.vdso); \ 360 + NEW_AUX_ENT(AT_MINSIGSTKSZ, get_sigframe_size()); \ 363 361 } while (0) 364 362 365 363 #define AT_SYSINFO 32
+2 -2
arch/x86/include/uapi/asm/auxvec.h
··· 12 12 13 13 /* entries in ARCH_DLINFO: */ 14 14 #if defined(CONFIG_IA32_EMULATION) || !defined(CONFIG_X86_64) 15 - # define AT_VECTOR_SIZE_ARCH 2 15 + # define AT_VECTOR_SIZE_ARCH 3 16 16 #else /* else it's non-compat x86-64 */ 17 - # define AT_VECTOR_SIZE_ARCH 1 17 + # define AT_VECTOR_SIZE_ARCH 2 18 18 #endif 19 19 20 20 #endif /* _ASM_X86_AUXVEC_H */
+5
arch/x86/kernel/signal.c
··· 718 718 pr_info("max sigframe size: %lu\n", max_frame_size); 719 719 } 720 720 721 + unsigned long get_sigframe_size(void) 722 + { 723 + return max_frame_size; 724 + } 725 + 721 726 static inline int is_ia32_compat_frame(struct ksignal *ksig) 722 727 { 723 728 return IS_ENABLED(CONFIG_IA32_EMULATION) &&