Commit 6c51e67b authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'x86-syscall-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull syscall updates from Ingo Molnar:
 "Improve the security of set_fs(): we now check the address limit on a
  number of key platforms (x86, arm, arm64) before returning to
  user-space - without adding overhead to the typical system call fast
  path"

* 'x86-syscall-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  arm64/syscalls: Check address limit on user-mode return
  arm/syscalls: Check address limit on user-mode return
  x86/syscalls: Check address limit on user-mode return
parents e0a195b5 cf7de27a
...@@ -139,10 +139,11 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, ...@@ -139,10 +139,11 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
#define TIF_NEED_RESCHED 1 /* rescheduling necessary */ #define TIF_NEED_RESCHED 1 /* rescheduling necessary */
#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */
#define TIF_UPROBE 3 /* breakpointed or singlestepping */ #define TIF_UPROBE 3 /* breakpointed or singlestepping */
#define TIF_SYSCALL_TRACE 4 /* syscall trace active */ #define TIF_FSCHECK 4 /* Check FS is USER_DS on return */
#define TIF_SYSCALL_AUDIT 5 /* syscall auditing active */ #define TIF_SYSCALL_TRACE 5 /* syscall trace active */
#define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */ #define TIF_SYSCALL_AUDIT 6 /* syscall auditing active */
#define TIF_SECCOMP 7 /* seccomp syscall filtering active */ #define TIF_SYSCALL_TRACEPOINT 7 /* syscall tracepoint instrumentation */
#define TIF_SECCOMP 8 /* seccomp syscall filtering active */
#define TIF_NOHZ 12 /* in adaptive nohz mode */ #define TIF_NOHZ 12 /* in adaptive nohz mode */
#define TIF_USING_IWMMXT 17 #define TIF_USING_IWMMXT 17
...@@ -153,6 +154,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, ...@@ -153,6 +154,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
#define _TIF_UPROBE (1 << TIF_UPROBE) #define _TIF_UPROBE (1 << TIF_UPROBE)
#define _TIF_FSCHECK (1 << TIF_FSCHECK)
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT)
...@@ -166,8 +168,9 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, ...@@ -166,8 +168,9 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
/* /*
* Change these and you break ASM code in entry-common.S * Change these and you break ASM code in entry-common.S
*/ */
#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ #define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
_TIF_NOTIFY_RESUME | _TIF_UPROBE) _TIF_NOTIFY_RESUME | _TIF_UPROBE | \
_TIF_FSCHECK)
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* __ASM_ARM_THREAD_INFO_H */ #endif /* __ASM_ARM_THREAD_INFO_H */
...@@ -70,6 +70,8 @@ static inline void set_fs(mm_segment_t fs) ...@@ -70,6 +70,8 @@ static inline void set_fs(mm_segment_t fs)
{ {
current_thread_info()->addr_limit = fs; current_thread_info()->addr_limit = fs;
modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER); modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER);
/* On user-mode return, check fs is correct */
set_thread_flag(TIF_FSCHECK);
} }
#define segment_eq(a, b) ((a) == (b)) #define segment_eq(a, b) ((a) == (b))
......
...@@ -41,7 +41,9 @@ ret_fast_syscall: ...@@ -41,7 +41,9 @@ ret_fast_syscall:
UNWIND(.cantunwind ) UNWIND(.cantunwind )
disable_irq_notrace @ disable interrupts disable_irq_notrace @ disable interrupts
ldr r1, [tsk, #TI_FLAGS] @ re-check for syscall tracing ldr r1, [tsk, #TI_FLAGS] @ re-check for syscall tracing
tst r1, #_TIF_SYSCALL_WORK | _TIF_WORK_MASK tst r1, #_TIF_SYSCALL_WORK
bne fast_work_pending
tst r1, #_TIF_WORK_MASK
bne fast_work_pending bne fast_work_pending
/* perform architecture specific actions before user return */ /* perform architecture specific actions before user return */
...@@ -67,12 +69,15 @@ ret_fast_syscall: ...@@ -67,12 +69,15 @@ ret_fast_syscall:
str r0, [sp, #S_R0 + S_OFF]! @ save returned r0 str r0, [sp, #S_R0 + S_OFF]! @ save returned r0
disable_irq_notrace @ disable interrupts disable_irq_notrace @ disable interrupts
ldr r1, [tsk, #TI_FLAGS] @ re-check for syscall tracing ldr r1, [tsk, #TI_FLAGS] @ re-check for syscall tracing
tst r1, #_TIF_SYSCALL_WORK | _TIF_WORK_MASK tst r1, #_TIF_SYSCALL_WORK
bne fast_work_pending
tst r1, #_TIF_WORK_MASK
beq no_work_pending beq no_work_pending
UNWIND(.fnend ) UNWIND(.fnend )
ENDPROC(ret_fast_syscall) ENDPROC(ret_fast_syscall)
/* Slower path - fall through to work_pending */ /* Slower path - fall through to work_pending */
fast_work_pending:
#endif #endif
tst r1, #_TIF_SYSCALL_WORK tst r1, #_TIF_SYSCALL_WORK
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/tracehook.h> #include <linux/tracehook.h>
#include <linux/uprobes.h> #include <linux/uprobes.h>
#include <linux/syscalls.h>
#include <asm/elf.h> #include <asm/elf.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
...@@ -613,6 +614,10 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) ...@@ -613,6 +614,10 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
* Update the trace code with the current status. * Update the trace code with the current status.
*/ */
trace_hardirqs_off(); trace_hardirqs_off();
/* Check valid user FS if needed */
addr_limit_user_check();
do { do {
if (likely(thread_flags & _TIF_NEED_RESCHED)) { if (likely(thread_flags & _TIF_NEED_RESCHED)) {
schedule(); schedule();
......
...@@ -86,6 +86,7 @@ struct thread_info { ...@@ -86,6 +86,7 @@ struct thread_info {
#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */
#define TIF_FOREIGN_FPSTATE 3 /* CPU's FP state is not current's */ #define TIF_FOREIGN_FPSTATE 3 /* CPU's FP state is not current's */
#define TIF_UPROBE 4 /* uprobe breakpoint or singlestep */ #define TIF_UPROBE 4 /* uprobe breakpoint or singlestep */
#define TIF_FSCHECK 5 /* Check FS is USER_DS on return */
#define TIF_NOHZ 7 #define TIF_NOHZ 7
#define TIF_SYSCALL_TRACE 8 #define TIF_SYSCALL_TRACE 8
#define TIF_SYSCALL_AUDIT 9 #define TIF_SYSCALL_AUDIT 9
...@@ -107,11 +108,12 @@ struct thread_info { ...@@ -107,11 +108,12 @@ struct thread_info {
#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT)
#define _TIF_SECCOMP (1 << TIF_SECCOMP) #define _TIF_SECCOMP (1 << TIF_SECCOMP)
#define _TIF_UPROBE (1 << TIF_UPROBE) #define _TIF_UPROBE (1 << TIF_UPROBE)
#define _TIF_FSCHECK (1 << TIF_FSCHECK)
#define _TIF_32BIT (1 << TIF_32BIT) #define _TIF_32BIT (1 << TIF_32BIT)
#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ #define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
_TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \ _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \
_TIF_UPROBE) _TIF_UPROBE | _TIF_FSCHECK)
#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ #define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
_TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \ _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \
......
...@@ -45,6 +45,9 @@ static inline void set_fs(mm_segment_t fs) ...@@ -45,6 +45,9 @@ static inline void set_fs(mm_segment_t fs)
{ {
current_thread_info()->addr_limit = fs; current_thread_info()->addr_limit = fs;
/* On user-mode return, check fs is correct */
set_thread_flag(TIF_FSCHECK);
/* /*
* Enable/disable UAO so that copy_to_user() etc can access * Enable/disable UAO so that copy_to_user() etc can access
* kernel memory with the unprivileged instructions. * kernel memory with the unprivileged instructions.
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/tracehook.h> #include <linux/tracehook.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <linux/syscalls.h>
#include <asm/debug-monitors.h> #include <asm/debug-monitors.h>
#include <asm/elf.h> #include <asm/elf.h>
...@@ -749,6 +750,10 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, ...@@ -749,6 +750,10 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,
* Update the trace code with the current status. * Update the trace code with the current status.
*/ */
trace_hardirqs_off(); trace_hardirqs_off();
/* Check valid user FS if needed */
addr_limit_user_check();
do { do {
if (thread_flags & _TIF_NEED_RESCHED) { if (thread_flags & _TIF_NEED_RESCHED) {
schedule(); schedule();
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/user-return-notifier.h> #include <linux/user-return-notifier.h>
#include <linux/uprobes.h> #include <linux/uprobes.h>
#include <linux/livepatch.h> #include <linux/livepatch.h>
#include <linux/syscalls.h>
#include <asm/desc.h> #include <asm/desc.h>
#include <asm/traps.h> #include <asm/traps.h>
...@@ -183,6 +184,8 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs) ...@@ -183,6 +184,8 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs)
struct thread_info *ti = current_thread_info(); struct thread_info *ti = current_thread_info();
u32 cached_flags; u32 cached_flags;
addr_limit_user_check();
if (IS_ENABLED(CONFIG_PROVE_LOCKING) && WARN_ON(!irqs_disabled())) if (IS_ENABLED(CONFIG_PROVE_LOCKING) && WARN_ON(!irqs_disabled()))
local_irq_disable(); local_irq_disable();
......
...@@ -98,6 +98,7 @@ struct thread_info { ...@@ -98,6 +98,7 @@ struct thread_info {
#define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */ #define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */
#define TIF_ADDR32 29 /* 32-bit address space on 64 bits */ #define TIF_ADDR32 29 /* 32-bit address space on 64 bits */
#define TIF_X32 30 /* 32-bit native x86-64 binary */ #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_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
...@@ -122,6 +123,7 @@ struct thread_info { ...@@ -122,6 +123,7 @@ struct thread_info {
#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT)
#define _TIF_ADDR32 (1 << TIF_ADDR32) #define _TIF_ADDR32 (1 << TIF_ADDR32)
#define _TIF_X32 (1 << TIF_X32) #define _TIF_X32 (1 << TIF_X32)
#define _TIF_FSCHECK (1 << TIF_FSCHECK)
/* /*
* work to do in syscall_trace_enter(). Also includes TIF_NOHZ for * work to do in syscall_trace_enter(). Also includes TIF_NOHZ for
...@@ -137,7 +139,8 @@ struct thread_info { ...@@ -137,7 +139,8 @@ struct thread_info {
(_TIF_SYSCALL_TRACE | _TIF_NOTIFY_RESUME | _TIF_SIGPENDING | \ (_TIF_SYSCALL_TRACE | _TIF_NOTIFY_RESUME | _TIF_SIGPENDING | \
_TIF_NEED_RESCHED | _TIF_SINGLESTEP | _TIF_SYSCALL_EMU | \ _TIF_NEED_RESCHED | _TIF_SINGLESTEP | _TIF_SYSCALL_EMU | \
_TIF_SYSCALL_AUDIT | _TIF_USER_RETURN_NOTIFY | _TIF_UPROBE | \ _TIF_SYSCALL_AUDIT | _TIF_USER_RETURN_NOTIFY | _TIF_UPROBE | \
_TIF_PATCH_PENDING | _TIF_NOHZ | _TIF_SYSCALL_TRACEPOINT) _TIF_PATCH_PENDING | _TIF_NOHZ | _TIF_SYSCALL_TRACEPOINT | \
_TIF_FSCHECK)
/* flags to check in __switch_to() */ /* flags to check in __switch_to() */
#define _TIF_WORK_CTXSW \ #define _TIF_WORK_CTXSW \
......
...@@ -26,7 +26,12 @@ ...@@ -26,7 +26,12 @@
#define get_ds() (KERNEL_DS) #define get_ds() (KERNEL_DS)
#define get_fs() (current->thread.addr_limit) #define get_fs() (current->thread.addr_limit)
#define set_fs(x) (current->thread.addr_limit = (x)) static inline void set_fs(mm_segment_t fs)
{
current->thread.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) #define segment_eq(a, b) ((a).seg == (b).seg)
......
...@@ -207,6 +207,22 @@ extern struct trace_event_functions exit_syscall_print_funcs; ...@@ -207,6 +207,22 @@ extern struct trace_event_functions exit_syscall_print_funcs;
} \ } \
static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) 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, asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special,
qid_t id, void __user *addr); qid_t id, void __user *addr);
asmlinkage long sys_time(time_t __user *tloc); asmlinkage long sys_time(time_t __user *tloc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment