1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
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>
---
.../admin-guide/kernel-parameters.txt | 4 ++
arch/x86/Kconfig | 8 ++++
arch/x86/entry/common.c | 3 +-
arch/x86/entry/syscall_64.c | 46 +++++++++++++++++++
arch/x86/include/asm/elf.h | 6 ++-
arch/x86/include/asm/syscall.h | 13 ++++++
6 files changed, 78 insertions(+), 2 deletions(-)
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -6498,6 +6498,10 @@
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
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -3058,6 +3058,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
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -64,7 +64,7 @@ static __always_inline bool do_syscall_x
*/
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;
--- a/arch/x86/entry/syscall_x32.c
+++ b/arch/x86/entry/syscall_x32.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 <linux/syscalls.h>
#include <asm/syscall.h>
@@ -20,3 +23,46 @@
default: return __x64_sys_ni_syscall(regs);
}
};
+
+/* 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);
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -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;
@@ -151,7 +154,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)
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -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 const sys_call_ptr_t ia32_sys_cal
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
|