linux-api.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/5] x86: Improve Minimum Alternate Stack Size
@ 2021-02-03 17:22 Chang S. Bae
  2021-02-03 17:22 ` [PATCH v5 1/5] uapi: Move the aux vector AT_MINSIGSTKSZ define to uapi Chang S. Bae
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Chang S. Bae @ 2021-02-03 17:22 UTC (permalink / raw)
  To: bp, tglx, mingo, luto, x86
  Cc: len.brown, dave.hansen, hjl.tools, Dave.Martin, jannh, mpe,
	carlos, tony.luck, ravi.v.shankar, libc-alpha, linux-arch,
	linux-api, linux-kernel, chang.seok.bae

During signal entry, the kernel pushes data onto the normal userspace
stack. On x86, the data pushed onto the user stack includes XSAVE state,
which has grown over time as new features and larger registers have been
added to the architecture.

MINSIGSTKSZ is a constant provided in the kernel signal.h headers and
typically distributed in lib-dev(el) packages, e.g. [1]. Its value is
compiled into programs and is part of the user/kernel ABI. The MINSIGSTKSZ
constant indicates to userspace how much data the kernel expects to push on
the user stack, [2][3].

However, this constant is much too small and does not reflect recent
additions to the architecture. For instance, when AVX-512 states are in
use, the signal frame size can be 3.5KB while MINSIGSTKSZ remains 2KB.

The bug report [4] explains this as an ABI issue. The small MINSIGSTKSZ can
cause user stack overflow when delivering a signal.

In this series, we suggest a couple of things:
1. Provide a variable minimum stack size to userspace, as a similar
   approach to [5]
2. Avoid using a too-small alternate stack

Changes from v4 [9]:
* Moved the aux vector define to the generic header (Carlos O'Donell)

Changes from v3 [8]:
* Updated the changelog (Borislav Petkov)
* Revised the test messages again (Borislav Petkov)

Changes from v2 [7]:
* Simplified the sigaltstack overflow prevention (Jann Horn)
* Renamed fpstate size helper with cleanup (Borislav Petkov)
* Cleaned up the signframe struct size defines (Borislav Petkov)
* Revised the selftest messages (Borislav Petkov)
* Revised a changelog (Borislav Petkov)

Changes from v1 [6]:
* Took stack alignment into account for sigframe size (Dave Martin)

[1]: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/bits/sigstack.h;h=b9dca794da09
3dc4d41d39db9851d444e1b54d9b;hb=HEAD
[2]: https://www.gnu.org/software/libc/manual/html_node/Signal-Stack.html
[3]: https://man7.org/linux/man-pages/man2/sigaltstack.2.html
[4]: https://bugzilla.kernel.org/show_bug.cgi?id=153531
[5]: https://blog.linuxplumbersconf.org/2017/ocw/system/presentations/4671/original/plumbers-dm-2017.pdf
[6]: https://lore.kernel.org/lkml/20200929205746.6763-1-chang.seok.bae@intel.com/
[7]: https://lore.kernel.org/lkml/20201119190237.626-1-chang.seok.bae@intel.com/
[8]: https://lore.kernel.org/lkml/20201223015312.4882-1-chang.seok.bae@intel.com/
[9]: https://lore.kernel.org/lkml/20210115211038.2072-1-chang.seok.bae@intel.com/

Chang S. Bae (5):
  uapi: Move the aux vector AT_MINSIGSTKSZ define to uapi
  x86/signal: Introduce helpers to get the maximum signal frame size
  x86/elf: Support a new ELF aux vector AT_MINSIGSTKSZ
  x86/signal: Detect and prevent an alternate signal stack overflow
  selftest/x86/signal: Include test cases for validating sigaltstack

 arch/arm64/include/uapi/asm/auxvec.h      |   1 -
 arch/x86/include/asm/elf.h                |   4 +
 arch/x86/include/asm/fpu/signal.h         |   2 +
 arch/x86/include/asm/sigframe.h           |   2 +
 arch/x86/include/uapi/asm/auxvec.h        |   4 +-
 arch/x86/kernel/cpu/common.c              |   3 +
 arch/x86/kernel/fpu/signal.c              |  19 ++++
 arch/x86/kernel/signal.c                  |  69 +++++++++++-
 include/uapi/linux/auxvec.h               |   1 +
 tools/testing/selftests/x86/Makefile      |   2 +-
 tools/testing/selftests/x86/sigaltstack.c | 128 ++++++++++++++++++++++
 11 files changed, 227 insertions(+), 8 deletions(-)
 create mode 100644 tools/testing/selftests/x86/sigaltstack.c

-- 
2.17.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v5 1/5] uapi: Move the aux vector AT_MINSIGSTKSZ define to uapi
  2021-02-03 17:22 [PATCH v5 0/5] x86: Improve Minimum Alternate Stack Size Chang S. Bae
@ 2021-02-03 17:22 ` Chang S. Bae
  2021-02-04 15:55   ` Dave Martin
  2021-02-03 17:22 ` [PATCH v5 2/5] x86/signal: Introduce helpers to get the maximum signal frame size Chang S. Bae
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Chang S. Bae @ 2021-02-03 17:22 UTC (permalink / raw)
  To: bp, tglx, mingo, luto, x86
  Cc: len.brown, dave.hansen, hjl.tools, Dave.Martin, jannh, mpe,
	carlos, tony.luck, ravi.v.shankar, libc-alpha, linux-arch,
	linux-api, linux-kernel, chang.seok.bae, linux-arm-kernel

