x86/signal: Implement sigaltstack size validation
authorThomas Gleixner <tglx@linutronix.de>
Thu, 21 Oct 2021 22:55:06 +0000 (15:55 -0700)
committerBorislav Petkov <bp@suse.de>
Tue, 26 Oct 2021 08:18:09 +0000 (10:18 +0200)
For historical reasons MINSIGSTKSZ is a constant which became already too
small with AVX512 support.

Add a mechanism to enforce strict checking of the sigaltstack size against
the real size of the FPU frame.

The strict check can be enabled via a config option and can also be
controlled via the kernel command line option 'strict_sas_size' independent
of the config switch.

Enabling it might break existing applications which allocate a too small
sigaltstack but 'work' because they never get a signal delivered. Though it
can be handy to filter out binaries which are not yet aware of
AT_MINSIGSTKSZ.

Also the upcoming support for dynamically enabled FPU features requires a
strict sanity check to ensure that:

   - Enabling of a dynamic feature, which changes the sigframe size fits
     into an enabled sigaltstack

   - Installing a too small sigaltstack after a dynamic feature has been
     added is not possible.

Implement the base check which is controlled by config and command line
options.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/20211021225527.10184-3-chang.seok.bae@intel.com
Documentation/admin-guide/kernel-parameters.txt
arch/x86/Kconfig
arch/x86/kernel/signal.c

index 43dc35f..eb9a73a 100644 (file)
        stifb=          [HW]
                        Format: bpp:<bpp1>[:<bpp2>[:<bpp3>...]]
 
+        strict_sas_size=
+                       [X86]
+                       Format: <bool>
+                       Enable or disable strict sigaltstack size checks
+                       against the required signal frame size which
+                       depends on the supported FPU features. This can
+                       be used to filter out binaries which have
+                       not yet been made aware of AT_MINSIGSTKSZ.
+
        sunrpc.min_resvport=
        sunrpc.max_resvport=
                        [NFS,SUNRPC]
index d9830e7..8584b30 100644 (file)
@@ -125,6 +125,7 @@ config X86
        select CLOCKSOURCE_VALIDATE_LAST_CYCLE
        select CLOCKSOURCE_WATCHDOG
        select DCACHE_WORD_ACCESS
+       select DYNAMIC_SIGFRAME
        select EDAC_ATOMIC_SCRUB
        select EDAC_SUPPORT
        select GENERIC_CLOCKEVENTS_BROADCAST    if X86_64 || (X86_32 && X86_LOCAL_APIC)
@@ -2388,6 +2389,22 @@ config MODIFY_LDT_SYSCALL
 
          Saying 'N' here may make sense for embedded or server kernels.
 
+config STRICT_SIGALTSTACK_SIZE
+       bool "Enforce strict size checking for sigaltstack"
+       depends on DYNAMIC_SIGFRAME
+       help
+         For historical reasons MINSIGSTKSZ is a constant which became
+         already too small with AVX512 support. Add a mechanism to
+         enforce strict checking of the sigaltstack size against the
+         real size of the FPU frame. This option enables the check
+         by default. It can also be controlled via the kernel command
+         line option 'strict_sas_size' independent of this config
+         switch. Enabling it might break existing applications which
+         allocate a too small sigaltstack but 'work' because they
+         never get a signal delivered.
+
+         Say 'N' unless you want to really enforce this check.
+
 source "kernel/livepatch/Kconfig"
 
 endmenu
index 58bd070..0111a6a 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/mm.h>
 #include <linux/smp.h>
 #include <linux/kernel.h>
+#include <linux/kstrtox.h>
 #include <linux/errno.h>
 #include <linux/wait.h>
 #include <linux/tracehook.h>
@@ -40,6 +41,7 @@
 #include <linux/compat.h>
 #include <asm/proto.h>
 #include <asm/ia32_unistd.h>
+#include <asm/fpu/xstate.h>
 #endif /* CONFIG_X86_64 */
 
 #include <asm/syscall.h>
@@ -907,6 +909,39 @@ void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
        force_sig(SIGSEGV);
 }
 
+#ifdef CONFIG_DYNAMIC_SIGFRAME
+#ifdef CONFIG_STRICT_SIGALTSTACK_SIZE
+static bool strict_sigaltstack_size __ro_after_init = true;
+#else
+static bool strict_sigaltstack_size __ro_after_init = false;
+#endif
+
+static int __init strict_sas_size(char *arg)
+{
+       return kstrtobool(arg, &strict_sigaltstack_size);
+}
+__setup("strict_sas_size", strict_sas_size);
+
+/*
+ * MINSIGSTKSZ is 2048 and can't be changed despite the fact that AVX512
+ * exceeds that size already. As such programs might never use the
+ * sigaltstack they just continued to work. While always checking against
+ * the real size would be correct, this might be considered a regression.
+ *
+ * Therefore avoid the sanity check, unless enforced by kernel config or
+ * command line option.
+ */
+bool sigaltstack_size_valid(size_t ss_size)
+{
+       lockdep_assert_held(&current->sighand->siglock);
+
+       if (strict_sigaltstack_size)
+               return ss_size > get_sigframe_size();
+
+       return true;
+}
+#endif /* CONFIG_DYNAMIC_SIGFRAME */
+
 #ifdef CONFIG_X86_X32_ABI
 COMPAT_SYSCALL_DEFINE0(x32_rt_sigreturn)
 {