Skip to content

Commit b9cd18d

Browse files
htejuntorvalds
authored andcommitted
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>
1 parent 5170a3b commit b9cd18d

File tree

2 files changed

+19
-0
lines changed

2 files changed

+19
-0
lines changed

Diff for: arch/x86/include/asm/ptrace.h

+16
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,22 @@ static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
231231

232232
#define ARCH_HAS_USER_SINGLE_STEP_INFO
233233

234+
/*
235+
* When hitting ptrace_stop(), we cannot return using SYSRET because
236+
* that does not restore the full CPU state, only a minimal set. The
237+
* ptracer can change arbitrary register values, which is usually okay
238+
* because the usual ptrace stops run off the signal delivery path which
239+
* forces IRET; however, ptrace_event() stops happen in arbitrary places
240+
* in the kernel and don't force IRET path.
241+
*
242+
* So force IRET path after a ptrace stop.
243+
*/
244+
#define arch_ptrace_stop_needed(code, info) \
245+
({ \
246+
set_thread_flag(TIF_NOTIFY_RESUME); \
247+
false; \
248+
})
249+
234250
struct user_desc;
235251
extern int do_get_thread_area(struct task_struct *p, int idx,
236252
struct user_desc __user *info);

Diff for: include/linux/ptrace.h

+3
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,9 @@ static inline void user_single_step_siginfo(struct task_struct *tsk,
334334
* calling arch_ptrace_stop() when it would be superfluous. For example,
335335
* if the thread has not been back to user mode since the last stop, the
336336
* thread state might indicate that nothing needs to be done.
337+
*
338+
* This is guaranteed to be invoked once before a task stops for ptrace and
339+
* may include arch-specific operations necessary prior to a ptrace stop.
337340
*/
338341
#define arch_ptrace_stop_needed(code, info) (0)
339342
#endif

0 commit comments

Comments
 (0)