Move the AT_MINSIGSTKSZ definition to generic Linux from arm64. It is
already used as generic ABI in glibc's generic elf.h, and this move will
prevent future namespace conflicts. In particular, x86 will re-use this
generic definition.

Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Reviewed-by: Len Brown <len.brown@intel.com>
Cc: Carlos O'Donell <carlos@redhat.com>
Cc: Dave Martin <Dave.Martin@arm.com>
Cc: libc-alpha@sourceware.org
Cc: linux-arch@vger.kernel.org
Cc: linux-api@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
Change from v4:
* Added as a new patch (Carlos O'Donell)
---
 arch/arm64/include/uapi/asm/auxvec.h | 1 -
 include/uapi/linux/auxvec.h          | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/include/uapi/asm/auxvec.h b/arch/arm64/include/uapi/asm/auxvec.h
index 743c0b84fd30..767d710c92aa 100644
--- a/arch/arm64/include/uapi/asm/auxvec.h
+++ b/arch/arm64/include/uapi/asm/auxvec.h
@@ -19,7 +19,6 @@
 
 /* vDSO location */
 #define AT_SYSINFO_EHDR	33
-#define AT_MINSIGSTKSZ	51	/* stack needed for signal delivery */
 
 #define AT_VECTOR_SIZE_ARCH 2 /* entries in ARCH_DLINFO */
 
diff --git a/include/uapi/linux/auxvec.h b/include/uapi/linux/auxvec.h
index abe5f2b6581b..cc4fa77bd2a7 100644
--- a/include/uapi/linux/auxvec.h
+++ b/include/uapi/linux/auxvec.h
@@ -33,5 +33,6 @@
 
 #define AT_EXECFN  31	/* filename of program */
 
+#define AT_MINSIGSTKSZ	51	/* stack needed for signal delivery  */
 
 #endif /* _UAPI_LINUX_AUXVEC_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5 2/5] x86/signal: Introduce helpers to get the maximum signal frame size
  2021-02-03 17:22 [PATCH v5 0/5] x86: Improve Minimum Alternate Stack Size Chang S. Bae
  2021-02-03 17:22 ` [PATCH v5 1/5] uapi: Move the aux vector AT_MINSIGSTKSZ define to uapi Chang S. Bae
@ 2021-02-03 17:22 ` Chang S. Bae
  2021-02-03 17:22 ` [PATCH v5 3/5] x86/elf: Support a new ELF aux vector AT_MINSIGSTKSZ Chang S. Bae
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Chang S. Bae @ 2021-02-03 17:22 UTC (permalink / raw)
  To: bp, tglx, mingo, luto, x86
  Cc: len.brown, dave.hansen, hjl.tools, Dave.Martin, jannh, mpe,
	carlos, tony.luck, ravi.v.shankar, libc-alpha, linux-arch,
	linux-api, linux-kernel, chang.seok.bae, Borislav Petkov

Signal frames do not have a fixed format and can vary in size when a number
of things change: support XSAVE features, 32 vs. 64-bit apps. Add the code
to support 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>
Reviewed-by: Len Brown <len.brown@intel.com>
Acked-by: H.J. Lu <hjl.tools@gmail.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: x86@kernel.org
Cc: linux-kernel@vger.kernel.org
---
Changes from v2:
* Renamed the fpstate size helper with cleanup (Borislav Petkov)
* Moved the sigframe struct size defines to where used (Borislav Petkov)
* Removed unneeded sentence in the changelog (Borislav Petkov)

Change from v1:
* Took stack alignment into account for sigframe size (Dave Martin)
---
 arch/x86/include/asm/fpu/signal.h |  2 ++
 arch/x86/include/asm/sigframe.h   |  2 ++
 arch/x86/kernel/cpu/common.c      |  3 ++
 arch/x86/kernel/fpu/signal.c      | 19 +++++++++++
 arch/x86/kernel/signal.c          | 57 +++++++++++++++++++++++++++++--
 5 files changed, 81 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/asm/fpu/signal.h b/arch/x86/include/asm/fpu/signal.h
index 7fb516b6893a..8b6631dffefd 100644
--- a/arch/x86/include/asm/fpu/signal.h
+++ b/arch/x86/include/asm/fpu/signal.h
@@ -29,6 +29,8 @@ unsigned long
 fpu__alloc_mathframe(unsigned long sp, int ia32_frame,
 		     unsigned long *buf_fx, unsigned long *size);
 
+unsigned long fpu__get_fpstate_size(void);
+
 extern void fpu__init_prepare_fx_sw_frame(void);
 
 #endif /* _ASM_X86_FPU_SIGNAL_H */
diff --git a/arch/x86/include/asm/sigframe.h b/arch/x86/include/asm/sigframe.h
index 84eab2724875..5b1ed650b124 100644
--- a/arch/x86/include/asm/sigframe.h
+++ b/arch/x86/include/asm/sigframe.h
@@ -85,4 +85,6 @@ struct rt_sigframe_x32 {
 
 #endif /* CONFIG_X86_64 */
 
+void __init init_sigframe_size(void);
+
 #endif /* _ASM_X86_SIGFRAME_H */
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 35ad8480c464..6954932272d5 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -58,6 +58,7 @@
 #include <asm/intel-family.h>
 #include <asm/cpu_device_id.h>
 #include <asm/uv/uv.h>
+#include <asm/sigframe.h>
 
 #include "cpu.h"
 
@@ -1331,6 +1332,8 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
 
 	fpu__init_system(c);
 
+	init_sigframe_size();
+
 #ifdef CONFIG_X86_32
 	/*
 	 * Regardless of whether PCID is enumerated, the SDM says
diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c
index a4ec65317a7f..dbb304e48f16 100644
--- a/arch/x86/kernel/fpu/signal.c
+++ b/arch/x86/kernel/fpu/signal.c
@@ -507,6 +507,25 @@ fpu__alloc_mathframe(unsigned long sp, int ia32_frame,
 
 	return sp;
 }
+
+unsigned long fpu__get_fpstate_size(void)
+{
+	unsigned long ret = xstate_sigframe_size();
+
+	/*
+	 * This space is needed on (most) 32-bit kernels, or when a 32-bit
+	 * app is running on a 64-bit kernel. To keep things simple, just
+	 * assume the worst case and always include space for 'freg_state',
+	 * even for 64-bit apps on 64-bit kernels. This wastes a bit of
+	 * space, but keeps the code simple.
+	 */
+	if ((IS_ENABLED(CONFIG_IA32_EMULATION) ||
+	     IS_ENABLED(CONFIG_X86_32)) && use_fxsr())
+		ret += sizeof(struct fregs_state);
+
+	return ret;
+}
+
 /*
  * Prepare the SW reserved portion of the fxsave memory layout, indicating
  * the presence of the extended state information in the memory layout
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index ea794a083c44..800243afd1ef 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -212,6 +212,11 @@ do {									\
  * Set up a signal frame.
  */
 
+/* x86 ABI requires 16-byte alignment */
+#define FRAME_ALIGNMENT	16UL
+
+#define MAX_FRAME_PADDING	(FRAME_ALIGNMENT - 1)
+
 /*
  * Determine which stack to use..
  */
@@ -222,9 +227,9 @@ static unsigned long align_sigframe(unsigned long sp)
 	 * Align the stack pointer according to the i386 ABI,
 	 * i.e. so that on function entry ((sp + 4) & 15) == 0.
 	 */
-	sp = ((sp + 4) & -16ul) - 4;
+	sp = ((sp + 4) & -FRAME_ALIGNMENT) - 4;
 #else /* !CONFIG_X86_32 */
-	sp = round_down(sp, 16) - 8;
+	sp = round_down(sp, FRAME_ALIGNMENT) - 8;
 #endif
 	return sp;
 }
@@ -663,6 +668,54 @@ SYSCALL_DEFINE0(rt_sigreturn)
 	return 0;
 }
 
+/*
+ * There are four different struct types for signal frame: sigframe_ia32,
+ * rt_sigframe_ia32, rt_sigframe_x32, and rt_sigframe. Use the worst case
+ * -- the largest size. It means the size for 64-bit apps is a bit more
+ * than needed, but this keeps the code simple.
+ */
+#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
+# define MAX_FRAME_SIGINFO_UCTXT_SIZE	sizeof(struct sigframe_ia32)
+#else
+# define MAX_FRAME_SIGINFO_UCTXT_SIZE	sizeof(struct rt_sigframe)
+#endif
+
+/*
+ * The FP state frame contains an XSAVE buffer which must be 64-byte aligned.
+ * If a signal frame starts at an unaligned address, extra space is required.
+ * This is the max alignment padding, conservatively.
+ */
+#define MAX_XSAVE_PADDING	63UL
+
+/*
+ * The frame data is composed of the following areas and laid out as:
+ *
+ * -------------------------
+ * | alignment padding     |
+ * -------------------------
+ * | (f)xsave frame        |
+ * -------------------------
+ * | fsave header          |
+ * -------------------------
+ * | alignment padding     |
+ * -------------------------
+ * | siginfo + ucontext    |
+ * -------------------------
+ */
+
+/* max_frame_size tells userspace the worst case signal stack size. */
+static unsigned long __ro_after_init max_frame_size;
+
+void __init init_sigframe_size(void)
+{
+	max_frame_size = MAX_FRAME_SIGINFO_UCTXT_SIZE + MAX_FRAME_PADDING;
+
+	max_frame_size += fpu__get_fpstate_size() + MAX_XSAVE_PADDING;
+
+	/* Userspace expects an aligned size. */
+	max_frame_size = round_up(max_frame_size, FRAME_ALIGNMENT);
+}
+
 static inline int is_ia32_compat_frame(struct ksignal *ksig)
 {
 	return IS_ENABLED(CONFIG_IA32_EMULATION) &&
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5 3/5] x86/elf: Support a new ELF aux vector AT_MINSIGSTKSZ
  2021-02-03 17:22 [PATCH v5 0/5] x86: Improve Minimum Alternate Stack Size Chang S. Bae
  2021-02-03 17:22 ` [PATCH v5 1/5] uapi: Move the aux vector AT_MINSIGSTKSZ define to uapi Chang S. Bae
  2021-02-03 17:22 ` [PATCH v5 2/5] x86/signal: Introduce helpers to get the maximum signal frame size Chang S. Bae
@ 2021-02-03 17:22 ` Chang S. Bae
  2021-02-03 17:22 ` [PATCH v5 4/5] x86/signal: Detect and prevent an alternate signal stack overflow Chang S. Bae
  2021-02-03 17:22 ` [PATCH v5 5/5] selftest/x86/signal: Include test cases for validating sigaltstack Chang S. Bae
  4 siblings, 0 replies; 9+ messages in thread
From: Chang S. Bae @ 2021-02-03 17:22 UTC (permalink / raw)
  To: bp, tglx, mingo, luto, x86
  Cc: len.brown, dave.hansen, hjl.tools, Dave.Martin, jannh, mpe,
	carlos, tony.luck, ravi.v.shankar, libc-alpha, linux-arch,
	linux-api, linux-kernel, chang.seok.bae, Fenghua Yu

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), such that the kernel can supply at runtime value
that is an appropriate replacement on the current and future hardware.

