diff options
| author | Mattias Andrée <maandree@kth.se> | 2020-05-31 23:22:22 +0200 | 
|---|---|---|
| committer | Mattias Andrée <maandree@kth.se> | 2020-05-31 23:22:22 +0200 | 
| commit | be1c092cd99a023b3d4178066925b644feb0565f (patch) | |
| tree | ea5fd38c39aacdc93b55fa8fc9271ad523032f7c | |
| parent | m (diff) | |
| download | sctrace-be1c092cd99a023b3d4178066925b644feb0565f.tar.gz sctrace-be1c092cd99a023b3d4178066925b644feb0565f.tar.bz2 sctrace-be1c092cd99a023b3d4178066925b644feb0565f.tar.xz | |
Tracing fork children does not work
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to '')
| -rw-r--r-- | common.h | 8 | ||||
| -rw-r--r-- | process.c | 31 | ||||
| -rw-r--r-- | sctrace.c | 248 | ||||
| -rw-r--r-- | util.c | 53 | 
4 files changed, 187 insertions, 153 deletions
| @@ -79,10 +79,11 @@ enum state {  struct process {  	pid_t pid; -	pid_t thread_group_leader; +	pid_t thread_leader;  	struct process *next;  	struct process *prev;  	enum state state; +	int silent_until_execed;  	/* Syscall data */  	unsigned long long int scall; @@ -119,4 +120,7 @@ void remove_process(struct process *proc);  /* util.c */  void setup_trace_output(FILE *fp, int multiprocess);  void tprintf(struct process *proc, const char *fmt, ...); -_Noreturn void eprintf(const char *fmt, ...); +void weprintf(const char *fmt, ...); +#define eprintf(...) (weprintf(__VA_ARGS__), exit(1)) +#define eprintf_and_kill(PID, ...) (weprintf(__VA_ARGS__), kill((PID), SIGKILL), exit(1)) +FILE *xfopen(const char *file, const char *mode); @@ -29,28 +29,31 @@ struct process *  add_process(pid_t pid, unsigned long int trace_options)  {          struct process *proc; -	int saved_errno; +	int status; +          proc = calloc(1, sizeof(*proc));  	if (!proc)                  eprintf("calloc: %s\n");          proc->pid = pid; -	if (waitpid(pid, NULL, 0) < 0) { -		eprintf("waitpid <child> NULL 0:"); -		kill(pid, SIGKILL); -		exit(1); -	} -	if (ptrace(PTRACE_SETOPTIONS, pid, 0, trace_options)) { -		saved_errno = errno; -		kill(pid, SIGKILL); -		errno = saved_errno; -		eprintf("ptrace PTRACE_SETOPTIONS %ju 0 ...:", (uintmax_t)pid); -	} -	if (ptrace(PTRACE_SYSCALL, pid, NULL, 0)) -		eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)pid);          proc->next = &tail;  	proc->prev = tail.prev;  	proc->prev->next = proc;  	tail.prev = proc; + +	while (waitpid(pid, &status, WSTOPPED) != pid) { +		if (errno == EINTR) +			continue; +		eprintf_and_kill(pid, "waitpid %ju <buffer> WSTOPPED:", (uintmax_t)pid); +	} +	if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP) +		eprintf_and_kill(pid, "unexpected return of waitpid %ju <buffer> WSTOPPED:", (uintmax_t)pid); +	if (ptrace(PTRACE_SEIZE, pid, 0, trace_options)) +		eprintf_and_kill(pid, "ptrace PTRACE_SEIZE %ju 0 ...:", (uintmax_t)pid); +	if (ptrace(PTRACE_INTERRUPT, pid, 0, 0)) +		eprintf_and_kill(pid, "ptrace PTRACE_INTERRUPT %ju 0 0:", (uintmax_t)pid); +	if (kill(pid, SIGCONT) < 0) +		eprintf_and_kill(pid, "kill &ju SIGCONT:", (uintmax_t)pid); +  	return proc;  } @@ -3,6 +3,7 @@  char *argv0; +static unsigned long int trace_options = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC;  static void @@ -70,11 +71,6 @@ handle_syscall(struct process *proc)  		break;  	case Exec: -		if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) -			eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); -		proc->state = Normal; -		break; -  	case VforkParent:  		if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))  			eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); @@ -91,18 +87,95 @@ handle_syscall(struct process *proc)  } +static void +handle_event(struct process *proc, int status) +{ +	int trace_event, sig; +	unsigned long int event; +	struct process *proc2; + +	sig = WSTOPSIG(status); +	trace_event = ((status >> 8) ^ SIGTRAP) >> 8; +	switch (trace_event) { + +	case PTRACE_EVENT_VFORK: +		tprintf(proc, "\nProcess stopped by vfork until child exits or exec(2)s\n"); +		/* fall thought */ +	case PTRACE_EVENT_FORK: +	case PTRACE_EVENT_CLONE: +		if (ptrace(PTRACE_GETEVENTMSG, proc->pid, NULL, &event)) +			eprintf("ptrace PTRACE_GETEVENTMSG %ju NULL <buffer>:", (uintmax_t)proc->pid); +		proc2 = add_process((pid_t)event, trace_options); +		if (trace_event == PTRACE_EVENT_CLONE) +			proc2->thread_leader = proc->pid; +		proc->ret = event; +		if (trace_event == PTRACE_EVENT_VFORK) { +			proc2->continue_on_exit = proc; +			proc->vfork_waiting_on = proc2; +			proc->state = VforkParent; +		} else { +			proc->state = trace_event == PTRACE_EVENT_CLONE ? CloneParent : ForkParent; +			handle_syscall(proc); +		} +		tprintf(proc2, "\nTracing new process\n"); +		proc2->state = trace_event == PTRACE_EVENT_FORK ? ForkChild : +			trace_event == PTRACE_EVENT_VFORK ? VforkChild : CloneChild; +		handle_syscall(proc2); +		break; + +	case PTRACE_EVENT_EXEC: +		proc->state = Exec; +		handle_syscall(proc); +		proc2 = proc->continue_on_exit; +		if (proc2) { +			proc->continue_on_exit = NULL; +			proc2->vfork_waiting_on = NULL; +			tprintf(proc2, "\nProcess continues due to exec(2) of vfork child\n"); +			handle_syscall(proc2); +		} +		break; + +	case PTRACE_EVENT_STOP: +		switch (sig) { +		case SIGSTOP: +		case SIGTSTP: +		case SIGTTIN: +		case SIGTTOU: +		stop_signal: +			tprintf(proc, "\nProcess stopped by signal %i (%s: %s)\n", sig, get_signum_name(sig), strsignal(sig)); +			if (ptrace(PTRACE_LISTEN, proc->pid, NULL, 0)) +				eprintf("ptrace PTRACE_LISTEN %ju NULL 0:", (uintmax_t)proc->pid); +			break; +		default: +			if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) +				eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); +			break; +		} +		break; + +	default: +		abort(); + +	case 0: +		/* TODO ensure proper handling of signals (multithreaded?, siginfo?, in new processes?) */ +		if (ptrace(PTRACE_GETSIGINFO, proc->pid, 0, &(siginfo_t){0})) +			goto stop_signal; +		tprintf(proc, "\nProcess received signal %i (%s: %s)\n", sig, get_signum_name(sig), strsignal(sig)); +		if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, sig)) +			eprintf("ptrace PTRACE_SYSCALL %ju NULL %i:", (uintmax_t)proc->pid, sig); +		break; +	} +} + +  int  main(int argc, char **argv)  {  	pid_t pid, orig_pid; -	long int tmp;  	char *outfile = NULL;  	FILE *outfp = stderr; -	const char *num = NULL; -	int status, exit_value = 0, trace_event, with_argv0 = 0, multiprocess = 0; -	unsigned long int trace_options = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC; +	int status, exit_code = 0, with_argv0 = 0, multiprocess = 0;  	struct process *proc, *proc2; -	unsigned long int event;  	/* TODO add option to trace signals with siginfo (-s) */  	ARGBEGIN { @@ -128,154 +201,67 @@ main(int argc, char **argv)  	if (!argc)  		usage(); -	init_process_list(); -  	/* Start program to trace */ -	pid = fork(); -	switch (pid) { +	orig_pid = fork(); +	switch (orig_pid) {  	case -1:  		eprintf("fork:"); -		return 1;  	case 0: -		if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) { -			eprintf("ptrace PTRACE_TRACEME 0 NULL 0:"); -			return 1; -		} -		/* exec will block until parent attaches */ +		if (raise(SIGSTOP)) +			eprintf_and_kill(getppid(), "raise SIGSTOP:");  		execvp(*argv, &argv[with_argv0]); -		kill(getppid(), SIGKILL); -		eprintf("execvp %s:", *argv); +		eprintf_and_kill(getppid(), "execvp %s:", *argv);  	default: -		orig_pid = pid; -		add_process(pid, trace_options);  		break;  	} -	/* Open trace output file */ -	if (outfile) { -		if (!strncmp(outfile, "/dev/fd/", sizeof("/dev/fd/") - 1)) -			num = &outfile[sizeof("/dev/fd/") - 1]; -		else if (!strncmp(outfile, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1)) -			num = &outfile[sizeof("/proc/self/fd/") - 1]; -		else if (!strcmp(outfile, "/dev/stdin")) -			num = "0"; -		else if (!strcmp(outfile, "/dev/stdout")) -			num = "1"; -		else if (!strcmp(outfile, "/dev/stderr")) -			num = "2"; -		if (num && isdigit(*num)) { -			errno = 0; -			tmp = strtol(num, (void *)&num, 10); -			if (!errno && tmp >= 0 && -#if INT_MAX < LONG_MAX -			    tmp < INT_MAX && -#endif -			    !*num) { -				outfp = fdopen((int)tmp, "wb"); -				if (!outfp) { -					eprintf("fdopen %li wb:", tmp); -					return 1; -				} -				goto have_outfp; -			} -		} -		outfp = fopen(outfile, "wb"); -		if (!outfp) { -			eprintf("fopen %s wb:", outfile); -			return 1; -		} -	} - -have_outfp: +	/* TODO need to reset signal handlers and mask */ +	outfp = outfile ? xfopen(outfile, "wb") : stderr;  	setup_trace_output(outfp, multiprocess); +	init_process_list(); +	add_process(orig_pid, trace_options);  	for (;;) { -		pid = wait(&status); +		pid = waitpid(-1, &status, __WALL | WCONTINUED);  		if (pid < 0) {  			if (errno == ECHILD) -				return exit_value; -			eprintf("wait <buffer>:"); -			return 1; +				return WEXITSTATUS(exit_code); /* TODO mimic kill by signal */ +			if (errno == EINTR) +				continue; +			eprintf("waitpid -1 <buffer> __WALL:");  		}  		proc = find_process(pid);  		if (!proc)  			continue; -		if (WIFEXITED(status)) { +		if (WIFSTOPPED(status)) { +			if (WSTOPSIG(status) == (SIGTRAP | 0x80)) +				handle_syscall(proc); +			else +				handle_event(proc, status); +		} else if (WIFCONTINUED(status)) { +			tprintf(proc, "\nProcess continued, presumably by signal %i (SIGCONT: %s)\n", SIGCONT, strsignal(SIGCONT)); +		} else {  			if (pid == orig_pid) -				exit_value = WEXITSTATUS(status); -			tprintf(proc, "\nProcess exited with value %i\n", WEXITSTATUS(status)); +				exit_code = status; +			if (WIFEXITED(status)) { +				tprintf(proc, "\nProcess exited with value %i\n", WEXITSTATUS(status)); +			} else { +				tprintf(proc, "\nProcess terminated by signal %i (%s: %s)\n", WTERMSIG(status), +				        get_signum_name(WTERMSIG(status)), strsignal(WTERMSIG(status))); +			}  			proc2 = proc->continue_on_exit;  			remove_process(proc);  			if (proc2) { -				tprintf(proc2, "\nProcess continues due to exit of vfork child\n"); +				if (WIFEXITED(status)) +					tprintf(proc2, "\nProcess continues due to exit of vfork child\n"); +				else +					tprintf(proc2, "\nProcess continues due to abnormal termination of vfork child\n");  				handle_syscall(proc2);  			} - -		} else if (WIFSIGNALED(status)) { -			tprintf(proc, "\nProcess terminated by signal %i (%s: %s)\n", WTERMSIG(status), -			        get_signum_name(WTERMSIG(status)), strsignal(WTERMSIG(status))); - -		} else if (WIFSTOPPED(status)) { -			if (WSTOPSIG(status) == (SIGTRAP | 0x80)) { -				handle_syscall(proc); -			} else if (WSTOPSIG(status) == SIGTRAP) { -				trace_event = ((status >> 8) ^ SIGTRAP) >> 8; -				switch (trace_event) { - -				case PTRACE_EVENT_VFORK: -					tprintf(proc, "\nProcess stopped by vfork until child exits or exec(2)s\n"); -					/* fall thought */ -				case PTRACE_EVENT_FORK: -				case PTRACE_EVENT_CLONE: -					if (ptrace(PTRACE_GETEVENTMSG, proc->pid, NULL, &event)) -						eprintf("ptrace PTRACE_GETEVENTMSG %ju NULL <buffer>:", (uintmax_t)proc->pid); -					proc2 = add_process((pid_t)event, trace_options); -					if (trace_event == PTRACE_EVENT_CLONE) -						proc2->thread_group_leader = proc->pid; -					proc->ret = event; -					if (trace_event == PTRACE_EVENT_VFORK) { -						proc2->continue_on_exit = proc; -						proc->vfork_waiting_on = proc2; -						proc->state = VforkParent; -					} else { -						proc->state = trace_event == PTRACE_EVENT_CLONE ? CloneParent : ForkParent; -						handle_syscall(proc); -					} -					tprintf(proc2, "\nTracing new process\n"); -					proc2->state = trace_event == PTRACE_EVENT_FORK ? ForkChild : -					               trace_event == PTRACE_EVENT_VFORK ? VforkChild : CloneChild; -					handle_syscall(proc2); -					break; - -				case PTRACE_EVENT_EXEC: -					proc->state = Exec; -					handle_syscall(proc); -					proc2 = proc->continue_on_exit; -					if (proc2) { -						proc->continue_on_exit = NULL; -						proc2->vfork_waiting_on = NULL; -						tprintf(proc2, "\nProcess continues due to exec(2) of vfork child\n"); -						handle_syscall(proc2); -					} -					break; - -				default: -					goto print_signal; -				} -			} else { -			print_signal: -				tprintf(proc, "\nProcess received signal %i (%s: %s)\n", WSTOPSIG(status), -				        get_signum_name(WSTOPSIG(status)), strsignal(WSTOPSIG(status))); -				/* TODO handle signals properly (siginfo?, SIGSTOP &c does not stop the process) */ -				if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, WSTOPSIG(status))) -					eprintf("ptrace PTRACE_SYSCALL %ju NULL %i:", (uintmax_t)proc->pid, WSTOPSIG(status)); -			} - -		} else if (WIFCONTINUED(status)) { -			tprintf(proc, "\nProcess continued\n", (uintmax_t)pid);  		} +  	}  	fclose(outfp); @@ -20,17 +20,19 @@ void  tprintf(struct process *proc, const char *fmt, ...)  {  	va_list ap; +	if (proc->silent_until_execed) +		return;  	if (fmt[0] == '\n' && fmt[1]) {  		last_pid = 0;  		if (multiproctrace || last_char == '\n')  			fmt = &fmt[1];  	}  	if (multiproctrace) { -		if (proc->thread_group_leader) { +		if (proc->thread_leader) {  			if (last_char == '\n') -				fprintf(trace_fp, "[%ju, %ju] ", (uintmax_t)proc->thread_group_leader, (uintmax_t)proc->pid); +				fprintf(trace_fp, "[%ju, %ju] ", (uintmax_t)proc->thread_leader, (uintmax_t)proc->pid);  			else if (proc->pid != last_pid) -				fprintf(trace_fp, "\n[%ju, %ju] ", (uintmax_t)proc->thread_group_leader, (uintmax_t)proc->pid); +				fprintf(trace_fp, "\n[%ju, %ju] ", (uintmax_t)proc->thread_leader, (uintmax_t)proc->pid);  		} else {  			if (last_char == '\n')  				fprintf(trace_fp, "[%ju] ", (uintmax_t)proc->pid); @@ -46,8 +48,8 @@ tprintf(struct process *proc, const char *fmt, ...)  } -_Noreturn void -eprintf(const char *fmt, ...) +void +weprintf(const char *fmt, ...)  {  	va_list ap;  	va_start(ap, fmt); @@ -64,5 +66,44 @@ eprintf(const char *fmt, ...)  		break;  	}  	va_end(ap); -	exit(1); +} + + +FILE * +xfopen(const char *file, const char *mode) +{ +	FILE *ret; +	const char *num = NULL; +	long int tmp; + +	if (!strncmp(file, "/dev/fd/", sizeof("/dev/fd/") - 1)) +		num = &file[sizeof("/dev/fd/") - 1]; +	else if (!strncmp(file, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1)) +		num = &file[sizeof("/proc/self/fd/") - 1]; +	else if (!strcmp(file, "/dev/stdin")) +		num = "0"; +	else if (!strcmp(file, "/dev/stdout")) +		num = "1"; +	else if (!strcmp(file, "/dev/stderr")) +		num = "2"; + +	if (num && isdigit(*num)) { +		errno = 0; +		tmp = strtol(num, (void *)&num, 10); +		if (!errno && tmp >= 0 && +#if INT_MAX < LONG_MAX +		    tmp < INT_MAX && +#endif +		    !*num) { +			ret = fdopen((int)tmp, mode); +			if (!ret) +				eprintf("fdopen %li %s:", tmp, mode); +			return ret; +		} +	} + +	ret = fopen(file, mode); +	if (!ret) +		eprintf("fopen %s %s:", file, mode); +	return ret;  } | 
