7
#include <sys/ptrace.h>
12
#define NUM_CHLD_SYSCALLS 10
14
#define PARENT_TRACE 0
16
#define HELPER_TRACE 2
18
extern char **environ;
20
int interp_status(int status)
24
if (WIFEXITED(status)) {
25
if (WEXITSTATUS(status) == 0) {
28
rc = -WEXITSTATUS(status);
31
rc = -ECONNABORTED; /* overload to mean child signal */
37
/* return 0 on success. Child failure -errorno, parent failure errno */
38
int do_parent(pid_t pid, int trace, int num_syscall)
47
fprintf(stderr, "FAIL: unexpected alarm already set\n");
51
//fprintf(stderr, "waiting ... ");
52
if (waitpid(pid, &status, WUNTRACED) == -1)
54
if (!WIFSTOPPED(status))
55
return interp_status(status);
56
//fprintf(stderr, " done initial wait\n");
58
/* this sends a child SIGSTOP */
59
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
60
perror("FAIL: parent ptrace(PTRACE_ATTACH) failed - ");
64
/* continue child so it can attach to parent */
66
//fprintf(stderr, "waiting2 ... ");
67
if (waitpid(pid, &status, WUNTRACED) == -1)
69
//fprintf(stderr, " done\n");
71
if (!WIFSTOPPED(status))
72
return interp_status(status);
76
for (i = 0; i < num_syscall * 2; i++){
77
/* this will restart stopped child */
78
if (ptrace(PTRACE_SYSCALL, pid, NULL, 0) == -1) {
79
perror("FAIL: parent ptrace(PTRACE_SINGLESTEP) failed - ");
83
//fprintf(stderr, "waiting3 ... ");
84
if (waitpid(pid, &status, WUNTRACED) == -1)
86
//fprintf(stderr, " done\n");
88
if (!WIFSTOPPED(status))
89
return interp_status(status);
91
memset(®s, 0, sizeof(regs));
92
if (ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1) {
93
perror("FAIL: parent ptrace(PTRACE_GETREGS) failed - ");
98
if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) {
99
perror("FAIL: parent ptrace(PTRACE_DETACH) failed - ");
105
/* returns 0 on success or error code of failure */
106
int do_child(char *argv[], int child_trace, int helper)
109
/* for helper we want to transition before ptrace occurs
110
* so don't stop here, let the helper do that
113
putenv("_tracer=child");
115
putenv("_tracer=parent");
117
//fprintf(stderr, "child trace %d\n", child_trace);
119
/* stop child to ensure it doesn't finish before it is traced */
120
if (raise(SIGSTOP) != 0){
121
perror("FAIL: child/helper SIGSTOP itself failed -");
126
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1){
127
perror("FAIL: child ptrace(PTRACE_TRACEME) failed - ");
130
if (raise(SIGSTOP) != 0){
131
perror("FAIL: child SIGSTOP itself failed -");
134
/* ok we're stopped, wait for parent to trace (continue) us */
139
execve(argv[0], argv, environ);
141
perror("FAIL: child exec failed - ");
146
/* make pid_t global so the alarm handler can kill off children */
149
void sigalrm_handler(int sig) {
150
fprintf(stderr, "FAIL: parent timed out waiting for child\n");
155
int main(int argc, char *argv[])
157
int parent_trace = 1,
159
num_syscall = NUM_CHLD_SYSCALLS,
162
const char *usage = "usage: %s [-c] [-n #syscall] program [args ...]\n";
165
if (signal(SIGALRM, sigalrm_handler) == SIG_ERR) {
166
perror ("FAIL - signal failed: ");
172
opt = getopt(argc, argv, "chn:");
177
case 'c': parent_trace = 0;
179
case 'h': use_helper = 1;
181
case 'n': num_syscall = atoi(optarg);
184
fprintf(stderr, usage, argv[0]);
190
fprintf(stderr, usage, argv[0]);
194
args = &argv[optind];
197
if (pid > 0){ /*parent */
200
ret = do_parent(pid, parent_trace, num_syscall);
206
waitpid(pid, &stat, 0);
210
perror("FAIL: parent failed: ");
211
} else if (ret == 0) {
214
} else if (ret == -ECONNABORTED) {
216
perror("FAIL: child killed: ");
219
perror("FAIL: child failed: ");
221
} else if (pid == 0) { /* child */
222
if (do_child(args, !parent_trace, use_helper))
226
perror("FAIL: fork failed - ");