Add getauxval(AT_MINSIGSTKSZ) support to x86, analogous to the support
added for ARM in commit 94b07c1f8c39 ("arm64: signal: Report signal frame
size to userspace via auxv").

Reported-by: Florian Weimer <fweimer@redhat.com>
Fixes: c2bc11f10a39 ("x86, AVX-512: Enable AVX-512 States Context Switch")
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Reviewed-by: Len Brown <len.brown@intel.com>
Cc: H.J. Lu <hjl.tools@gmail.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: Dave Martin <Dave.Martin@arm.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: x86@kernel.org
Cc: libc-alpha@sourceware.org
Cc: linux-arch@vger.kernel.org
Cc: linux-api@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Link: https://bugzilla.kernel.org/show_bug.cgi?id=153531
---
 arch/x86/include/asm/elf.h         | 4 ++++
 arch/x86/include/uapi/asm/auxvec.h | 4 ++--
 arch/x86/kernel/signal.c           | 5 +++++
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index 66bdfe838d61..cd10795c178e 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -312,6 +312,7 @@ do {									\
 		NEW_AUX_ENT(AT_SYSINFO,	VDSO_ENTRY);			\
 		NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE);	\
 	}								\
+	NEW_AUX_ENT(AT_MINSIGSTKSZ, get_sigframe_size());			\
 } while (0)
 
 /*
@@ -328,6 +329,7 @@ extern unsigned long task_size_32bit(void);
 extern unsigned long task_size_64bit(int full_addr_space);
 extern unsigned long get_mmap_base(int is_legacy);
 extern bool mmap_address_hint_valid(unsigned long addr, unsigned long len);
+extern unsigned long get_sigframe_size(void);
 
 #ifdef CONFIG_X86_32
 
@@ -349,6 +351,7 @@ do {									\
 	if (vdso64_enabled)						\
 		NEW_AUX_ENT(AT_SYSINFO_EHDR,				\
 			    (unsigned long __force)current->mm->context.vdso); \
+	NEW_AUX_ENT(AT_MINSIGSTKSZ, get_sigframe_size());			\
 } while (0)
 
 /* As a historical oddity, the x32 and x86_64 vDSOs are controlled together. */
