x86: Make x32 syscall support conditional on a kernel parameter
authorBen Hutchings <ben@decadent.org.uk>
Mon, 12 Feb 2018 23:59:26 +0000 (23:59 +0000)
committerSalvatore Bonaccorso <carnil@debian.org>
Sat, 20 Dec 2025 09:15:48 +0000 (10:15 +0100)
Bug-Debian: https://bugs.debian.org/708070
Forwarded: https://lore.kernel.org/lkml/1415245982.3398.53.camel@decadent.org.uk/T/#u

Enabling x32 in the standard amd64 kernel would increase its attack
surface while provide no benefit to the vast majority of its users.
No-one seems interested in regularly checking for vulnerabilities
specific to x32 (at least no-one with a white hat).

Still, adding another flavour just to turn on x32 seems wasteful.  And
the only differences on syscall entry are a few instructions that mask
out the x32 flag and compare the syscall number.

Use a static key to control whether x32 syscalls are really enabled, a
Kconfig parameter to set its default value and a kernel parameter
"syscall.x32" to change it at boot time.

Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Gbp-Pq: Topic features/x86
Gbp-Pq: Name x86-make-x32-syscall-support-conditional.patch

Documentation/admin-guide/kernel-parameters.txt
arch/x86/Kconfig
arch/x86/entry/syscall_64.c
arch/x86/include/asm/elf.h
arch/x86/include/asm/syscall.h

index 6711cff80054cbaf0f3dfb5e46c36adbe9b73f9b..80b8ae54e0295a4de01b5aa273786cd78a71cc11 100644 (file)
                        later by a loaded module cannot be set this way.
                        Example: sysctl.vm.swappiness=40
 
+       syscall.x32=    [KNL,x86_64] Enable/disable use of x32 syscalls on
+                       an x86_64 kernel where CONFIG_X86_X32 is enabled.
+                       Default depends on CONFIG_X86_X32_DISABLED.
+
        sysrq_always_enabled
                        [KNL]
                        Ignore sysrq setting - this boot parameter will
index 05880301212e3a5d944f186d0f86f987132655f2..29d2ae66fe9776047e8884a129814c92a047c2d1 100644 (file)
@@ -3156,6 +3156,14 @@ config COMPAT_32
        select HAVE_UID16
        select OLD_SIGSUSPEND3
 
+config X86_X32_DISABLED
+       bool "x32 ABI disabled by default"
+       depends on X86_X32_ABI
+       default n
+       help
+         Disable the x32 ABI unless explicitly enabled using the
+         kernel paramter "syscall.x32=y".
+
 config COMPAT
        def_bool y
        depends on IA32_EMULATION || X86_X32_ABI
index b6e68ea98b839ac9c6b564c085190b2627a17265..cc3c6b00da2c45c5aaec5878252952148303faf6 100644 (file)
@@ -7,6 +7,9 @@
 #include <linux/syscalls.h>
 #include <linux/entry-common.h>
 #include <linux/nospec.h>
+#include <linux/moduleparam.h>
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "syscall."
 #include <asm/syscall.h>
 
 #define __SYSCALL(nr, sym) extern long __x64_##sym(const struct pt_regs *);
@@ -75,7 +78,7 @@ static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
         */
        unsigned int xnr = nr - __X32_SYSCALL_BIT;
 
