diff --git a/src/include/deepstate/DeepState.h b/src/include/deepstate/DeepState.h index 67fa862e..b531c92f 100644 --- a/src/include/deepstate/DeepState.h +++ b/src/include/deepstate/DeepState.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -90,6 +91,7 @@ DECLARE_bool(fork); DECLARE_bool(list_tests); DECLARE_bool(boring_only); DECLARE_bool(run_disabled); +DECLARE_bool(verbose_crash_trace); DECLARE_int(min_log_level); DECLARE_int(seed); @@ -521,6 +523,25 @@ extern void DeepState_SaveFailingTest(void); /* Save a crashing test to the output test directory. */ extern void DeepState_SaveCrashingTest(void); +/* Emit test function backtrace after test crashes. */ +static void DeepState_EmitBacktrace(int signum, siginfo_t *sig, void *context) { + + DeepState_LogFormat(DeepState_LogInfo, "Test crashed with: %s", sys_siglist[sig->si_status]); + + void *array[10]; + size_t size; + char **strings; + + size = backtrace(array, 10); + strings = backtrace_symbols(array, size); + + for (size_t i = 0; i < size; i++) + DeepState_LogFormat(DeepState_LogTrace, "%s", strings[i]); + + free(strings); +} + + /* Jump buffer for returning to `DeepState_Run`. */ extern jmp_buf DeepState_ReturnToRun; @@ -650,7 +671,6 @@ static int DeepState_RunTestNoFork(struct DeepState_TestInfo *test) { #if defined(__cplusplus) && defined(__cpp_exceptions) try { #endif /* __cplusplus */ - test->test_func(); /* Run the test function. */ return(DeepState_TestRunPass); @@ -692,6 +712,19 @@ static int DeepState_RunTestNoFork(struct DeepState_TestInfo *test) { /* Fork and run `test`. */ static enum DeepState_TestRunResult DeepState_ForkAndRunTest(struct DeepState_TestInfo *test) { + + /* If flag is set, install a signal handler for SIGCHLD */ + /* TODO(alan): use handler as "multiplexer" and handle child signal */ + if (FLAGS_verbose_crash_trace) { + struct sigaction sigact, oldact; + + sigact.sa_flags = SA_SIGINFO | SA_NOCLDWAIT; + sigact.sa_sigaction = DeepState_EmitBacktrace; + + sigaction(SIGCHLD, &sigact, &oldact); + } + + pid_t test_pid; if (FLAGS_fork) { test_pid = fork(); @@ -754,6 +787,7 @@ DeepState_RunSavedTestCase(struct DeepState_TestInfo *test, const char *dir, DeepState_LogFormat(DeepState_LogError, "Crashed: %s", test->test_name); DeepState_LogFormat(DeepState_LogError, "Test case %s crashed", path); free(path); + if (HAS_FLAG_output_test_dir) { DeepState_SaveCrashingTest(); } diff --git a/src/lib/DeepState.c b/src/lib/DeepState.c index 7b275de8..5cbf281f 100644 --- a/src/lib/DeepState.c +++ b/src/lib/DeepState.c @@ -41,10 +41,11 @@ DEFINE_string(output_test_dir, InputOutputGroup, "", "Directory where tests will DEFINE_bool(take_over, ExecutionGroup, false, "Replay test cases in take-over mode."); DEFINE_bool(abort_on_fail, ExecutionGroup, false, "Abort on file replay failure (useful in file fuzzing)."); DEFINE_bool(exit_on_fail, ExecutionGroup, false, "Exit with status 255 on test failure."); -DEFINE_bool(verbose_reads, ExecutionGroup, false, "Report on bytes being read during execution of test."); DEFINE_int(min_log_level, ExecutionGroup, 0, "Minimum level of logging to output (default 2, 0=debug, 1=trace, 2=info, ...)."); DEFINE_int(timeout, ExecutionGroup, 120, "Timeout for brute force fuzzing."); DEFINE_uint(num_workers, ExecutionGroup, 1, "Number of workers to spawn for testing and test generation."); +DEFINE_bool(verbose_reads, ExecutionGroup, false, "Report on bytes being read during execution of test."); +DEFINE_bool(verbose_crash_trace, ExecutionGroup, false, "If test crashes, report an execution backtrace after abrupt exit."); /* Fuzzing and symex related options, baked in to perform analysis-related tasks without auxiliary tools */ DEFINE_bool(fuzz, AnalysisGroup, false, "Perform brute force unguided fuzzing."); @@ -254,7 +255,7 @@ void DeepState_SwarmAssignCStr_C(const char* file, unsigned line, int stype, if (NULL == str) { DeepState_Abandon("Attempted to populate null pointer."); } - char swarm_allowed[256]; + char swarm_allowed[256]; if (allowed == 0) { /* In swarm mode, if there is no allowed string, create one over all chars. */ for (int i = 0; i < 255; i++) { @@ -306,7 +307,7 @@ char *DeepState_SwarmCStr_C(const char* file, unsigned line, int stype, if (NULL == str) { DeepState_Abandon("Can't allocate memory"); } - char swarm_allowed[256]; + char swarm_allowed[256]; if (allowed == 0) { /* In swarm mode, if there is no allowed string, create one over all chars. */ for (int i = 0; i < 255; i++) { @@ -347,14 +348,14 @@ void DeepState_SymbolizeCStr_C(char *begin, const char* allowed) { void DeepState_SwarmSymbolizeCStr_C(const char* file, unsigned line, int stype, char *begin, const char* allowed) { if (begin && begin[0]) { - char swarm_allowed[256]; + char swarm_allowed[256]; if (allowed == 0) { /* In swarm mode, if there is no allowed string, create one over all chars. */ for (int i = 0; i < 255; i++) { swarm_allowed[i] = i+1; } swarm_allowed[255] = 0; - allowed = (const char*)&swarm_allowed; + allowed = (const char*)&swarm_allowed; } uint32_t allowed_size = strlen(allowed); struct DeepState_SwarmConfig* sc = DeepState_GetSwarmConfig(allowed_size, file, line, stype); @@ -668,7 +669,7 @@ extern void DeepState_CleanUp() { free(DeepState_GeneratedStrings[i]); } DeepState_GeneratedStringsIndex = 0; - + for (int i = 0; i < DeepState_SwarmConfigsIndex; i++) { free(DeepState_SwarmConfigs[i]->file); free(DeepState_SwarmConfigs[i]->fmap); @@ -1116,6 +1117,7 @@ enum DeepState_TestRunResult DeepState_FuzzOneTestCase(struct DeepState_TestInfo enum DeepState_TestRunResult result = DeepState_ForkAndRunTest(test); + if (result == DeepState_TestRunCrash) { DeepState_LogFormat(DeepState_LogError, "Crashed: %s", test->test_name);