@@ -357,6 +360,7 @@ do {									\
 	if (vdso64_enabled)						\
 		NEW_AUX_ENT(AT_SYSINFO_EHDR,				\
 			    (unsigned long __force)current->mm->context.vdso); \
+	NEW_AUX_ENT(AT_MINSIGSTKSZ, get_sigframe_size());			\
 } while (0)
 
 #define AT_SYSINFO		32
diff --git a/arch/x86/include/uapi/asm/auxvec.h b/arch/x86/include/uapi/asm/auxvec.h
index 580e3c567046..6beb55bbefa4 100644
--- a/arch/x86/include/uapi/asm/auxvec.h
+++ b/arch/x86/include/uapi/asm/auxvec.h
@@ -12,9 +12,9 @@
 
 /* entries in ARCH_DLINFO: */
 #if defined(CONFIG_IA32_EMULATION) || !defined(CONFIG_X86_64)
-# define AT_VECTOR_SIZE_ARCH 2
+# define AT_VECTOR_SIZE_ARCH 3
 #else /* else it's non-compat x86-64 */
-# define AT_VECTOR_SIZE_ARCH 1
+# define AT_VECTOR_SIZE_ARCH 2
 #endif
 
 #endif /* _ASM_X86_AUXVEC_H */
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 800243afd1ef..0d24f64d0145 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -716,6 +716,11 @@ void __init init_sigframe_size(void)
 	max_frame_size = round_up(max_frame_size, FRAME_ALIGNMENT);
 }
 
