Do post-attach initialization earlier; fix "we ignore SIGSTOP on NOMMU" bug

We set ptrace options when we see post-attach SIGSTOP.
This is wrong: it's better to set them right away on the very first
stop (whichever it will be). It also will make adding SEIZE support easier,
since SEIZE has no post-attach SIGSTOP.

We do it by adding a new bit, TCB_IGNORE_ONE_SIGSTOP, and treating
TCB_STARTUP and TCB_IGNORE_ONE_SIGSTOP as two slightly different things.

* defs.h: Add a new flag bit, TCB_IGNORE_ONE_SIGSTOP.
* process.c (internal_fork): Set TCB_IGNORE_ONE_SIGSTOP on a newly added child.
* strace.c (startup_attach): Set TCB_IGNORE_ONE_SIGSTOP after attach.
Fix a case when "strace -p PID" found PID dead but sone other of its threads
still alive.
(startup_child): Set TCB_IGNORE_ONE_SIGSTOP after attach, _if needed_.
This fixes a bogus case where we can ignore a _real_ SIGSTOP on NOMMU.
(detach): Perform anti-SIGSTOP dance only if TCB_IGNORE_ONE_SIGSTOP is set,
not if TCB_STARTUP is set.
(trace): Set TCB_IGNORE_ONE_SIGSTOP after attach.
Clear TCB_STARTUP and initialize tracee on the very first tracee stop.
Clear TCB_IGNORE_ONE_SIGSTOP when SIGSTOP is seen.

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
This commit is contained in:
Denys Vlasenko 2011-09-05 14:05:46 +02:00
parent d116a73386
commit f88837a666
3 changed files with 53 additions and 35 deletions

15
defs.h
View File

@ -436,8 +436,15 @@ struct tcb {
};
/* TCB flags */
#define TCB_STARTUP 00001 /* We have just begun ptracing this process */
#define TCB_INUSE 00002 /* This table entry is in use */
#define TCB_INUSE 00001 /* This table entry is in use */
/* We have attached to this process, but did not see it stopping yet.
* (If this bit is not set, we either didn't attach yet,
* or we did attach to it, already saw it stopping at least once,
* did some init work on it and cleared this bit. TODO: maybe it makes sense
* to split these two states?)
*/
#define TCB_STARTUP 00002
#define TCB_IGNORE_ONE_SIGSTOP 00004 /* Next SIGSTOP is to be ignored */
/*
* Are we in system call entry or in syscall exit?
*
@ -456,8 +463,8 @@ struct tcb {
*
* Use entering(tcp) / exiting(tcp) to check this bit to make code more readable.
*/
#define TCB_INSYSCALL 00004
#define TCB_ATTACHED 00010 /* Process is not our own child */
#define TCB_INSYSCALL 00010
#define TCB_ATTACHED 00020 /* Process is not our own child */
#define TCB_BPTSET 00100 /* "Breakpoint" set after fork(2) */
#define TCB_SIGTRAPPED 00200 /* Process wanted to block SIGTRAP */
#define TCB_REPRINT 01000 /* We should reprint this syscall on exit */

View File

