From: Ben Hutchings Date: Mon, 12 Feb 2018 23:59:26 +0000 (+0000) Subject: x86: Make x32 syscall support conditional on a kernel parameter X-Git-Tag: archive/raspbian/4.9.82-1+deb9u3+rpi1_jessie~8^2~81 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=745c0811b54f31498fa956c7f2f69ebdd315f877;p=linux-4.9.git x86: Make x32 syscall support conditional on a kernel parameter 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 Gbp-Pq: Topic features/x86 Gbp-Pq: Name x86-make-x32-syscall-support-conditional.patch --- diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 466c039c622b..9b54031d2bb6 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -4070,6 +4070,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. switches= [HW,M68k] + 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. + sysfs.deprecated=0|1 [KNL] Enable/disable old style sysfs layout for old udev on older distributions. When this option is enabled diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 0ca4d12ce95c..447542388dd0 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2735,6 +2735,14 @@ config X86_X32 elf32_x86_64 support enabled to compile a kernel with this option set. +config X86_X32_DISABLED + bool "x32 ABI disabled by default" + depends on X86_X32 + 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 diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index b0cd306dc527..6ad3d7ddbdda 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -277,8 +277,15 @@ __visible void do_syscall_64(struct pt_regs *regs) * table. The only functional difference is the x32 bit in * regs->orig_ax, which changes the behavior of some syscalls. */ - if (likely((nr & __SYSCALL_MASK) < NR_syscalls)) { - nr = array_index_nospec(nr & __SYSCALL_MASK, NR_syscalls); + if (x32_enabled) { + if (likely((nr & ~__X32_SYSCALL_BIT) < NR_syscalls)) { + nr = array_index_nospec(nr & ~__X32_SYSCALL_BIT, + NR_syscalls); + goto good; + } + } else if (likely((nr & ~0U) < NR_non_x32_syscalls)) { + nr = array_index_nospec(nr & ~0U, NR_non_x32_syscalls); + good: regs->ax = sys_call_table[nr]( regs->di, regs->si, regs->dx, regs->r10, regs->r8, regs->r9); diff --git a/arch/x86/entry/syscall_64.c b/arch/x86/entry/syscall_64.c index 6705edda4ac3..bce23bb4b33a 100644 --- a/arch/x86/entry/syscall_64.c +++ b/arch/x86/entry/syscall_64.c @@ -3,6 +3,9 @@ #include #include #include +#include +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "syscall." #include #include @@ -22,3 +25,50 @@ asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = { [0 ... __NR_syscall_max] = &sys_ni_syscall, #include }; + +#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 diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h index 7bcd138c3aa9..3c59fb4edeb5 100644 --- a/arch/x86/include/asm/elf.h +++ b/arch/x86/include/asm/elf.h @@ -9,6 +9,7 @@ #include #include #include +#include typedef unsigned long elf_greg_t; @@ -162,7 +163,8 @@ do { \ #define compat_elf_check_arch(x) \ (elf_check_arch_ia32(x) || \ - (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)) #if __USER32_DS != __USER_DS # error "The following code assumes __USER32_DS == __USER_DS" diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h index 03eedc21246d..aede3b276452 100644 --- a/arch/x86/include/asm/syscall.h +++ b/arch/x86/include/asm/syscall.h @@ -16,6 +16,7 @@ #include #include #include +#include #include /* For NR_syscalls */ #include /* for TS_COMPAT */ #include @@ -35,6 +36,18 @@ extern const sys_call_ptr_t sys_call_table[]; extern const sys_call_ptr_t ia32_sys_call_table[]; #endif +#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 diff --git a/arch/x86/include/asm/unistd.h b/arch/x86/include/asm/unistd.h index 32712a925f26..9c83a35dc331 100644 --- a/arch/x86/include/asm/unistd.h +++ b/arch/x86/include/asm/unistd.h @@ -5,9 +5,9 @@ # ifdef CONFIG_X86_X32_ABI -# define __SYSCALL_MASK (~(__X32_SYSCALL_BIT)) +# define NR_non_x32_syscalls 512 # else -# define __SYSCALL_MASK (~0) +# define NR_non_x32_syscalls NR_syscalls # endif # ifdef CONFIG_X86_32