CVE-2014-4699: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (CWE-362)
value: The Linux kernel before 3.15.4 on Intel processors does not properly restrict use of a non-canonical value for the saved RIP address in the case of a system call that does not use IRET, which allows local users to leverage a race condition and gain privileges, or cause a denial of service (double fault), via a crafted application that makes ptrace and fork system calls.
CWE description: The product contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently.
Published: 2014-07-09T11:07Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: arch/x86/include/asm/ptrace.h, include/linux/ptrace.h
Commit date: 2014-07-03 17:27:23 -0700
Commit message: ptrace,x86: force IRET path after a ptrace_stop()

The 'sysret' fastpath does not correctly restore even all regular
registers, much less any segment registers or reflags values.  That is
very much part of why it's faster than 'iret'.

Normally that isn't a problem, because the normal ptrace() interface
catches the process using the signal handler infrastructure, which
always returns with an iret.

However, some paths can get caught using ptrace_event() instead of the
signal path, and for those we need to make sure that we aren't going to
return to user space using 'sysret'.  Otherwise the modifications that
may have been done to the register set by the tracer wouldn't
necessarily take effect.

Fix it by forcing IRET path by setting TIF_NOTIFY_RESUME from
arch_ptrace_stop_needed() which is invoked from ptrace_stop().

Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Andy Lutomirski <luto@amacapital.net>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: stable@vger.kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Diff: diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h
index 14fd6fd75a19b8..6205f0c434dbab 100644
--- a/arch/x86/include/asm/ptrace.h
+++ b/arch/x86/include/asm/ptrace.h
@@ -229,10 +229,26 @@ static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
 #define arch_has_block_step()	(boot_cpu_data.x86 >= 6)
 #endif
 
 #define ARCH_HAS_USER_SINGLE_STEP_INFO
 
+/*
+ * When hitting ptrace_stop(), we cannot return using SYSRET because
+ * that does not restore the full CPU state, only a minimal set.  The
+ * ptracer can change arbitrary register values, which is usually okay
+ * because the usual ptrace stops run off the signal delivery path which
+ * forces IRET; however, ptrace_event() stops happen in arbitrary places
+ * in the kernel and don't force IRET path.
+ *
+ * So force IRET path after a ptrace stop.
+ */
+#define arch_ptrace_stop_needed(code, info)				\
+({									\
+	set_thread_flag(TIF_NOTIFY_RESUME);				\
+	false;								\
+})
+
 struct user_desc;
 extern int do_get_thread_area(struct task_struct *p, int idx,
 			      struct user_desc __user *info);
 extern int do_set_thread_area(struct task_struct *p, int idx,
 			      struct user_desc __user *info, int can_allocate);
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 077904c8b70de8..cc79eff4a1adbb 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -332,10 +332,13 @@ static inline void user_single_step_siginfo(struct task_struct *tsk,
  * arch_ptrace_stop() is never required, or always is.  On machines where
  * this makes sense, it should be defined to a quick test to optimize out
  * calling arch_ptrace_stop() when it would be superfluous.  For example,
  * if the thread has not been back to user mode since the last stop, the
  * thread state might indicate that nothing needs to be done.
+ *
+ * This is guaranteed to be invoked once before a task stops for ptrace and
+ * may include arch-specific operations necessary prior to a ptrace stop.
  */
 #define arch_ptrace_stop_needed(code, info)	(0)
 #endif
 
 #ifndef arch_ptrace_stop

Fixed code: /*
 * When hitting ptrace_stop(), we cannot return using SYSRET because
 * that does not restore the full CPU state, only a minimal set.  The
 * ptracer can change arbitrary register values, which is usually okay
 * because the usual ptrace stops run off the signal delivery path which
 * forces IRET; however, ptrace_event() stops happen in arbitrary places
 * in the kernel and don't force IRET path.
 *
 * So force IRET path after a ptrace stop.
 */
#define arch_ptrace_stop_needed(code, info)				\
({									\
	set_thread_flag(TIF_NOTIFY_RESUME);				\
	false;								\
})

 *
 * This is guaranteed to be invoked once before a task stops for ptrace and
 * may include arch-specific operations necessary prior to a ptrace stop.