Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime/tracer: passthrough unexpected signals by default #488

Open
wants to merge 3 commits into
base: kkysen/post-condition-negative-control
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions misc/test_runner/include/ia2_test_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ struct fake_criterion_test {
ia2_test_fn test;
ia2_test_fn init;
int exit_code;
int signal;
};

extern struct fake_criterion_test *fake_criterion_tests;

#define _STRINGIFY(a) #a
#define STRINGIFY(a) _STRINGIFY(a)
#define STRINGIFY(a) #a
#define EXPAND_AND_STRINGIFY(a) STRINGIFY(a)

/*
* Placing IA2_{BEGIN,END}_NO_WRAP between the function declaration stops the rewriter from creating a
Expand All @@ -42,8 +43,8 @@ extern struct fake_criterion_test *fake_criterion_tests;
IA2_END_NO_WRAP \
struct fake_criterion_test fake_criterion_##suite_##_##name_##_##test IA2_SHARED_DATA = { \
.next = NULL, \
.suite = STRINGIFY(suite_), \
.name = STRINGIFY(name_), \
.suite = EXPAND_AND_STRINGIFY(suite_), \
.name = EXPAND_AND_STRINGIFY(name_), \
.test = fake_criterion_##suite_##_##name_, \
##__VA_ARGS__}; \
__attribute__((constructor)) void fake_criterion_add_##suite_##_##name_##_##test(void) { \
Expand Down
12 changes: 10 additions & 2 deletions misc/test_runner/test_runner.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ int main() {
fprintf(stderr, " (%s)", STRINGIFY(EXIT_FAILURE));
} else if (exit_code > 128) {
fprintf(stderr, " (SIG%s)", sigabbrev_np(exit_code - 128));
} else if (test_info->signal != 0) {
// Sometimes the signal doesn't show up with `WIFSIGNALED`, so we check the exit status as well.
fprintf(stderr, " (SIG%s)", sigabbrev_np(test_info->signal));
}
if (test_info->signal != 0) {
fprintf(stderr, ", signal (SIG%s)", sigabbrev_np(test_info->signal));
}
fprintf(stderr, "...\n");

Expand All @@ -118,12 +124,14 @@ int main() {
perror("waitpid");
return 2;
}
if WIFSIGNALED (stat) {
if (WIFSIGNALED(stat) && WTERMSIG(stat) != test_info->signal) {
fprintf(stderr, "forked test child was terminated by signal %d\n", WTERMSIG(stat));
return 1;
}
int exit_status = WEXITSTATUS(stat);
if (exit_status != test_info->exit_code) {
if (exit_status != test_info->exit_code ||
// Sometimes the signal doesn't show up with `WIFSIGNALED`, so we check the exit status as well.
(exit_status > 128 && exit_status - 128 == test_info->signal)) {
fprintf(stderr, "forked test child exited with status %d, but %d was expected\n", exit_status, test_info->exit_code);
return 1;
}
Expand Down
45 changes: 32 additions & 13 deletions runtime/tracer/track_memory_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,15 +471,24 @@ enum wait_trap_result {
WAIT_PTRACE_FORK,
WAIT_EXEC,
WAIT_CONT,
};

/* wait for the next trap from the inferior.

returns the wait_trap_result corresponding to the event.
// Passthrough ptrace the stopped signal.
// This is the default cause.
WAIT_PASSTHROUGH_SIGNAL,
};

if the exit is WAIT_EXITED, the exit status will be placed in *exit_status_out
if it is non-NULL. */
static enum wait_trap_result wait_for_next_trap(pid_t pid, pid_t *pid_out, int *exit_status_out) {
/**
* Wait for the next trap from the inferior.
*
* Returns the `wait_trap_result` corresponding to the event.
*
* If the exit is `WAIT_EXITED`, the exit status will be placed
* in `*exit_status_out` if it is non-NULL.
*
* If the exit is `WAIT_PASSTHROUGH_SIGNAL`, the signal will be placed
* in `*passthrough_signal` if it is non-NULL.
*/
static enum wait_trap_result wait_for_next_trap(pid_t pid, pid_t *pid_out, int *exit_status_out, int *passthrough_signal) {
bool entry = (pid == -1);
int stat = 0;
static pid_t last_pid = 0; /* used to limit logs to when pid changes */
Expand Down Expand Up @@ -556,8 +565,11 @@ static enum wait_trap_result wait_for_next_trap(pid_t pid, pid_t *pid_out, int *
debug_event("child stopped by sigsegv\n");
return WAIT_SIGSEGV;
default:
fprintf(stderr, "child stopped by unexpected signal %d\n", WSTOPSIG(stat));
return WAIT_ERROR;
debug_event("child stopped by unexpected signal %d\n", WSTOPSIG(stat));
if (passthrough_signal) {
*passthrough_signal = WSTOPSIG(stat);
}
return WAIT_PASSTHROUGH_SIGNAL;
}
}
fprintf(stderr, "unknown wait status %x\n", stat);
Expand Down Expand Up @@ -716,7 +728,8 @@ bool track_memory_map(pid_t pid, int *exit_status_out, enum trace_mode mode) {
while (true) {
/* wait for the process to get signalled */
pid_t waited_pid = pid;
enum wait_trap_result wait_result = wait_for_next_trap(-1, &waited_pid, exit_status_out);
int passthrough_signal = 0;
enum wait_trap_result wait_result = wait_for_next_trap(-1, &waited_pid, exit_status_out, &passthrough_signal);
switch (wait_result) {
/* we need to handle events relating to process lifetime upfront: these
include clone()/fork()/exec() and sigchld */
Expand Down Expand Up @@ -745,6 +758,12 @@ bool track_memory_map(pid_t pid, int *exit_status_out, enum trace_mode mode) {
}
continue;
break;
case WAIT_PASSTHROUGH_SIGNAL:
if (ptrace(continue_request, waited_pid, 0, passthrough_signal) < 0) {
perror("could not PTRACE_SYSCALL...");
}
continue;
break;
case WAIT_GROUP_STOP:
printf("group stop in syscall entry\n");
if (ptrace(PTRACE_LISTEN, waited_pid, 0, 0) < 0) {
Expand Down Expand Up @@ -817,7 +836,7 @@ bool track_memory_map(pid_t pid, int *exit_status_out, enum trace_mode mode) {
case WAIT_SIGNALED: {
fprintf(stderr, "process received fatal signal (syscall entry)\n");
enum control_flow cf = handle_process_exit(&maps, waited_pid);
return false;
return true;
}
case WAIT_EXITED: {
debug_exit("pid %d exited (syscall entry)\n", waited_pid);
Expand Down Expand Up @@ -888,7 +907,7 @@ bool track_memory_map(pid_t pid, int *exit_status_out, enum trace_mode mode) {
if (ptrace(PTRACE_SYSCALL, waited_pid, 0, 0) < 0) {
perror("could not PTRACE_SYSCALL");
}
switch (wait_for_next_trap(waited_pid, NULL, exit_status_out)) {
switch (wait_for_next_trap(waited_pid, NULL, exit_status_out, NULL)) {
case WAIT_SYSCALL:
break;
case WAIT_ERROR:
Expand Down Expand Up @@ -922,7 +941,7 @@ bool track_memory_map(pid_t pid, int *exit_status_out, enum trace_mode mode) {
if (ptrace(PTRACE_SYSCALL, waited_pid, 0, 0) < 0) {
perror("could not PTRACE_SYSCALL");
}
switch (wait_for_next_trap(waited_pid, NULL, exit_status_out)) {
switch (wait_for_next_trap(waited_pid, NULL, exit_status_out, NULL)) {
case WAIT_STOP:
break;
case WAIT_SYSCALL:
Expand Down
4 changes: 1 addition & 3 deletions tests/post_condition/dav1d.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ void dav1d_get_picture_post_condition(Dav1dContext *const c, Dav1dPicture *const
cr_log_info("dav1d_get_picture post condition ran");
if (out->stride[0] < 0) {
cr_log_info("negative stride");
// signals, like the `SIGABRT` from `assert` don't yet work with
// `ia2-sandbox` and its `ptrace` tracer.
exit(128 + SIGABRT);
}
assert(out->stride[0] > 0);
}
2 changes: 1 addition & 1 deletion tests/post_condition/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Test(post_condition, normal) {
dav1d_get_picture(&c, &pic);
}

Test(post_condition, corrupt_bounds, .exit_code = 128 + SIGABRT) {
Test(post_condition, corrupt_bounds, .signal = SIGABRT) {
corrupt_stride = true;
dav1d_get_picture(&c, &pic);
}
Loading