[linux] 03/10: [x86] Rewrite "Make x32 syscall support conditional ..." to use a static key

debian-kernel at lists.debian.org debian-kernel at lists.debian.org
Fri Feb 16 17:28:17 UTC 2018


This is an automated email from the git hooks/post-receive script.

corsac pushed a commit to branch stretch
in repository linux.

commit aba8ef1b45367344420cac249de2307e5ec3aa23
Author: Yves-Alexis Perez <corsac at corsac.net>
Date:   Thu Feb 15 21:31:50 2018 +0100

    [x86] Rewrite "Make x32 syscall support conditional ..." to use a static key
    
    Use rewritten patch for 4.15 from Ben Hutchings
---
 debian/changelog                                   |   2 +
 .../x86-make-x32-syscall-support-conditional.patch | 249 ++++++++++-----------
 2 files changed, 123 insertions(+), 128 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index 4d3aef4..21d101d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -48,6 +48,8 @@ linux (4.9.81-1) UNRELEASED; urgency=medium
     - KVM/VMX: Allow direct access to MSR_IA32_SPEC_CTRL
     - KVM/SVM: Allow direct access to MSR_IA32_SPEC_CTRL
     - [x86] x86/microcode: Do the family check first
+  * [x86] Rewrite "Make x32 syscall support conditional on a kernel parameter"
+    to use a static key
 
  -- Yves-Alexis Perez <corsac at debian.org>  Tue, 13 Feb 2018 17:44:41 +0100
 
diff --git a/debian/patches/features/x86/x86-make-x32-syscall-support-conditional.patch b/debian/patches/features/x86/x86-make-x32-syscall-support-conditional.patch
index d606fbf..fe701e4 100644
--- a/debian/patches/features/x86/x86-make-x32-syscall-support-conditional.patch
+++ b/debian/patches/features/x86/x86-make-x32-syscall-support-conditional.patch
@@ -1,8 +1,7 @@
 From: Ben Hutchings <ben at decadent.org.uk>
-Date: Fri, 25 Jul 2014 01:16:15 +0100
+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: 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.
@@ -10,25 +9,27 @@ 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).
+the only differences on syscall entry are a few instructions that 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.
+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 at decadent.org.uk>
 ---
- Documentation/kernel-parameters.txt |  4 ++++
- arch/x86/Kconfig                    |  8 +++++++
- arch/x86/include/asm/elf.h          |  8 ++++++-
- arch/x86/entry/entry_64.S           | 36 ++++++++++++++++++++++---------
- arch/x86/entry/syscall_64.c         | 43 +++++++++++++++++++++++++++++++++++++
- 5 files changed, 88 insertions(+), 11 deletions(-)
+ Documentation/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/kernel-parameters.txt
 +++ b/Documentation/kernel-parameters.txt
-@@ -4005,6 +4005,10 @@ bytes respectively. Such letter suffixes
+@@ -4096,6 +4096,10 @@
  
  	switches=	[HW,M68k]
  
@@ -41,7 +42,7 @@ Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
  			on older distributions. When this option is enabled
 --- a/arch/x86/Kconfig
 +++ b/arch/x86/Kconfig
-@@ -2721,6 +2721,14 @@ config X86_X32
+@@ -2735,6 +2735,14 @@ config X86_X32
  	  elf32_x86_64 support enabled to compile a kernel with this
  	  option set.
  
@@ -56,77 +57,39 @@ Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
  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
