From 2191e07a73b26fa1f56b9e64f983f9da3f173883 Mon Sep 17 00:00:00 2001 From: Thomas Garnier Date: Wed, 14 Jun 2017 18:12:01 -0700 Subject: [PATCH] BACKPORT: x86/syscalls: Check address limit on user-mode return (cherry-picked from 5ea0727b163cb5575e36397a12eade68a1f35f24) Ensure the address limit is a user-mode segment before returning to user-mode. Otherwise a process can corrupt kernel-mode memory and elevate privileges [1]. The set_fs function sets the TIF_SETFS flag to force a slow path on return. In the slow path, the address limit is checked to be USER_DS if needed. The addr_limit_user_check function is added as a cross-architecture function to check the address limit. [1] https://bugs.chromium.org/p/project-zero/issues/detail?id=990 Change-Id: I604d85b262cc5b439b2665852865ca5a9ea6c5a3 Signed-off-by: Thomas Garnier Signed-off-by: Thomas Gleixner Cc: Mark Rutland Cc: kernel-hardening@lists.openwall.com Cc: Catalin Marinas Cc: Will Deacon Cc: David Howells Cc: Dave Hansen Cc: Miroslav Benes Cc: Chris Metcalf Cc: Pratyush Anand Cc: Russell King Cc: Petr Mladek Cc: Rik van Riel Cc: Kees Cook Cc: Arnd Bergmann Cc: Al Viro Cc: Andy Lutomirski Cc: Josh Poimboeuf Cc: linux-arm-kernel@lists.infradead.org Cc: Will Drewry Cc: linux-api@vger.kernel.org Cc: Oleg Nesterov Cc: Andy Lutomirski Cc: Paolo Bonzini Link: http://lkml.kernel.org/r/20170615011203.144108-1-thgarnie@google.com Signed-off-by: Satya Tangirala --- arch/x86/entry/common.c | 3 +++ arch/x86/include/asm/thread_info.h | 4 +++- arch/x86/include/asm/uaccess.h | 7 ++++++- include/linux/syscalls.h | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index 071582a3b5c0..a9e501303e15 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -273,6 +274,8 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs) struct thread_info *ti = pt_regs_to_thread_info(regs); u32 cached_flags; + addr_limit_user_check(); + if (IS_ENABLED(CONFIG_PROVE_LOCKING) && WARN_ON(!irqs_disabled())) local_irq_disable(); diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index e8d453732823..c14f699f5c36 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -111,6 +111,7 @@ struct thread_info { #define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */ #define TIF_ADDR32 29 /* 32-bit address space on 64 bits */ #define TIF_X32 30 /* 32-bit native x86-64 binary */ +#define TIF_FSCHECK 31 /* Check FS is USER_DS on return */ #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) @@ -135,6 +136,7 @@ struct thread_info { #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) #define _TIF_ADDR32 (1 << TIF_ADDR32) #define _TIF_X32 (1 << TIF_X32) +#define _TIF_FSCHECK (1 << TIF_FSCHECK) /* work to do in syscall_trace_enter() */ #define _TIF_WORK_SYSCALL_ENTRY \ @@ -145,7 +147,7 @@ struct thread_info { /* work to do on any return to user space */ #define _TIF_ALLWORK_MASK \ ((0x0000FFFF & ~_TIF_SECCOMP) | _TIF_SYSCALL_TRACEPOINT | \ - _TIF_NOHZ) + _TIF_NOHZ | _TIF_FSCHECK) /* flags to check in __switch_to() */ #define _TIF_WORK_CTXSW \ diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 6a07c05956a6..8857f6f4daa9 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -30,7 +30,12 @@ #define get_ds() (KERNEL_DS) #define get_fs() (current_thread_info()->addr_limit) -#define set_fs(x) (current_thread_info()->addr_limit = (x)) +static inline void set_fs(mm_segment_t fs) +{ + current_thread_info()->addr_limit = fs; + /* On user-mode return, check fs is correct */ + set_thread_flag(TIF_FSCHECK); +} #define segment_eq(a, b) ((a).seg == (b).seg) diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index c2b66a277e98..a95cb2589765 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -205,6 +205,22 @@ extern struct trace_event_functions exit_syscall_print_funcs; } \ static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) +#ifdef TIF_FSCHECK +/* + * Called before coming back to user-mode. Returning to user-mode with an + * address limit different than USER_DS can allow to overwrite kernel memory. + */ +static inline void addr_limit_user_check(void) +{ + + if (!test_thread_flag(TIF_FSCHECK)) + return; + + BUG_ON(!segment_eq(get_fs(), USER_DS)); + clear_thread_flag(TIF_FSCHECK); +} +#endif + asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special, qid_t id, void __user *addr); asmlinkage long sys_time(time_t __user *tloc);