+unsigned long get_sigframe_size(void)
+{
+	return max_frame_size;
+}
+
 static inline int is_ia32_compat_frame(struct ksignal *ksig)
 {
 	return IS_ENABLED(CONFIG_IA32_EMULATION) &&
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5 4/5] x86/signal: Detect and prevent an alternate signal stack overflow
  2021-02-03 17:22 [PATCH v5 0/5] x86: Improve Minimum Alternate Stack Size Chang S. Bae
                   ` (2 preceding siblings ...)
  2021-02-03 17:22 ` [PATCH v5 3/5] x86/elf: Support a new ELF aux vector AT_MINSIGSTKSZ Chang S. Bae
@ 2021-02-03 17:22 ` Chang S. Bae
  2021-02-19  1:25   ` Andy Lutomirski
  2021-02-03 17:22 ` [PATCH v5 5/5] selftest/x86/signal: Include test cases for validating sigaltstack Chang S. Bae
  4 siblings, 1 reply; 9+ messages in thread
From: Chang S. Bae @ 2021-02-03 17:22 UTC (permalink / raw)
  To: bp, tglx, mingo, luto, x86
  Cc: len.brown, dave.hansen, hjl.tools, Dave.Martin, jannh, mpe,
	carlos, tony.luck, ravi.v.shankar, libc-alpha, linux-arch,
	linux-api, linux-kernel, chang.seok.bae, Borislav Petkov

The kernel pushes context on to the userspace stack to prepare for the
user's signal handler. When the user has supplied an alternate signal
stack, via sigaltstack(2), it is easy for the kernel to verify that the
stack size is sufficient for the current hardware context.

Check if writing the hardware context to the alternate stack will exceed
it's size. If yes, then instead of corrupting user-data and proceeding with
the original signal handler, an immediate SIGSEGV signal is delivered.

While previous patches in this series allow new source code to discover and
use a sufficient alternate signal stack size, this check is still necessary
to protect binaries with insufficient alternate signal stack size from data
corruption.

Suggested-by: Jann Horn <jannh@google.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Reviewed-by: Len Brown <len.brown@intel.com>
Reviewed-by: Jann Horn <jannh@google.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Jann Horn <jannh@google.com>
Cc: x86@kernel.org
Cc: linux-kernel@vger.kernel.org
---
Changes from v3:
* Updated the changelog (Borislav Petkov)

Changes from v2:
* Simplified the implementation (Jann Horn)
---
 arch/x86/kernel/signal.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 0d24f64d0145..8e2df070dbfd 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -242,7 +242,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
 	unsigned long math_size = 0;
 	unsigned long sp = regs->sp;
 	unsigned long buf_fx = 0;
-	int onsigstack = on_sig_stack(sp);
+	bool onsigstack = on_sig_stack(sp);
 	int ret;
 
 	/* redzone */
@@ -251,8 +251,11 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
 
 	/* This is the X/Open sanctioned signal stack switching.  */
 	if (ka->sa.sa_flags & SA_ONSTACK) {
-		if (sas_ss_flags(sp) == 0)
+		if (sas_ss_flags(sp) == 0) {
 			sp = current->sas_ss_sp + current->sas_ss_size;
+			/* On the alternate signal stack */
+			onsigstack = true;
+		}
 	} else if (IS_ENABLED(CONFIG_X86_32) &&
 		   !onsigstack &&
 		   regs->ss != __USER_DS &&
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5 5/5] selftest/x86/signal: Include test cases for validating sigaltstack
  2021-02-03 17:22 [PATCH v5 0/5] x86: Improve Minimum Alternate Stack Size Chang S. Bae
                   ` (3 preceding siblings ...)
  2021-02-03 17:22 ` [PATCH v5 4/5] x86/signal: Detect and prevent an alternate signal stack overflow Chang S. Bae
@ 2021-02-03 17:22 ` Chang S. Bae
  4 siblings, 0 replies; 9+ messages in thread
From: Chang S. Bae @ 2021-02-03 17:22 UTC (permalink / raw)
  To: bp, tglx, mingo, luto, x86
  Cc: len.brown, dave.hansen, hjl.tools, Dave.Martin, jannh, mpe,
	carlos, tony.luck, ravi.v.shankar, libc-alpha, linux-arch,
	linux-api, linux-kernel, chang.seok.bae, Borislav Petkov,
	linux-kselftest

The test measures the kernel's signal delivery with different (enough vs.
insufficient) stack sizes.

Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Reviewed-by: Len Brown <len.brown@intel.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: x86@kernel.org
Cc: linux-kselftest@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
Changes from v3:
* Revised test messages again (Borislav Petkov)

Changes from v2:
* Revised test messages (Borislav Petkov)
---
 tools/testing/selftests/x86/Makefile      |   2 +-
 tools/testing/selftests/x86/sigaltstack.c | 128 ++++++++++++++++++++++
 2 files changed, 129 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/x86/sigaltstack.c

diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 333980375bc7..65bba2ae86ee 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -13,7 +13,7 @@ CAN_BUILD_WITH_NOPIE := $(shell ./check_cc.sh $(CC) trivial_program.c -no-pie)
 TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \
 			check_initial_reg_state sigreturn iopl ioperm \
 			test_vsyscall mov_ss_trap \
-			syscall_arg_fault fsgsbase_restore
+			syscall_arg_fault fsgsbase_restore sigaltstack
 TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \
 			test_FCMOV test_FCOMI test_FISTTP \
 			vdso_restorer
