190 lines
5.8 KiB
Diff
190 lines
5.8 KiB
Diff
From: Ben Hutchings <ben@decadent.org.uk>
|
|
Date: Fri, 25 Jul 2014 01:16:15 +0100
|
|
Subject: x86: Make x32 syscall support conditional on a kernel parameter
|
|
Bug-Debian: https://bugs.debian.org/708070
|
|
Forwarded: http://mid.gmane.org/1415245982.3398.53.camel@decadent.org.uk
|
|
|
|
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 two instructions (mask out
|
|
the x32 flag and compare the syscall number).
|
|
|
|
So pad the standard comparison with a nop and add a kernel parameter
|
|
"syscall.x32" which controls whether this is replaced with the x32
|
|
version at boot time. Add a Kconfig parameter to set the default.
|
|
|
|
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
|
|
---
|
|
Documentation/kernel-parameters.txt | 4 ++++
|
|
arch/x86/Kconfig | 8 +++++++
|
|
arch/x86/include/asm/elf.h | 8 ++++++-
|
|
arch/x86/kernel/entry_64.S | 36 ++++++++++++++++++++++---------
|
|
arch/x86/kernel/syscall_64.c | 43 +++++++++++++++++++++++++++++++++++++
|
|
5 files changed, 88 insertions(+), 11 deletions(-)
|
|
|
|
--- a/Documentation/kernel-parameters.txt
|
|
+++ b/Documentation/kernel-parameters.txt
|
|
@@ -3397,6 +3397,10 @@ bytes respectively. Such letter suffixes
|
|
|
|
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
|
|
@@ -2455,6 +2455,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
|
|
--- a/arch/x86/include/asm/elf.h
|
|
+++ b/arch/x86/include/asm/elf.h
|
|
@@ -154,6 +154,12 @@ do { \
|
|
|
|
#else /* CONFIG_X86_32 */
|
|
|
|
+#ifdef CONFIG_X86_X32_ABI
|
|
+extern bool x32_enabled;
|
|
+#else
|
|
+#define x32_enabled 0
|
|
+#endif
|
|
+
|
|
/*
|
|
* This is used to ensure we don't load something for the wrong architecture.
|
|
*/
|
|
@@ -162,7 +168,7 @@ do { \
|
|
|
|
#define compat_elf_check_arch(x) \
|
|
(elf_check_arch_ia32(x) || \
|
|
- (IS_ENABLED(CONFIG_X86_X32_ABI) && (x)->e_machine == EM_X86_64))
|
|
+ (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/kernel/entry_64.S
|
|
+++ b/arch/x86/kernel/entry_64.S
|
|
@@ -414,8 +414,12 @@ system_call_fastpath:
|
|
#if __SYSCALL_MASK == ~0
|
|
cmpq $__NR_syscall_max,%rax
|
|
#else
|
|
- andl $__SYSCALL_MASK,%eax
|
|
- cmpl $__NR_syscall_max,%eax
|
|
+ .globl system_call_fast_compare
|
|
+ .globl system_call_fast_compare_end
|
|
+system_call_fast_compare:
|
|
+ cmpq $511,%rax /* x32 syscalls start at 512 */
|
|
+ .byte P6_NOP4
|
|
+system_call_fast_compare_end:
|
|
#endif
|
|
ja ret_from_sys_call /* and return regs->ax */
|
|
movq %r10,%rcx
|
|
@@ -520,8 +524,12 @@ tracesys_phase2:
|
|
#if __SYSCALL_MASK == ~0
|
|
cmpq $__NR_syscall_max,%rax
|
|
#else
|
|
- andl $__SYSCALL_MASK,%eax
|
|
- cmpl $__NR_syscall_max,%eax
|
|
+ .globl system_call_trace_compare
|
|
+ .globl system_call_trace_compare_end
|
|
+system_call_trace_compare:
|
|
+ cmpq $511,%rax /* x32 syscalls start at 512 */
|
|
+ .byte P6_NOP4
|
|
+system_call_trace_compare_end:
|
|
#endif
|
|
ja int_ret_from_sys_call /* RAX(%rsp) is already set */
|
|
movq %r10,%rcx /* fixup for C */
|
|
@@ -593,6 +601,16 @@ int_restore_rest:
|
|
CFI_ENDPROC
|
|
END(system_call)
|
|
|
|
+#if __SYSCALL_MASK != ~0
|
|
+ /* This replaces the usual comparisons if syscall.x32 is set */
|
|
+ .globl system_call_mask_compare
|
|
+ .globl system_call_mask_compare_end
|
|
+system_call_mask_compare:
|
|
+ andl $__SYSCALL_MASK,%eax
|
|
+ cmpl $__NR_syscall_max,%eax
|
|
+system_call_mask_compare_end:
|
|
+#endif
|
|
+
|
|
.macro FORK_LIKE func
|
|
ENTRY(stub_\func)
|
|
CFI_STARTPROC
|
|
--- a/arch/x86/kernel/syscall_64.c
|
|
+++ b/arch/x86/kernel/syscall_64.c
|
|
@@ -3,8 +3,14 @@
|
|
#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/bug.h>
|
|
+#include <linux/init.h>
|
|
#include <asm/asm-offsets.h>
|
|
#include <asm/syscall.h>
|
|
+#include <asm/alternative.h>
|
|
|
|
#define __SYSCALL_COMMON(nr, sym, compat) __SYSCALL_64(nr, sym, compat)
|
|
|
|
@@ -30,3 +36,40 @@ 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 */
|
|
+
|
|
+bool x32_enabled = !IS_ENABLED(CONFIG_X86_X32_DISABLED);
|
|
+module_param_named(x32, x32_enabled, bool, 0444);
|
|
+
|
|
+extern char system_call_fast_compare_end[], system_call_fast_compare[],
|
|
+ system_call_trace_compare_end[], system_call_trace_compare[],
|
|
+ system_call_mask_compare_end[], system_call_mask_compare[];
|
|
+
|
|
+static int __init x32_enable(void)
|
|
+{
|
|
+ BUG_ON(system_call_fast_compare_end - system_call_fast_compare != 10);
|
|
+ BUG_ON(system_call_trace_compare_end - system_call_trace_compare != 10);
|
|
+ BUG_ON(system_call_mask_compare_end - system_call_mask_compare != 10);
|
|
+
|
|
+ if (x32_enabled) {
|
|
+ text_poke_early(system_call_fast_compare,
|
|
+ system_call_mask_compare, 10);
|
|
+ text_poke_early(system_call_trace_compare,
|
|
+ system_call_mask_compare, 10);
|
|
+#ifdef CONFIG_X86_X32_DISABLED
|
|
+ pr_info("Enabled x32 syscalls\n");
|
|
+#endif
|
|
+ }
|
|
+#ifndef CONFIG_X86_X32_DISABLED
|
|
+ else
|
|
+ pr_info("Disabled x32 syscalls\n");
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+late_initcall(x32_enable);
|
|
+
|
|
+#endif
|