summaryrefslogtreecommitdiffstats
path: root/debian/patches/features/x86/x86-make-x32-syscall-support-conditional.patch
blob: 3dde15cce18f24e632df9207d6a3e42a64c59f43 (plain)
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
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(-)

Index: linux/Documentation/admin-guide/kernel-parameters.txt
===================================================================
--- linux.orig/Documentation/admin-guide/kernel-parameters.txt
+++ linux/Documentation/admin-guide/kernel-parameters.txt
@@ -4501,6 +4501,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
Index: linux/arch/x86/Kconfig
===================================================================
--- linux.orig/arch/x86/Kconfig
+++ linux/arch/x86/Kconfig
@@ -2884,6 +2884,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
Index: linux/arch/x86/entry/common.c
===================================================================
--- linux.orig/arch/x86/entry/common.c
+++ linux/arch/x86/entry/common.c
@@ -287,12 +287,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
Index: linux/arch/x86/entry/syscall_64.c
===================================================================
--- linux.orig/arch/x86/entry/syscall_64.c
+++ linux/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
Index: linux/arch/x86/include/asm/elf.h
===================================================================
--- linux.orig/arch/x86/include/asm/elf.h
+++ linux/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"
Index: linux/arch/x86/include/asm/syscall.h
===================================================================
--- linux.orig/arch/x86/include/asm/syscall.h
+++ linux/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
Index: linux/arch/x86/include/asm/unistd.h
===================================================================
--- linux.orig/arch/x86/include/asm/unistd.h
+++ linux/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