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(a)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 807c2a2..7ab475f 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -807,6 +807,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 98d995d..65b1e4f 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -91,6 +91,8 @@
#define PT_TRACE_MASK 0x000003f4
#define PT_SYSCALL_TRACE 0x00010000
+#define PT_SINGLE_STEP 0x00020000
+#define PT_SINGLE_BLOCK 0x00040000
/* single stepping state bits (used on ARM and PA-RISC) */
#define PT_SINGLESTEP_BIT 31
@@ -189,7 +191,8 @@ static inline void ptrace_init_task(struct task_struct *child, bool
ptrace)
child->ptrace = 0;
if (unlikely(ptrace) && (current->ptrace & PT_PTRACED)) {
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);
}
diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
index 6ce7a37..06edb52 100644
--- a/include/linux/tracehook.h
+++ b/include/linux/tracehook.h
@@ -121,6 +121,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
@@ -143,7 +146,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);
@@ -436,7 +439,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 209ea2d..44908d0 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -575,13 +575,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);
--
1.5.5.1
Show replies by date