180 lines
5.5 KiB
Diff
180 lines
5.5 KiB
Diff
From d275fc0312255ddffa878b70da34d945b4fd8212 Mon Sep 17 00:00:00 2001
|
|
From: "Dmitry V. Levin" <ldv@strace.io>
|
|
Date: Mon, 27 Nov 2023 08:00:00 +0000
|
|
Subject: [PATCH] Factor out detach_interrupted_or_stopped() from detach()
|
|
|
|
* src/strace.c (detach_interrupted_or_stopped): New function.
|
|
(detach): Use it.
|
|
|
|
Reference: https://github.com/strace/strace/commit/d275fc0312255ddffa878b70da34d945b4fd8212
|
|
Conflict: NA
|
|
---
|
|
src/strace.c | 136 +++++++++++++++++++++++++++------------------------
|
|
1 file changed, 72 insertions(+), 64 deletions(-)
|
|
|
|
diff --git a/src/strace.c b/src/strace.c
|
|
index 385cbaa72..ef3360b95 100644
|
|
--- a/src/strace.c
|
|
+++ b/src/strace.c
|
|
@@ -1221,6 +1221,77 @@ interrupt_or_stop(struct tcb *tcp)
|
|
return false;
|
|
}
|
|
|
|
+/* Returns true if the tracee can be passed to droptcb. */
|
|
+static bool
|
|
+detach_interrupted_or_stopped(struct tcb *tcp, int status)
|
|
+{
|
|
+ if (!WIFSTOPPED(status)) {
|
|
+ /*
|
|
+ * Tracee exited or was killed by signal.
|
|
+ * We shouldn't normally reach this place:
|
|
+ * we don't want to consume exit status.
|
|
+ * Consider "strace -p PID" being ^C-ed:
|
|
+ * we want merely to detach from PID.
|
|
+ *
|
|
+ * However, we _can_ end up here if tracee
|
|
+ * was SIGKILLed.
|
|
+ */
|
|
+ return true;
|
|
+ }
|
|
+ unsigned int sig = WSTOPSIG(status);
|
|
+ debug_msg("detach wait: event:%d sig:%d",
|
|
+ (unsigned) status >> 16, sig);
|
|
+ if (use_seize) {
|
|
+ unsigned event = (unsigned)status >> 16;
|
|
+ if (event == PTRACE_EVENT_STOP /*&& sig == SIGTRAP*/) {
|
|
+ /*
|
|
+ * sig == SIGTRAP: PTRACE_INTERRUPT stop.
|
|
+ * sig == other: process was already stopped
|
|
+ * with this stopping sig (see tests/detach-stopped).
|
|
+ * Looks like re-injecting this sig is not necessary
|
|
+ * in DETACH for the tracee to remain stopped.
|
|
+ */
|
|
+ sig = 0;
|
|
+ }
|
|
+ /*
|
|
+ * PTRACE_INTERRUPT is not guaranteed to produce
|
|
+ * the above event if other ptrace-stop is pending.
|
|
+ * See tests/detach-sleeping testcase:
|
|
+ * strace got SIGINT while tracee is sleeping.
|
|
+ * We sent PTRACE_INTERRUPT.
|
|
+ * We see syscall exit, not PTRACE_INTERRUPT stop.
|
|
+ * We won't get PTRACE_INTERRUPT stop
|
|
+ * if we would CONT now. Need to DETACH.
|
|
+ */
|
|
+ if (sig == syscall_trap_sig)
|
|
+ sig = 0;
|
|
+ /* else: not sure in which case we can be here.
|
|
+ * Signal stop? Inject it while detaching.
|
|
+ */
|
|
+ ptrace_restart(PTRACE_DETACH, tcp, sig);
|
|
+ return true;
|
|
+ }
|
|
+ /* Note: this check has to be after use_seize check */
|
|
+ /* (else, in use_seize case SIGSTOP will be mistreated) */
|
|
+ if (sig == SIGSTOP) {
|
|
+ /* Detach, suppressing SIGSTOP */
|
|
+ ptrace_restart(PTRACE_DETACH, tcp, 0);
|
|
+ return true;
|
|
+ }
|
|
+ if (sig == syscall_trap_sig)
|
|
+ sig = 0;
|
|
+ /* Can't detach just yet, may need to wait for SIGSTOP */
|
|
+ int error = ptrace_restart(PTRACE_CONT, tcp, sig);
|
|
+ if (error < 0) {
|
|
+ /* Should not happen.
|
|
+ * Note: ptrace_restart returns 0 on ESRCH, so it's not it.
|
|
+ * ptrace_restart already emitted error message.
|
|
+ */
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
/* Detach traced process.
|
|
* Never call DETACH twice on the same process as both unattached and
|
|
* attached-unstopped processes give the same ESRCH. For unattached process we
|
|
@@ -1240,7 +1311,6 @@ detach(struct tcb *tcp)
|
|
*/
|
|
for (;;) {
|
|
int status;
|
|
- unsigned int sig;
|
|
if (waitpid(tcp->pid, &status, __WALL) < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
@@ -1252,70 +1322,8 @@ detach(struct tcb *tcp)
|
|
perror_func_msg("waitpid(%u)", tcp->pid);
|
|
break;
|
|
}
|
|
- if (!WIFSTOPPED(status)) {
|
|
- /*
|
|
- * Tracee exited or was killed by signal.
|
|
- * We shouldn't normally reach this place:
|
|
- * we don't want to consume exit status.
|
|
- * Consider "strace -p PID" being ^C-ed:
|
|
- * we want merely to detach from PID.
|
|
- *
|
|
- * However, we _can_ end up here if tracee
|
|
- * was SIGKILLed.
|
|
- */
|
|
- break;
|
|
- }
|
|
- sig = WSTOPSIG(status);
|
|
- debug_msg("detach wait: event:%d sig:%d",
|
|
- (unsigned) status >> 16, sig);
|
|
- if (use_seize) {
|
|
- unsigned event = (unsigned)status >> 16;
|
|
- if (event == PTRACE_EVENT_STOP /*&& sig == SIGTRAP*/) {
|
|
- /*
|
|
- * sig == SIGTRAP: PTRACE_INTERRUPT stop.
|
|
- * sig == other: process was already stopped
|
|
- * with this stopping sig (see tests/detach-stopped).
|
|
- * Looks like re-injecting this sig is not necessary
|
|
- * in DETACH for the tracee to remain stopped.
|
|
- */
|
|
- sig = 0;
|
|
- }
|
|
- /*
|
|
- * PTRACE_INTERRUPT is not guaranteed to produce
|
|
- * the above event if other ptrace-stop is pending.
|
|
- * See tests/detach-sleeping testcase:
|
|
- * strace got SIGINT while tracee is sleeping.
|
|
- * We sent PTRACE_INTERRUPT.
|
|
- * We see syscall exit, not PTRACE_INTERRUPT stop.
|
|
- * We won't get PTRACE_INTERRUPT stop
|
|
- * if we would CONT now. Need to DETACH.
|
|
- */
|
|
- if (sig == syscall_trap_sig)
|
|
- sig = 0;
|
|
- /* else: not sure in which case we can be here.
|
|
- * Signal stop? Inject it while detaching.
|
|
- */
|
|
- ptrace_restart(PTRACE_DETACH, tcp, sig);
|
|
- break;
|
|
- }
|
|
- /* Note: this check has to be after use_seize check */
|
|
- /* (else, in use_seize case SIGSTOP will be mistreated) */
|
|
- if (sig == SIGSTOP) {
|
|
- /* Detach, suppressing SIGSTOP */
|
|
- ptrace_restart(PTRACE_DETACH, tcp, 0);
|
|
+ if (detach_interrupted_or_stopped(tcp, status))
|
|
break;
|
|
- }
|
|
- if (sig == syscall_trap_sig)
|
|
- sig = 0;
|
|
- /* Can't detach just yet, may need to wait for SIGSTOP */
|
|
- error = ptrace_restart(PTRACE_CONT, tcp, sig);
|
|
- if (error < 0) {
|
|
- /* Should not happen.
|
|
- * Note: ptrace_restart returns 0 on ESRCH, so it's not it.
|
|
- * ptrace_restart already emitted error message.
|
|
- */
|
|
- break;
|
|
- }
|
|
}
|
|
|
|
drop:
|
|
--
|
|
2.33.0
|
|
|