-       if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) {
+       if (IS_ENABLED(CONFIG_X86_X32_ABI) && unlikely(x32_enabled) && likely(xnr < X32_NR_syscalls)) {
                xnr = array_index_nospec(xnr, X32_NR_syscalls);
                regs->ax = x32_sys_call(regs, xnr);
                return true;
@@ -139,3 +142,48 @@ __visible noinstr bool do_syscall_64(struct pt_regs *regs, int nr)
        /* Use SYSRET to exit to userspace */
        return true;
 }
+
+#ifdef CONFIG_X86_X32_ABI
+/* Maybe enable x32 syscalls */
+
+#if defined(CONFIG_X86_X32_DISABLED)
+DEFINE_STATIC_KEY_FALSE(x32_enabled_skey);
+#else
+DEFINE_STATIC_KEY_TRUE(x32_enabled_skey);
+#endif
+
+static int __init x32_param_set(const char *val, const struct kernel_param *p)
+{
+       bool enabled;
+       int ret;
+
+       ret = kstrtobool(val, &enabled);
+       if (ret)
+               return ret;
+       if (IS_ENABLED(CONFIG_X86_X32_DISABLED)) {
+               if (enabled) {
+                       static_key_enable(&x32_enabled_skey.key);
+                       pr_info("Enabled x32 syscalls\n");
+               }
+       } else {
+               if (!enabled) {
+                       static_key_disable(&x32_enabled_skey.key);
+                       pr_info("Disabled x32 syscalls\n");
+               }
+       }
+       return 0;
+}
+
+static int x32_param_get(char *buffer, const struct kernel_param *p)
+{
+       return sprintf(buffer, "%c\n",
+                      static_key_enabled(&x32_enabled_skey) ? 'Y' : 'N');
+}
+
+static const struct kernel_param_ops x32_param_ops = {
+       .set = x32_param_set,
+       .get = x32_param_get,
+};
+
+arch_param_cb(x32, &x32_param_ops, NULL, 0444);
+#endif
index 6c8fdc96be7e8bd33919d9b56ae8a8a68f4b0c3f..3d6c00df82446cf5f112325d8512560e98be4d95 100644 (file)
@@ -12,6 +12,9 @@
 #include <asm/user.h>
 #include <asm/auxvec.h>
 #include <asm/fsgsbase.h>
+#ifndef COMPILE_OFFSETS /* avoid a circular dependency on asm-offsets.h */
+#include <asm/syscall.h>
+#endif
 
 typedef unsigned long elf_greg_t;
 
@@ -148,7 +151,8 @@ do {                                                \
 
 #define compat_elf_check_arch(x)                                       \
        ((elf_check_arch_ia32(x) && ia32_enabled_verbose()) ||          \
-        (IS_ENABLED(CONFIG_X86_X32_ABI) && (x)->e_machine == EM_X86_64))
+        (IS_ENABLED(CONFIG_X86_X32_ABI) && x32_enabled &&              \
+         (x)->e_machine == EM_X86_64))
 
 static inline void elf_common_init(struct thread_struct *t,
                                   struct pt_regs *regs, const u16 ds)
index c10dbb74cd001e2c5920150c239d38fde3d4b5a6..05a4730a5a31d752f74cc390fabce70c76626869 100644 (file)
@@ -13,6 +13,7 @@
 #include <uapi/linux/audit.h>
 #include <linux/sched.h>
 #include <linux/err.h>
+#include <linux/jump_label.h>
 #include <asm/thread_info.h>   /* for TS_COMPAT */
 #include <asm/unistd.h>
 
@@ -28,6 +29,18 @@ extern long ia32_sys_call(const struct pt_regs *, unsigned int nr);
 extern long x32_sys_call(const struct pt_regs *, unsigned int nr);
 extern long x64_sys_call(const struct pt_regs *, unsigned int nr);
 
+#if defined(CONFIG_X86_X32_ABI)
+#if defined(CONFIG_X86_X32_DISABLED)
+DECLARE_STATIC_KEY_FALSE(x32_enabled_skey);
+#define x32_enabled static_branch_unlikely(&x32_enabled_skey)
+#else
+DECLARE_STATIC_KEY_TRUE(x32_enabled_skey);
+#define x32_enabled static_branch_likely(&x32_enabled_skey)
+#endif
+#else
+#define x32_enabled 0
+#endif
+
 /*
  * Only the low 32 bits of orig_ax are meaningful, so we return int.
  * This importantly ignores the high bits on 64-bit, so comparisons