Properly handle real SIGTRAPs.
* defs.h (ptrace_setoptions): Variable renamed to ptrace_setoptions_followfork. * process.c (internal_fork): Ditto. * strace.c (ptrace_setoptions_for_all): New variable. (SYSCALLTRAP): New variable. (error_msg_and_die): New function. (test_ptrace_setoptions_for_all): New function. (main): Call test_ptrace_setoptions_for_all() at init. (handle_ptrace_event): Handle PTRACE_EVENT_EXEC (by ignoring it). (trace): Check events and set ptrace options without -f too. Check WSTOPSIG(status) not for SIGTRAP, but for SYSCALLTRAP.
This commit is contained in:
parent
1cd5371d50
commit
3454e4b463
2
defs.h
2
defs.h
@ -504,7 +504,7 @@ typedef enum {
|
|||||||
extern struct tcb **tcbtab;
|
extern struct tcb **tcbtab;
|
||||||
extern int *qual_flags;
|
extern int *qual_flags;
|
||||||
extern int debug, followfork;
|
extern int debug, followfork;
|
||||||
extern unsigned int ptrace_setoptions;
|
extern unsigned int ptrace_setoptions_followfork;
|
||||||
extern int dtime, xflag, qflag;
|
extern int dtime, xflag, qflag;
|
||||||
extern cflag_t cflag;
|
extern cflag_t cflag;
|
||||||
extern int acolumn;
|
extern int acolumn;
|
||||||
|
@ -915,7 +915,7 @@ Process %u resumed (parent %d ready)\n",
|
|||||||
int
|
int
|
||||||
internal_fork(struct tcb *tcp)
|
internal_fork(struct tcb *tcp)
|
||||||
{
|
{
|
||||||
if ((ptrace_setoptions
|
if ((ptrace_setoptions_followfork
|
||||||
& (PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK))
|
& (PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK))
|
||||||
== (PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK))
|
== (PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK))
|
||||||
return 0;
|
return 0;
|
||||||
|
157
strace.c
157
strace.c
@ -33,6 +33,7 @@
|
|||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
@ -83,7 +84,10 @@ extern char *optarg;
|
|||||||
|
|
||||||
|
|
||||||
int debug = 0, followfork = 0;
|
int debug = 0, followfork = 0;
|
||||||
unsigned int ptrace_setoptions = 0;
|
unsigned int ptrace_setoptions_followfork = 0;
|
||||||
|
static unsigned int ptrace_setoptions_for_all = 0;
|
||||||
|
/* Which WSTOPSIG(status) value marks syscall traps? */
|
||||||
|
static unsigned int SYSCALLTRAP = SIGTRAP;
|
||||||
int dtime = 0, xflag = 0, qflag = 0;
|
int dtime = 0, xflag = 0, qflag = 0;
|
||||||
cflag_t cflag = CFLAG_NONE;
|
cflag_t cflag = CFLAG_NONE;
|
||||||
static int iflag = 0, interactive = 0, pflag_seen = 0, rflag = 0, tflag = 0;
|
static int iflag = 0, interactive = 0, pflag_seen = 0, rflag = 0, tflag = 0;
|
||||||
@ -211,6 +215,31 @@ usage: strace [-CdDffhiqrtttTvVxxy] [-a column] [-e expr] ... [-o file]\n\
|
|||||||
exit(exitval);
|
exit(exitval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void error_msg_and_die(const char *fmt, ...)
|
||||||
|
#if defined __GNUC__
|
||||||
|
__attribute__ ((noreturn, format(printf, 1, 2)))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
static void error_msg_and_die(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char *msg;
|
||||||
|
va_list p;
|
||||||
|
|
||||||
|
va_start(p, fmt);
|
||||||
|
msg = NULL;
|
||||||
|
vasprintf(&msg, fmt, p);
|
||||||
|
if (msg) {
|
||||||
|
fprintf(stderr, "%s: %s\n", progname, msg);
|
||||||
|
free(msg);
|
||||||
|
}
|
||||||
|
va_end(p);
|
||||||
|
|
||||||
|
/* TODO? cflag = 0; -- or else cleanup() may print summary */
|
||||||
|
cleanup();
|
||||||
|
fflush(NULL);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SVR4
|
#ifdef SVR4
|
||||||
#ifdef MIPS
|
#ifdef MIPS
|
||||||
void
|
void
|
||||||
@ -702,7 +731,7 @@ startup_child (char **argv)
|
|||||||
* and then see which options are supported by the kernel.
|
* and then see which options are supported by the kernel.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
test_ptrace_setoptions(void)
|
test_ptrace_setoptions_followfork(void)
|
||||||
{
|
{
|
||||||
int pid, expected_grandchild = 0, found_grandchild = 0;
|
int pid, expected_grandchild = 0, found_grandchild = 0;
|
||||||
const unsigned int test_options = PTRACE_O_TRACECLONE |
|
const unsigned int test_options = PTRACE_O_TRACECLONE |
|
||||||
@ -727,7 +756,7 @@ test_ptrace_setoptions(void)
|
|||||||
continue;
|
continue;
|
||||||
else if (errno == ECHILD)
|
else if (errno == ECHILD)
|
||||||
break;
|
break;
|
||||||
perror("test_ptrace_setoptions");
|
perror("test_ptrace_setoptions_followfork");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (tracee_pid != pid) {
|
if (tracee_pid != pid) {
|
||||||
@ -761,9 +790,91 @@ test_ptrace_setoptions(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (expected_grandchild && expected_grandchild == found_grandchild)
|
if (expected_grandchild && expected_grandchild == found_grandchild)
|
||||||
ptrace_setoptions |= test_options;
|
ptrace_setoptions_followfork |= test_options;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test whether the kernel support PTRACE_O_TRACESYSGOOD.
|
||||||
|
* First fork a new child, call ptrace(PTRACE_SETOPTIONS) on it,
|
||||||
|
* and then see whether it will stop with (SIGTRAP | 0x80).
|
||||||
|
*
|
||||||
|
* Use of this option enables correct handling of user-generated SIGTRAPs,
|
||||||
|
* and SIGTRAPs generated by special instructions such as int3 on x86:
|
||||||
|
* _start: .globl _start
|
||||||
|
* int3
|
||||||
|
* movl $42, %ebx
|
||||||
|
* movl $1, %eax
|
||||||
|
* int $0x80
|
||||||
|
* (compile with: "gcc -nostartfiles -nostdlib -o int3 int3.S")
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
test_ptrace_setoptions_for_all(void)
|
||||||
|
{
|
||||||
|
const unsigned int test_options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC;
|
||||||
|
int pid;
|
||||||
|
int it_worked = 0;
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid < 0)
|
||||||
|
error_msg_and_die("fork failed");
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
pid = getpid();
|
||||||
|
if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0)
|
||||||
|
/* "parent, something is deeply wrong!" */
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
kill(pid, SIGSTOP);
|
||||||
|
_exit(0); /* parent should see entry into this syscall */
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int status, tracee_pid;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
tracee_pid = wait(&status);
|
||||||
|
if (tracee_pid <= 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
error_msg_and_die("%s: unexpected wait result %d", __func__, tracee_pid);
|
||||||
|
}
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
break;
|
||||||
|
if (!WIFSTOPPED(status)) {
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
error_msg_and_die("%s: unexpected wait status %x", __func__, status);
|
||||||
|
}
|
||||||
|
if (WSTOPSIG(status) == SIGSTOP) {
|
||||||
|
/*
|
||||||
|
* We don't check "options aren't accepted" error.
|
||||||
|
* If it happens, we'll never get (SIGTRAP | 0x80),
|
||||||
|
* and thus will decide to not use the option.
|
||||||
|
* IOW: the outcome of the test will be correct.
|
||||||
|
*/
|
||||||
|
ptrace(PTRACE_SETOPTIONS, pid, 0L, test_options);
|
||||||
|
}
|
||||||
|
if (WSTOPSIG(status) == (SIGTRAP | 0x80)) {
|
||||||
|
it_worked = 1;
|
||||||
|
}
|
||||||
|
if (ptrace(PTRACE_SYSCALL, pid, 0L, 0L) < 0) {
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
error_msg_and_die("PTRACE_SYSCALL doesn't work");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it_worked) {
|
||||||
|
SYSCALLTRAP = (SIGTRAP | 0x80);
|
||||||
|
ptrace_setoptions_for_all = test_options;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr, "ptrace_setoptions_for_all = %#x\n",
|
||||||
|
ptrace_setoptions_for_all);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr,
|
||||||
|
"Test for PTRACE_O_TRACESYSGOOD failed, giving up using this feature.\n");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -977,16 +1088,17 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
#ifdef LINUX
|
#ifdef LINUX
|
||||||
if (followfork) {
|
if (followfork) {
|
||||||
if (test_ptrace_setoptions() < 0) {
|
if (test_ptrace_setoptions_followfork() < 0) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Test for options supported by PTRACE_SETOPTIONS "
|
"Test for options supported by PTRACE_SETOPTIONS "
|
||||||
"failed, giving up using this feature.\n");
|
"failed, giving up using this feature.\n");
|
||||||
ptrace_setoptions = 0;
|
ptrace_setoptions_followfork = 0;
|
||||||
}
|
}
|
||||||
if (debug)
|
if (debug)
|
||||||
fprintf(stderr, "ptrace_setoptions = %#x\n",
|
fprintf(stderr, "ptrace_setoptions_followfork = %#x\n",
|
||||||
ptrace_setoptions);
|
ptrace_setoptions_followfork);
|
||||||
}
|
}
|
||||||
|
test_ptrace_setoptions_for_all();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check if they want to redirect the output. */
|
/* Check if they want to redirect the output. */
|
||||||
@ -1751,7 +1863,7 @@ int sig;
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
error = ptrace_restart(PTRACE_CONT, tcp,
|
error = ptrace_restart(PTRACE_CONT, tcp,
|
||||||
WSTOPSIG(status) == SIGTRAP ? 0
|
WSTOPSIG(status) == SYSCALLTRAP ? 0
|
||||||
: WSTOPSIG(status));
|
: WSTOPSIG(status));
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
break;
|
break;
|
||||||
@ -2408,6 +2520,11 @@ handle_ptrace_event(int status, struct tcb *tcp)
|
|||||||
}
|
}
|
||||||
return handle_new_child(tcp, childpid, 0);
|
return handle_new_child(tcp, childpid, 0);
|
||||||
}
|
}
|
||||||
|
if (status >> 16 == PTRACE_EVENT_EXEC) {
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr, "PTRACE_EVENT_EXEC on pid %d (ignored)\n", tcp->pid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -2597,7 +2714,7 @@ Process %d attached (waiting for parent)\n",
|
|||||||
fprintf(stderr, "pid %u stopped, [%s]\n",
|
fprintf(stderr, "pid %u stopped, [%s]\n",
|
||||||
pid, signame(WSTOPSIG(status)));
|
pid, signame(WSTOPSIG(status)));
|
||||||
|
|
||||||
if (ptrace_setoptions && (status >> 16)) {
|
if (status >> 16) {
|
||||||
if (handle_ptrace_event(status, tcp) != 1)
|
if (handle_ptrace_event(status, tcp) != 1)
|
||||||
goto tracing;
|
goto tracing;
|
||||||
}
|
}
|
||||||
@ -2630,16 +2747,24 @@ Process %d attached (waiting for parent)\n",
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef LINUX
|
#ifdef LINUX
|
||||||
if (followfork && (tcp->parent == NULL) && ptrace_setoptions)
|
int options = ptrace_setoptions_for_all;
|
||||||
if (ptrace(PTRACE_SETOPTIONS, tcp->pid,
|
if (followfork && (tcp->parent == NULL))
|
||||||
NULL, ptrace_setoptions) < 0 &&
|
options |= ptrace_setoptions_followfork;
|
||||||
errno != ESRCH)
|
if (options) {
|
||||||
ptrace_setoptions = 0;
|
if (debug)
|
||||||
|
fprintf(stderr, "setting opts %x on pid %d\n", options, tcp->pid);
|
||||||
|
if (ptrace(PTRACE_SETOPTIONS, tcp->pid, NULL, options) < 0) {
|
||||||
|
if (errno != ESRCH) {
|
||||||
|
/* Should never happen, really */
|
||||||
|
error_msg_and_die("PTRACE_SETOPTIONS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
goto tracing;
|
goto tracing;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WSTOPSIG(status) != SIGTRAP) {
|
if (WSTOPSIG(status) != SYSCALLTRAP) {
|
||||||
if (WSTOPSIG(status) == SIGSTOP &&
|
if (WSTOPSIG(status) == SIGSTOP &&
|
||||||
(tcp->flags & TCB_SIGTRAPPED)) {
|
(tcp->flags & TCB_SIGTRAPPED)) {
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user