@ -859,7 +859,7 @@ internal_fork(struct tcb *tcp)
}
#endif /* !oldway */
#endif /* SUNOS4 */
tcpchild->flags |= TCB_ATTACHED | TCB_STARTUP;
tcpchild->flags |= TCB_ATTACHED | TCB_STARTUP | TCB_IGNORE_ONE_SIGSTOP;
/* Child has BPT too, must be removed on first occasion */
if (bpt) {
tcpchild->flags |= TCB_BPTSET;

View File

@ -491,7 +491,7 @@ startup_attach(void)
cur_tcp = tcp;
if (tid != tcp->pid)
cur_tcp = alloctcb(tid);
cur_tcp->flags |= TCB_ATTACHED | TCB_STARTUP;
cur_tcp->flags |= TCB_ATTACHED | TCB_STARTUP | TCB_IGNORE_ONE_SIGSTOP;
}
closedir(dir);
if (interactive) {
@ -512,6 +512,13 @@ startup_attach(void)
: "Process %u attached - interrupt to quit\n",
tcp->pid, ntid);
}
if (!(tcp->flags & TCB_STARTUP)) {
/* -p PID, we failed to attach to PID itself
* but did attach to some of its sibling threads.
* Drop PID's tcp.
*/
droptcb(tcp);
}
continue;
} /* if (opendir worked) */
} /* if (-f) */
@ -521,7 +528,7 @@ startup_attach(void)
droptcb(tcp);
continue;
}
tcp->flags |= TCB_STARTUP;
tcp->flags |= TCB_STARTUP | TCB_IGNORE_ONE_SIGSTOP;
if (debug)
fprintf(stderr, "attach to pid %d (main) succeeded\n", tcp->pid);
@ -707,6 +714,9 @@ startup_child(char **argv)
if (!daemonized_tracer) {
tcp = alloctcb(pid);
if (!strace_vforked)
tcp->flags |= TCB_STARTUP | TCB_IGNORE_ONE_SIGSTOP;
else
tcp->flags |= TCB_STARTUP;
}
else {
@ -1677,11 +1687,11 @@ detach(struct tcb *tcp, int sig)
#define PTRACE_DETACH PTRACE_SUNDETACH
#endif
/*
* On TCB_STARTUP we did PTRACE_ATTACH but still did not get the
* expected SIGSTOP. We must catch exactly one as otherwise the
* detached process would be left stopped (process state T).
* We did PTRACE_ATTACH but possibly didn't see the expected SIGSTOP.
* We must catch exactly one as otherwise the detached process
* would be left stopped (process state T).
*/
catch_sigstop = (tcp->flags & TCB_STARTUP);
catch_sigstop = (tcp->flags & TCB_IGNORE_ONE_SIGSTOP);
error = ptrace(PTRACE_DETACH, tcp->pid, (char *) 1, sig);
if (error == 0) {
/* On a clear day, you can see forever. */
@ -2411,7 +2421,7 @@ trace()
child so that we know how to do clearbpt
in the child. */
tcp = alloctcb(pid);
tcp->flags |= TCB_ATTACHED | TCB_STARTUP;
tcp->flags |= TCB_ATTACHED | TCB_STARTUP | TCB_IGNORE_ONE_SIGSTOP;
if (!qflag)
fprintf(stderr, "Process %d attached\n",
pid);
@ -2478,28 +2488,10 @@ trace()
continue;
}
if (status >> 16) {
/* Ptrace event (we ignore all of them for now) */
goto restart_tracee_with_sig_0;
}
sig = WSTOPSIG(status);
/*
* Interestingly, the process may stop
* with STOPSIG equal to some other signal
* than SIGSTOP if we happend to attach
* just before the process takes a signal.
* A no-MMU vforked child won't send up a signal,
* so skip the first (lost) execve notification.
*/
if ((tcp->flags & TCB_STARTUP) &&
(sig == SIGSTOP || strace_vforked)) {
/*
* This flag is there to keep us in sync.
* Next time this process stops it should
* really be entering a system call.
*/
/* Is this the very first time we see this tracee stopped? */
if (tcp->flags & TCB_STARTUP) {
if (debug)
fprintf(stderr, "pid %d has TCB_STARTUP, initializing it\n", tcp->pid);
tcp->flags &= ~TCB_STARTUP;
if (tcp->flags & TCB_BPTSET) {
/*
@ -2525,6 +2517,25 @@ trace()
}
}
#endif
}
if (((unsigned)status >> 16) != 0) {
/* Ptrace event (we ignore all of them for now) */
goto restart_tracee_with_sig_0;
}
sig = WSTOPSIG(status);
/* Is this post-attach SIGSTOP?
* Interestingly, the process may stop
* with STOPSIG equal to some other signal
* than SIGSTOP if we happend to attach
* just before the process takes a signal.
*/
if (sig == SIGSTOP && (tcp->flags & TCB_IGNORE_ONE_SIGSTOP)) {
if (debug)
fprintf(stderr, "ignored SIGSTOP on pid %d\n", tcp->pid);
tcp->flags &= ~TCB_IGNORE_ONE_SIGSTOP;
goto restart_tracee_with_sig_0;
}