linux/debian/patches/features/x86/x86-make-x32-syscall-suppor...

214 lines
6.2 KiB
Diff

From: Ben Hutchings <ben@decadent.org.uk>
Date: Mon, 12 Feb 2018 23:59:26 +0000
Subject: x86: Make x32 syscall support conditional on a kernel parameter
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>
---
Documentation/admin-guide/kernel-parameters.txt | 4 ++
arch/x86/Kconfig | 8 ++++
arch/x86/entry/common.c | 11 +++++-
arch/x86/entry/syscall_64.c | 41 ++++++++++++++++++++++++
arch/x86/include/asm/elf.h | 4 +-
arch/x86/include/asm/syscall.h | 13 +++++++
arch/x86/include/asm/unistd.h | 4 +-
7 files changed, 80 insertions(+), 5 deletions(-)
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -4142,6 +4142,10 @@
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
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2910,6 +2910,14 @@ config COMPAT_32
select HAVE_UID16
select OLD_SIGSUSPEND3
+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
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -281,12 +281,21 @@ __visible void do_syscall_64(unsigned lo
* table. The only functional difference is the x32 bit in
* regs->orig_ax, which changes the behavior of some syscalls.
*/
- nr &= __SYSCALL_MASK;
- if (likely(nr < NR_syscalls)) {
+ if (x32_enabled) {
+ nr &= ~__X32_SYSCALL_BIT;
+ if (unlikely(nr >= NR_syscalls))
+ goto bad;
nr = array_index_nospec(nr, NR_syscalls);
+ goto good;
+ } else {
+ nr &= ~0U;
+ if (unlikely(nr >= NR_non_x32_syscalls))
+ goto bad;
+ nr = array_index_nospec(nr, NR_non_x32_syscalls);
+good:
regs->ax = sys_call_table[nr](regs);
}
-
+bad:
syscall_return_slowpath(regs);
}
#endif
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -4,6 +4,9 @@
#include <linux/linkage.h>
#include <linux/sys.h>
#include <linux/cache.h>
+#include <linux/moduleparam.h>
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "syscall."
#include <asm/asm-offsets.h>
#include <asm/syscall.h>
@@ -23,3 +26,50 @@ asmlinkage const sys_call_ptr_t sys_call
[0 ... __NR_syscall_max] = &sys_ni_syscall,
#include <asm/syscalls_64.h>
};
+
+#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
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -10,6 +10,7 @@
#include <asm/ptrace.h>
#include <asm/user.h>
#include <asm/auxvec.h>
+#include <asm/syscall.h>
typedef unsigned long elf_greg_t;
@@ -163,7 +164,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"
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -16,6 +16,7 @@
#include <uapi/linux/audit.h>
#include <linux/sched.h>
#include <linux/err.h>
+#include <linux/jump_label.h>
#include <asm/asm-offsets.h> /* For NR_syscalls */
#include <asm/thread_info.h> /* for TS_COMPAT */
#include <asm/unistd.h>
@@ -39,6 +40,18 @@ extern const sys_call_ptr_t sys_call_tab
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
--- a/arch/x86/include/asm/unistd.h
+++ b/arch/x86/include/asm/unistd.h
@@ -6,9 +6,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