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

x86/signal: Introduce helpers to get the maximum signal frame size

Signal frames do not have a fixed format and can vary in size when a number
of things change: supported XSAVE features, 32 vs. 64-bit apps, etc.

Add support for a runtime method for userspace to dynamically discover
how large a signal stack needs to be.

Introduce a new variable, max_frame_size, and helper functions for the
calculation to be used in a new user interface. Set max_frame_size to a
system-wide worst-case value, instead of storing multiple app-specific
values.

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>
Acked-by: H.J. Lu <hjl.tools@gmail.com>
Link: https://lkml.kernel.org/r/20210518200320.17239-3-chang.seok.bae@intel.com

authored by

Chang S. Bae and committed by
Borislav Petkov
939ef713 7cd60e43

+83 -2
+2
arch/x86/include/asm/fpu/signal.h
··· 29 29 fpu__alloc_mathframe(unsigned long sp, int ia32_frame, 30 30 unsigned long *buf_fx, unsigned long *size); 31 31 32 + unsigned long fpu__get_fpstate_size(void); 33 + 32 34 extern void fpu__init_prepare_fx_sw_frame(void); 33 35 34 36 #endif /* _ASM_X86_FPU_SIGNAL_H */
+2
arch/x86/include/asm/sigframe.h
··· 85 85 86 86 #endif /* CONFIG_X86_64 */ 87 87 88 + void __init init_sigframe_size(void); 89 + 88 90 #endif /* _ASM_X86_SIGFRAME_H */
+3
arch/x86/kernel/cpu/common.c
··· 58 58 #include <asm/intel-family.h> 59 59 #include <asm/cpu_device_id.h> 60 60 #include <asm/uv/uv.h> 61 + #include <asm/sigframe.h> 61 62 62 63 #include "cpu.h" 63 64 ··· 1332 1331 sld_setup(c); 1333 1332 1334 1333 fpu__init_system(c); 1334 + 1335 + init_sigframe_size(); 1335 1336 1336 1337 #ifdef CONFIG_X86_32 1337 1338 /*
+19
arch/x86/kernel/fpu/signal.c
··· 507 507 508 508 return sp; 509 509 } 510 + 511 + unsigned long fpu__get_fpstate_size(void) 512 + { 513 + unsigned long ret = xstate_sigframe_size(); 514 + 515 + /* 516 + * This space is needed on (most) 32-bit kernels, or when a 32-bit 517 + * app is running on a 64-bit kernel. To keep things simple, just 518 + * assume the worst case and always include space for 'freg_state', 519 + * even for 64-bit apps on 64-bit kernels. This wastes a bit of 520 + * space, but keeps the code simple. 521 + */ 522 + if ((IS_ENABLED(CONFIG_IA32_EMULATION) || 523 + IS_ENABLED(CONFIG_X86_32)) && use_fxsr()) 524 + ret += sizeof(struct fregs_state); 525 + 526 + return ret; 527 + } 528 + 510 529 /* 511 530 * Prepare the SW reserved portion of the fxsave memory layout, indicating 512 531 * the presence of the extended state information in the memory layout
+57 -2
arch/x86/kernel/signal.c
··· 212 212 * Set up a signal frame. 213 213 */ 214 214 215 + /* x86 ABI requires 16-byte alignment */ 216 + #define FRAME_ALIGNMENT 16UL 217 + 218 + #define MAX_FRAME_PADDING (FRAME_ALIGNMENT - 1) 219 + 215 220 /* 216 221 * Determine which stack to use.. 217 222 */ ··· 227 222 * Align the stack pointer according to the i386 ABI, 228 223 * i.e. so that on function entry ((sp + 4) & 15) == 0. 229 224 */ 230 - sp = ((sp + 4) & -16ul) - 4; 225 + sp = ((sp + 4) & -FRAME_ALIGNMENT) - 4; 231 226 #else /* !CONFIG_X86_32 */ 232 - sp = round_down(sp, 16) - 8; 227 + sp = round_down(sp, FRAME_ALIGNMENT) - 8; 233 228 #endif 234 229 return sp; 235 230 } ··· 666 661 badframe: 667 662 signal_fault(regs, frame, "rt_sigreturn"); 668 663 return 0; 664 + } 665 + 666 + /* 667 + * There are four different struct types for signal frame: sigframe_ia32, 668 + * rt_sigframe_ia32, rt_sigframe_x32, and rt_sigframe. Use the worst case 669 + * -- the largest size. It means the size for 64-bit apps is a bit more 670 + * than needed, but this keeps the code simple. 671 + */ 672 + #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) 673 + # define MAX_FRAME_SIGINFO_UCTXT_SIZE sizeof(struct sigframe_ia32) 674 + #else 675 + # define MAX_FRAME_SIGINFO_UCTXT_SIZE sizeof(struct rt_sigframe) 676 + #endif 677 + 678 + /* 679 + * The FP state frame contains an XSAVE buffer which must be 64-byte aligned. 680 + * If a signal frame starts at an unaligned address, extra space is required. 681 + * This is the max alignment padding, conservatively. 682 + */ 683 + #define MAX_XSAVE_PADDING 63UL 684 + 685 + /* 686 + * The frame data is composed of the following areas and laid out as: 687 + * 688 + * ------------------------- 689 + * | alignment padding | 690 + * ------------------------- 691 + * | (f)xsave frame | 692 + * ------------------------- 693 + * | fsave header | 694 + * ------------------------- 695 + * | alignment padding | 696 + * ------------------------- 697 + * | siginfo + ucontext | 698 + * ------------------------- 699 + */ 700 + 701 + /* max_frame_size tells userspace the worst case signal stack size. */ 702 + static unsigned long __ro_after_init max_frame_size; 703 + 704 + void __init init_sigframe_size(void) 705 + { 706 + max_frame_size = MAX_FRAME_SIGINFO_UCTXT_SIZE + MAX_FRAME_PADDING; 707 + 708 + max_frame_size += fpu__get_fpstate_size() + MAX_XSAVE_PADDING; 709 + 710 + /* Userspace expects an aligned size. */ 711 + max_frame_size = round_up(max_frame_size, FRAME_ALIGNMENT); 712 + 713 + pr_info("max sigframe size: %lu\n", max_frame_size); 669 714 } 670 715 671 716 static inline int is_ia32_compat_frame(struct ksignal *ksig)