diff --git a/tools/testing/selftests/x86/sigaltstack.c b/tools/testing/selftests/x86/sigaltstack.c
new file mode 100644
index 000000000000..f689af75e979
--- /dev/null
+++ b/tools/testing/selftests/x86/sigaltstack.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define _GNU_SOURCE
+#include <signal.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/auxv.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <setjmp.h>
+
+/* sigaltstack()-enforced minimum stack */
+#define ENFORCED_MINSIGSTKSZ	2048
+
+#ifndef AT_MINSIGSTKSZ
+#  define AT_MINSIGSTKSZ	51
+#endif
+
+static int nerrs;
+
+static bool sigalrm_expected;
+
+static unsigned long at_minstack_size;
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+		       int flags)
+{
+	struct sigaction sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_sigaction = handler;
+	sa.sa_flags = SA_SIGINFO | flags;
+	sigemptyset(&sa.sa_mask);
+	if (sigaction(sig, &sa, 0))
+		err(1, "sigaction");
+}
+
+static void clearhandler(int sig)
+{
+	struct sigaction sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = SIG_DFL;
+	sigemptyset(&sa.sa_mask);
+	if (sigaction(sig, &sa, 0))
+		err(1, "sigaction");
+}
+
+static int setup_altstack(void *start, unsigned long size)
+{
+	stack_t ss;
+
+	memset(&ss, 0, sizeof(ss));
+	ss.ss_size = size;
+	ss.ss_sp = start;
+
+	return sigaltstack(&ss, NULL);
+}
+
+static jmp_buf jmpbuf;
+
+static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
+{
+	if (sigalrm_expected) {
+		printf("[FAIL]\tWrong signal delivered: SIGSEGV (expected SIGALRM).");
+		nerrs++;
+	} else {
+		printf("[OK]\tSIGSEGV signal delivered.\n");
+	}
+
+	siglongjmp(jmpbuf, 1);
+}
+
+static void sigalrm(int sig, siginfo_t *info, void *ctx_void)
+{
+	if (!sigalrm_expected) {
+		printf("[FAIL]\tWrong signal delivered: SIGALRM (expected SIGSEGV).");
+		nerrs++;
+	} else {
+		printf("[OK]\tSIGALRM signal delivered.\n");
+	}
+}
+
+static void test_sigaltstack(void *altstack, unsigned long size)
+{
+	if (setup_altstack(altstack, size))
+		err(1, "sigaltstack()");
+
+	sigalrm_expected = (size > at_minstack_size) ? true : false;
+
+	sethandler(SIGSEGV, sigsegv, 0);
+	sethandler(SIGALRM, sigalrm, SA_ONSTACK);
+
+	if (!sigsetjmp(jmpbuf, 1)) {
+		printf("[RUN]\tTest an alternate signal stack of %ssufficient size.\n",
+		       sigalrm_expected ? "" : "in");
+		printf("\tRaise SIGALRM. %s is expected to be delivered.\n",
+		       sigalrm_expected ? "It" : "SIGSEGV");
+		raise(SIGALRM);
+	}
+
+	clearhandler(SIGALRM);
+	clearhandler(SIGSEGV);
+}
+
+int main(void)
+{
+	void *altstack;
+
+	at_minstack_size = getauxval(AT_MINSIGSTKSZ);
+
+	altstack = mmap(NULL, at_minstack_size + SIGSTKSZ, PROT_READ | PROT_WRITE,
+			MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
+	if (altstack == MAP_FAILED)
+		err(1, "mmap()");
+
+	if ((ENFORCED_MINSIGSTKSZ + 1) < at_minstack_size)
+		test_sigaltstack(altstack, ENFORCED_MINSIGSTKSZ + 1);
+
+	test_sigaltstack(altstack, at_minstack_size + SIGSTKSZ);
+
+	return nerrs == 0 ? 0 : 1;
+}
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v5 1/5] uapi: Move the aux vector AT_MINSIGSTKSZ define to uapi
  2021-02-03 17:22 ` [PATCH v5 1/5] uapi: Move the aux vector AT_MINSIGSTKSZ define to uapi Chang S. Bae
@ 2021-02-04 15:55   ` Dave Martin
  2021-02-12 18:46     ` Will Deacon
  0 siblings, 1 reply; 9+ messages in thread
From: Dave Martin @ 2021-02-04 15:55 UTC (permalink / raw)
  To: Chang S. Bae
  Cc: bp, tglx, mingo, luto, x86, linux-arch, len.brown, tony.luck,
	libc-alpha, ravi.v.shankar, hjl.tools, carlos, mpe, jannh,
	linux-kernel, dave.hansen, linux-api, linux-arm-kernel

On Wed, Feb 03, 2021 at 09:22:38AM -0800, Chang S. Bae wrote:
> Move the AT_MINSIGSTKSZ definition to generic Linux from arm64. It is
> already used as generic ABI in glibc's generic elf.h, and this move will
> prevent future namespace conflicts. In particular, x86 will re-use this
> generic definition.
> 
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
> Reviewed-by: Len Brown <len.brown@intel.com>
> Cc: Carlos O'Donell <carlos@redhat.com>
> Cc: Dave Martin <Dave.Martin@arm.com>
> Cc: libc-alpha@sourceware.org
> Cc: linux-arch@vger.kernel.org
> Cc: linux-api@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> ---
> Change from v4:
> * Added as a new patch (Carlos O'Donell)
> ---
>  arch/arm64/include/uapi/asm/auxvec.h | 1 -
>  include/uapi/linux/auxvec.h          | 1 +
>  2 files changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/include/uapi/asm/auxvec.h b/arch/arm64/include/uapi/asm/auxvec.h
> index 743c0b84fd30..767d710c92aa 100644
> --- a/arch/arm64/include/uapi/asm/auxvec.h
> +++ b/arch/arm64/include/uapi/asm/auxvec.h
> @@ -19,7 +19,6 @@
>  
>  /* vDSO location */
>  #define AT_SYSINFO_EHDR	33
> -#define AT_MINSIGSTKSZ	51	/* stack needed for signal delivery */

Since this is UAPI, I'm wondering whether we should try to preserve this
definition for users of <asm/auxvec.h>.  (Indeed, it is not uncommon to
include <asm/> headers in userspace hackery, since the <linux/> headers
tend to interact badly with the the libc headers.)

In C11 at least, duplicate #defines are not an error if the definitions
are the same.  I don't know about the history, but I suspect this was
true for older standards too.  So maybe we can just keep this definition
with a duplicate definition in the common header.

Otherwise, we could have

#ifndef AT_MINSIGSTKSZ
#define AT_MINSIGSTKSZ 51
#endif

in include/linux/uapi/auxvec.h, and keep the arm64 header unchanged.

>  
>  #define AT_VECTOR_SIZE_ARCH 2 /* entries in ARCH_DLINFO */
>  
> diff --git a/include/uapi/linux/auxvec.h b/include/uapi/linux/auxvec.h
> index abe5f2b6581b..cc4fa77bd2a7 100644
> --- a/include/uapi/linux/auxvec.h
> +++ b/include/uapi/linux/auxvec.h
> @@ -33,5 +33,6 @@
>  
>  #define AT_EXECFN  31	/* filename of program */
>  
> +#define AT_MINSIGSTKSZ	51	/* stack needed for signal delivery  */
>  
>  #endif /* _UAPI_LINUX_AUXVEC_H */

Otherwise, this looks fine as a concept.

AFAICT, no other arch is already using the value 51.

If nobody else objects to the loss of the definition from arm64's
<asm/auxvec.h> then I guess I can put up with that -- but I will wait to
see if anyone gives a view first.

Cheers
---Dave

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v5 1/5] uapi: Move the aux vector AT_MINSIGSTKSZ define to uapi
  2021-02-04 15:55   ` Dave Martin
