diff --git a/src/include/deepstate/DeepState.h b/src/include/deepstate/DeepState.h index 69268b26..6a8f404d 100644 --- a/src/include/deepstate/DeepState.h +++ b/src/include/deepstate/DeepState.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -58,6 +59,10 @@ #define DEEPSTATE_SIZE 8192 #endif +#ifndef DEEPSTATE_CRASH_MAX_FRAMES +#define DEEPSTATE_CRASH_MAX_FRAMES 63 +#endif + #ifndef DEEPSTATE_MAX_SWARM_CONFIGS #define DEEPSTATE_MAX_SWARM_CONFIGS 1024 #endif @@ -90,6 +95,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); @@ -597,6 +603,42 @@ 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) { + + /* output information about the signal caught and the exception that occurred */ + const char *result; + if (!sig->si_status) + result = sys_siglist[signum]; + else + result = sys_siglist[sig->si_status]; + DeepState_LogFormat(DeepState_LogError, "Signal caught in test: %s (error: %d)", result, sig->si_signo); + + /* return a backtrace */ + size_t size; + void *back_addrs[DEEPSTATE_CRASH_MAX_FRAMES]; + char **symbols; + + size = backtrace(back_addrs, DEEPSTATE_CRASH_MAX_FRAMES); + if (size == 0) + DeepState_Abandon("Cannot retrieve backtrace stack addresses"); + + symbols = backtrace_symbols(back_addrs, size); + if (symbols == NULL) + DeepState_Abandon("Cannot retrieve symbols for stack addresses"); + + DeepState_LogFormat(DeepState_LogTrace, "======= Backtrace: ========="); + for (size_t i = 0; i < size; i++) + DeepState_LogFormat(DeepState_LogTrace, "%s", symbols[i]); + DeepState_LogFormat(DeepState_LogTrace, "==========================="); + + /* cleanup resources and exit */ + free(symbols); + DeepState_CleanUp(); + exit(DeepState_TestRunCrash); +} + + /* Jump buffer for returning to `DeepState_Run`. */ extern jmp_buf DeepState_ReturnToRun; @@ -726,7 +768,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); @@ -780,6 +821,18 @@ DeepState_ForkAndRunTest(struct DeepState_TestInfo *test) { if (FLAGS_fork) { waitpid(test_pid, &wstatus, 0); } else { + + /* If flag is set, install a "multiplexed" signal handler + * in order to backtrace and cleanup without an abrupt exit. */ + if (FLAGS_verbose_crash_trace) { + struct sigaction sigact; + sigact.sa_flags = SA_SIGINFO; + sigact.sa_sigaction = DeepState_EmitBacktrace; + + for (int i = 0; i <= sizeof(sys_siglist); i++) + sigaction(i, &sigact, 0); + } + wstatus = DeepState_RunTestNoFork(test); DeepState_CleanUp(); } @@ -830,6 +883,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 a9d665b0..249c5415 100644 --- a/src/lib/DeepState.c +++ b/src/lib/DeepState.c @@ -41,10 +41,10 @@ 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, 3600, "Timeout for brute force fuzzing."); -DEFINE_uint(num_workers, ExecutionGroup, 1, "Number of workers to spawn for testing and test generation."); +DEFINE_int(timeout, ExecutionGroup, 120, "Timeout for brute force fuzzing."); +DEFINE_bool(verbose_reads, ExecutionGroup, false, "Report on bytes being read during execution of test."); +DEFINE_bool(verbose_crash_trace, ExecutionGroup, false, "If unforked test crashes, report crash information and backtrace."); /* 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 +254,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 +306,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 +347,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); @@ -676,6 +676,8 @@ extern void DeepState_CleanUp() { for (int i = 0; i < DeepState_GeneratedAllocsIndex; i++) { free(DeepState_GeneratedAllocs[i]); } + + DeepState_GeneratedStringsIndex = 0; DeepState_GeneratedAllocsIndex = 0; for (int i = 0; i < DeepState_SwarmConfigsIndex; i++) { @@ -1125,6 +1127,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);