-@@ -9,6 +9,7 @@
- #include <asm/ptrace.h>
- #include <asm/user.h>
- #include <asm/auxvec.h>
-+#include <asm/syscall.h>
- 
- typedef unsigned long elf_greg_t;
- 
-@@ -162,7 +163,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/entry/entry_64.S
-+++ b/arch/x86/entry/entry_64.S
-@@ -195,8 +195,12 @@ entry_SYSCALL_64_fastpath:
- #if __SYSCALL_MASK == ~0
- 	cmpq	$__NR_syscall_max, %rax
- #else
--	andl	$__SYSCALL_MASK, %eax
--	cmpl	$__NR_syscall_max, %eax
-+.global system_call_fast_compare
-+.global 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	1f				/* return -ENOSYS (already in pt_regs->ax) */
- 	movq	%r10, %rcx
-@@ -331,6 +335,16 @@ opportunistic_sysret_failed:
- 	jmp	restore_c_regs_and_iret
- END(entry_SYSCALL_64)
- 
-+#if __SYSCALL_MASK != ~0
-+	/* This replaces the usual comparisons if syscall.x32 is set */
-+.global system_call_mask_compare
-+.global system_call_mask_compare_end
-+system_call_mask_compare:
-+	andl	$__SYSCALL_MASK, %eax
-+	cmpl	$__NR_syscall_max, %eax
-+system_call_mask_compare_end:
-+#endif
-+
- ENTRY(stub_ptregs_64)
- 	/*
- 	 * Syscalls marked as needing ptregs land here.
+--- a/arch/x86/entry/common.c
++++ b/arch/x86/entry/common.c
+@@ -282,8 +282,15 @@ __visible void do_syscall_64(struct pt_r
+ 	 * 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);
 --- a/arch/x86/entry/syscall_64.c
 +++ b/arch/x86/entry/syscall_64.c
-@@ -3,8 +3,14 @@
+@@ -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/bug.h>
-+#include <linux/init.h>
  #include <asm/asm-offsets.h>
  #include <asm/syscall.h>
-+#include <asm/text-patching.h>
  
- #define __SYSCALL_64_QUAL_(sym) sym
- #define __SYSCALL_64_QUAL_ptregs(sym) ptregs_##sym
-@@ -25,3 +31,36 @@ asmlinkage const sys_call_ptr_t sys_call
+@@ -23,3 +26,50 @@ asmlinkage const sys_call_ptr_t sys_call
  	[0 ... __NR_syscall_max] = &sys_ni_syscall,
  #include <asm/syscalls_64.h>
  };
@@ -135,74 +98,90 @@ Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
 +
 +/* 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_mask_compare_end[], system_call_mask_compare[];
++#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_enable(void)
++static int __init x32_param_set(const char *val, const struct kernel_param *p)
 +{
-+	BUG_ON(system_call_fast_compare_end - system_call_fast_compare != 10);
-+	BUG_ON(system_call_mask_compare_end - system_call_mask_compare != 10);
++	bool enabled;
++	int ret;
 +
-+	if (x32_enabled) {
-+		text_poke_early(system_call_fast_compare,
-+				system_call_mask_compare, 10);
-+#ifdef CONFIG_X86_X32_DISABLED
-+		pr_info("Enabled x32 syscalls\n");
-+#endif
++	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");
++		}
 +	}
-+#ifndef CONFIG_X86_X32_DISABLED
-+	else
-+		pr_info("Disabled x32 syscalls\n");
-+#endif
-+
 +	return 0;
 +}
-+late_initcall(x32_enable);
++
++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/entry/common.c
-+++ b/arch/x86/entry/common.c
-@@ -264,6 +264,7 @@ __visible void do_syscall_64(struct pt_r
- {
- 	struct thread_info *ti = current_thread_info();
- 	unsigned long nr = regs->orig_ax;
-+	unsigned int syscall_mask, nr_syscalls_enabled;
+--- 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>
  
- 	enter_from_user_mode();
- 	local_irq_enable();
-@@ -276,8 +277,19 @@ __visible void do_syscall_64(struct pt_r
- 	 * 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)) {
--		regs->ax = sys_call_table[nr & __SYSCALL_MASK](
-+	if (__SYSCALL_MASK == ~0U || x32_enabled) {
-+		syscall_mask = __SYSCALL_MASK;
-+		nr_syscalls_enabled = NR_syscalls;
-+	} else {
-+		/*
-+		 * x32 syscalls present but not enabled.  Don't mask out
-+		 * the x32 flag and don't enable any x32-specific calls.
-+		 */
-+		syscall_mask = ~0U;
-+		nr_syscalls_enabled = 512;
-+	}
-+	if (likely((nr & syscall_mask) < nr_syscalls_enabled)) {
-+		regs->ax = sys_call_table[nr & syscall_mask](
- 			regs->di, regs->si, regs->dx,
- 			regs->r10, regs->r8, regs->r9);
- 	}
+ 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
-@@ -35,6 +35,12 @@ extern const sys_call_ptr_t sys_call_tab
+@@ -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>
+@@ -35,6 +36,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)
-+extern bool x32_enabled;
++#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
@@ -210,3 +189,17 @@ Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
  /*
   * 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

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/kernel/linux.git



More information about the Kernel-svn-changes mailing list