@ 2021-02-12 18:46     ` Will Deacon
  0 siblings, 0 replies; 9+ messages in thread
From: Will Deacon @ 2021-02-12 18:46 UTC (permalink / raw)
  To: Dave Martin
  Cc: Chang S. Bae, linux-arch, len.brown, tony.luck, libc-alpha,
	jannh, ravi.v.shankar, carlos, mpe, hjl.tools, x86, linux-kernel,
	dave.hansen, luto, linux-api, tglx, bp, mingo, linux-arm-kernel

On Thu, Feb 04, 2021 at 03:55:30PM +0000, Dave Martin wrote:
> On Wed, Feb 03, 2021 at 09:22:38AM -0800, Chang S. Bae wrote:
> > Move the AT_MINSIGSTKSZ definition to generic Linux from arm64. It is
> > already used as generic ABI in glibc's generic elf.h, and this move will
> > prevent future namespace conflicts. In particular, x86 will re-use this
> > generic definition.
> > 
> > Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
> > Reviewed-by: Len Brown <len.brown@intel.com>
> > Cc: Carlos O'Donell <carlos@redhat.com>
> > Cc: Dave Martin <Dave.Martin@arm.com>
> > Cc: libc-alpha@sourceware.org
> > Cc: linux-arch@vger.kernel.org
> > Cc: linux-api@vger.kernel.org
> > Cc: linux-arm-kernel@lists.infradead.org
> > Cc: linux-kernel@vger.kernel.org
> > ---
> > Change from v4:
> > * Added as a new patch (Carlos O'Donell)
> > ---
> >  arch/arm64/include/uapi/asm/auxvec.h | 1 -
> >  include/uapi/linux/auxvec.h          | 1 +
> >  2 files changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/arch/arm64/include/uapi/asm/auxvec.h b/arch/arm64/include/uapi/asm/auxvec.h
> > index 743c0b84fd30..767d710c92aa 100644
> > --- a/arch/arm64/include/uapi/asm/auxvec.h
> > +++ b/arch/arm64/include/uapi/asm/auxvec.h
> > @@ -19,7 +19,6 @@
> >  
> >  /* vDSO location */
> >  #define AT_SYSINFO_EHDR	33
> > -#define AT_MINSIGSTKSZ	51	/* stack needed for signal delivery */
> 
> Since this is UAPI, I'm wondering whether we should try to preserve this
> definition for users of <asm/auxvec.h>.  (Indeed, it is not uncommon to
> include <asm/> headers in userspace hackery, since the <linux/> headers
> tend to interact badly with the the libc headers.)
> 
> In C11 at least, duplicate #defines are not an error if the definitions
> are the same.  I don't know about the history, but I suspect this was
> true for older standards too.  So maybe we can just keep this definition
> with a duplicate definition in the common header.
> 
> Otherwise, we could have
> 
> #ifndef AT_MINSIGSTKSZ
> #define AT_MINSIGSTKSZ 51
> #endif
> 
> in include/linux/uapi/auxvec.h, and keep the arm64 header unchanged.

I think it just boils down to whether or not anything breaks. If it does,
then we'll have to revert the patch, so anything we can do now to minimise
that possibility would be good.

Will

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v5 4/5] x86/signal: Detect and prevent an alternate signal stack overflow
  2021-02-03 17:22 ` [PATCH v5 4/5] x86/signal: Detect and prevent an alternate signal stack overflow Chang S. Bae
