Add the new internal ptrace flags, PT_SINGLE_STEP and PT_SINGLE_BLOCK.
Like PT_SYSCALL_TRACE, this is needed to avoid the unnecessary ptrace reports when TIF_SINGLESTEP was set by another engine, not by ptrace. Also, we need these bits to coordinate the user_*_single_step() calls from ptrace and utrace.
TODO: update the !x86 ptrace code which does user_disable_single_step().
Signed-off-by: Oleg Nesterov oleg@redhat.com --- arch/x86/kernel/ptrace.c | 1 + include/linux/ptrace.h | 5 ++++- include/linux/tracehook.h | 7 +++++-- kernel/ptrace.c | 3 +++ 4 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 8252879..d1557dc 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -808,6 +808,7 @@ static int ioperm_get(struct task_struct *target, */ void ptrace_disable(struct task_struct *child) { + child->ptrace &= ~(PT_SINGLE_STEP | PT_SINGLE_BLOCK); user_disable_single_step(child); #ifdef TIF_SYSCALL_EMU clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index c10f610..2743315 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -105,6 +105,8 @@ #define PT_TRACE_MASK 0x000003f4
#define PT_SYSCALL_TRACE 0x00020000 +#define PT_SINGLE_STEP 0x00040000 +#define PT_SINGLE_BLOCK 0x00080000
/* single stepping state bits (used on ARM and PA-RISC) */ #define PT_SINGLESTEP_BIT 31 @@ -229,7 +231,8 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
if (unlikely(ptrace) && current->ptrace) { child->ptrace = current->ptrace; - child->ptrace &= ~PT_SYSCALL_TRACE; + child->ptrace &= + ~(PT_SYSCALL_TRACE | PT_SINGLE_STEP | PT_SINGLE_BLOCK); __ptrace_link(child, current->parent);
if (child->ptrace & PT_SEIZED) diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index eb9fe30..21c8ca2 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -104,6 +104,9 @@ static inline __must_check int tracehook_report_syscall_entry( return 0; }
+#define ptrace_wants_step() \ + (current->ptrace & (PT_SINGLE_STEP | PT_SINGLE_BLOCK)) + /** * tracehook_report_syscall_exit - task has just finished a system call * @regs: user register state of current task @@ -126,7 +129,7 @@ static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step) if (task_utrace_flags(current) & UTRACE_EVENT(SYSCALL_EXIT)) utrace_report_syscall_exit(regs);
- if (step) { + if (step && ptrace_wants_step()) { siginfo_t info; user_single_step_siginfo(current, regs, &info); force_sig_info(SIGTRAP, &info, current); @@ -157,7 +160,7 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info, { if (task_utrace_flags(current)) utrace_signal_handler(current, stepping); - if (stepping) + if (stepping && ptrace_wants_step()) ptrace_notify(SIGTRAP); }
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 69850e9..d250a71 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -630,13 +630,16 @@ static int ptrace_resume(struct task_struct *child, long request, clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); #endif
+ child->ptrace &= ~(PT_SINGLE_STEP | PT_SINGLE_BLOCK); if (is_singleblock(request)) { if (unlikely(!arch_has_block_step())) return -EIO; + child->ptrace |= PT_SINGLE_BLOCK; user_enable_block_step(child); } else if (is_singlestep(request) || is_sysemu_singlestep(request)) { if (unlikely(!arch_has_single_step())) return -EIO; + child->ptrace |= PT_SINGLE_STEP; user_enable_single_step(child); } else { user_disable_single_step(child);