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

Initial crash triaging support for DeepState harnesses #288

Open
wants to merge 3 commits into
base: master
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
56 changes: 55 additions & 1 deletion src/include/deepstate/DeepState.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <time.h>
#include <unistd.h>
#include <fnmatch.h>
#include <execinfo.h>

#include <deepstate/Log.h>
#include <deepstate/Compiler.h>
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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();
}
Expand Down
17 changes: 10 additions & 7 deletions src/lib/DeepState.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down Expand Up @@ -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++) {
Expand Down Expand Up @@ -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++) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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++) {
Expand Down Expand Up @@ -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);

Expand Down