@ 2021-02-19  1:25   ` Andy Lutomirski
  0 siblings, 0 replies; 9+ messages in thread
From: Andy Lutomirski @ 2021-02-19  1:25 UTC (permalink / raw)
  To: Chang S. Bae
  Cc: Borislav Petkov, Thomas Gleixner, Ingo Molnar, Andrew Lutomirski,
	X86 ML, Len Brown, Dave Hansen, H. J. Lu, Dave Martin, Jann Horn,
	Michael Ellerman, Carlos O'Donell, Tony Luck,
	Ravi V. Shankar, libc-alpha, linux-arch, Linux API, LKML,
	Borislav Petkov

On Wed, Feb 3, 2021 at 9:27 AM Chang S. Bae <chang.seok.bae@intel.com> wrote:
>
> The kernel pushes context on to the userspace stack to prepare for the
> user's signal handler. When the user has supplied an alternate signal
> stack, via sigaltstack(2), it is easy for the kernel to verify that the
> stack size is sufficient for the current hardware context.
>
> Check if writing the hardware context to the alternate stack will exceed
> it's size. If yes, then instead of corrupting user-data and proceeding with
> the original signal handler, an immediate SIGSEGV signal is delivered.
>
> While previous patches in this series allow new source code to discover and
> use a sufficient alternate signal stack size, this check is still necessary
> to protect binaries with insufficient alternate signal stack size from data
> corruption.
>
> Suggested-by: Jann Horn <jannh@google.com>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
> Reviewed-by: Len Brown <len.brown@intel.com>
> Reviewed-by: Jann Horn <jannh@google.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Jann Horn <jannh@google.com>
> Cc: x86@kernel.org
> Cc: linux-kernel@vger.kernel.org
> ---
> Changes from v3:
> * Updated the changelog (Borislav Petkov)
>
> Changes from v2:
> * Simplified the implementation (Jann Horn)
> ---
>  arch/x86/kernel/signal.c | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
> index 0d24f64d0145..8e2df070dbfd 100644
> --- a/arch/x86/kernel/signal.c
> +++ b/arch/x86/kernel/signal.c
> @@ -242,7 +242,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
>         unsigned long math_size = 0;
>         unsigned long sp = regs->sp;
>         unsigned long buf_fx = 0;
> -       int onsigstack = on_sig_stack(sp);
> +       bool onsigstack = on_sig_stack(sp);
>         int ret;
>
>         /* redzone */
> @@ -251,8 +251,11 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
>
>         /* This is the X/Open sanctioned signal stack switching.  */
>         if (ka->sa.sa_flags & SA_ONSTACK) {
> -               if (sas_ss_flags(sp) == 0)
> +               if (sas_ss_flags(sp) == 0) {
>                         sp = current->sas_ss_sp + current->sas_ss_size;
> +                       /* On the alternate signal stack */
> +                       onsigstack = true;

This is buggy.  The old code had a dubious special case for
SS_AUTODISARM, and this interacts poorly with it.  I think you could
fix it by separating the case in which you are already on the altstack
from the case in which you're switching to the altstack, or you could
fix it by changing the check at the end of the function to literally
check whether the sp value is in bounds instead of calling
on_sig_stack.

Arguably the generic helpers could be adjusted to make this less annoying.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-02-19  1:26 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-03 17:22 [PATCH v5 0/5] x86: Improve Minimum Alternate Stack Size Chang S. Bae
2021-02-03 17:22 ` [PATCH v5 1/5] uapi: Move the aux vector AT_MINSIGSTKSZ define to uapi Chang S. Bae
2021-02-04 15:55   ` Dave Martin
2021-02-12 18:46     ` Will Deacon
2021-02-03 17:22 ` [PATCH v5 2/5] x86/signal: Introduce helpers to get the maximum signal frame size Chang S. Bae
2021-02-03 17:22 ` [PATCH v5 3/5] x86/elf: Support a new ELF aux vector AT_MINSIGSTKSZ Chang S. Bae
2021-02-03 17:22 ` [PATCH v5 4/5] x86/signal: Detect and prevent an alternate signal stack overflow Chang S. Bae
2021-02-19  1:25   ` Andy Lutomirski
2021-02-03 17:22 ` [PATCH v5 5/5] selftest/x86/signal: Include test cases for validating sigaltstack Chang S. Bae

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).