Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up| /* | |
| * Copyright (c) 2019 Trail of Bits, Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include "deepstate/DeepState.h" | |
| #include "deepstate/Option.h" | |
| #include "deepstate/Log.h" | |
| #include <assert.h> | |
| #include <limits.h> | |
| #include <setjmp.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #ifdef DEEPSTATE_TAKEOVER_RAND | |
| #undef rand | |
| #undef srand | |
| #endif | |
| DEEPSTATE_BEGIN_EXTERN_C | |
| /* Basic input and output options, specifies files for read/write before and after test analysis */ | |
| DEFINE_string(input_test_dir, InputOutputGroup, "", "Directory of saved tests to run."); | |
| DEFINE_string(input_test_file, InputOutputGroup, "", "Saved test to run."); | |
| DEFINE_string(input_test_files_dir, InputOutputGroup, "", "Directory of saved test files to run (flat structure)."); | |
| DEFINE_string(output_test_dir, InputOutputGroup, "", "Directory where tests will be saved."); | |
| /* Test execution-related options, configures how an execution run is carried out */ | |
| 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."); | |
| /* 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."); | |
| DEFINE_bool(fuzz_save_passing, AnalysisGroup, false, "Save passing tests during fuzzing."); | |
| DEFINE_bool(fork, AnalysisGroup, true, "Fork when running a test."); | |
| DEFINE_int(seed, AnalysisGroup, 0, "Seed for brute force fuzzing (uses time if not set)."); | |
| /* Test selection options to configure what test or tests should be executed during a run */ | |
| DEFINE_string(input_which_test, TestSelectionGroup, "", "Test to use with --input_test_file or --input_test_files_dir."); | |
| DEFINE_string(test_filter, TestSelectionGroup, "", "Run all tests matched with wildcard pattern."); | |
| DEFINE_bool(list_tests, TestSelectionGroup, false, "List all available tests instead of running tests."); | |
| DEFINE_bool(boring_only, TestSelectionGroup, false, "Run Boring concrete tests only."); | |
| DEFINE_bool(run_disabled, TestSelectionGroup, false, "Run Disabled tests alongside other tests."); | |
| /* Set to 1 by Manticore/Angr/etc. when we're running symbolically. */ | |
| int DeepState_UsingSymExec = 0; | |
| /* Set to 1 when we're using libFuzzer. */ | |
| int DeepState_UsingLibFuzzer = 0; | |
| /* To make libFuzzer louder on mac OS. */ | |
| int DeepState_LibFuzzerLoud = 0; | |
| /* Array of DeepState generated allocations. Impossible for there to | |
| * be more than there are input bytes. Index stores where we are. */ | |
| char* DeepState_GeneratedAllocs[DeepState_InputSize]; | |
| uint32_t DeepState_GeneratedAllocsIndex = 0; | |
| /* Pointer to the last registers DeepState_TestInfo data structure */ | |
| struct DeepState_TestInfo *DeepState_LastTestInfo = NULL; | |
| /* Pointer to structure for ordered DeepState_TestInfo */ | |
| struct DeepState_TestInfo *DeepState_FirstTestInfo = NULL; | |
| /* Pointer to the test being run in this process by Dr. Fuzz. */ | |
| static struct DeepState_TestInfo *DeepState_DrFuzzTest = NULL; | |
| /* Initialize global input buffer and index. */ | |
| volatile uint8_t DeepState_Input[DeepState_InputSize] = {}; | |
| uint32_t DeepState_InputIndex = 0; | |
| /* Swarm related state. */ | |
| uint32_t DeepState_SwarmConfigsIndex = 0; | |
| struct DeepState_SwarmConfig *DeepState_SwarmConfigs[DEEPSTATE_MAX_SWARM_CONFIGS]; | |
| /* Jump buffer for returning to `DeepState_Run`. */ | |
| jmp_buf DeepState_ReturnToRun = {}; | |
| /* Information about the current test run, if any. */ | |
| static struct DeepState_TestRunInfo *DeepState_CurrentTestRun = NULL; | |
| static void DeepState_SetTestPassed(void) { | |
| DeepState_CurrentTestRun->result = DeepState_TestRunPass; | |
| } | |
| static void DeepState_SetTestFailed(void) { | |
| DeepState_CurrentTestRun->result = DeepState_TestRunFail; | |
| } | |
| static void DeepState_SetTestAbandoned(const char *reason) { | |
| DeepState_CurrentTestRun->result = DeepState_TestRunAbandon; | |
| DeepState_CurrentTestRun->reason = reason; | |
| } | |
| void DeepState_AllocCurrentTestRun(void) { | |
| int mem_prot = PROT_READ | PROT_WRITE; | |
| int mem_vis = MAP_ANONYMOUS | MAP_SHARED; | |
| void *shared_mem = mmap(NULL, sizeof(struct DeepState_TestRunInfo), mem_prot, | |
| mem_vis, 0, 0); | |
| if (shared_mem == MAP_FAILED) { | |
| DeepState_Log(DeepState_LogError, "Unable to map shared memory"); | |
| exit(1); | |
| } | |
| DeepState_CurrentTestRun = (struct DeepState_TestRunInfo *) shared_mem; | |
| } | |
| static void DeepState_InitCurrentTestRun(struct DeepState_TestInfo *test) { | |
| DeepState_CurrentTestRun->test = test; | |
| DeepState_CurrentTestRun->result = DeepState_TestRunPass; | |
| DeepState_CurrentTestRun->reason = NULL; | |
| } | |
| /* Abandon this test. We've hit some kind of internal problem. */ | |
| DEEPSTATE_NORETURN | |
| void DeepState_Abandon(const char *reason) { | |
| DeepState_Log(DeepState_LogError, reason); | |
| DeepState_CurrentTestRun->result = DeepState_TestRunAbandon; | |
| DeepState_CurrentTestRun->reason = reason; | |
| longjmp(DeepState_ReturnToRun, 1); | |
| } | |
| /* Mark this test as having crashed. */ | |
| void DeepState_Crash(void) { | |
| DeepState_SetTestFailed(); | |
| } | |
| /* Mark this test as failing. */ | |
| DEEPSTATE_NORETURN | |
| void DeepState_Fail(void) { | |
| DeepState_SetTestFailed(); | |
| if (FLAGS_take_over) { | |
| // We want to communicate the failure to a parent process, so exit. | |
| exit(DeepState_TestRunFail); | |
| } else { | |
| longjmp(DeepState_ReturnToRun, 1); | |
| } | |
| } | |
| /* Mark this test as passing. */ | |
| DEEPSTATE_NORETURN | |
| void DeepState_Pass(void) { | |
| longjmp(DeepState_ReturnToRun, 0); | |
| } | |
| void DeepState_SoftFail(void) { | |
| DeepState_SetTestFailed(); | |
| } | |
| /* Symbolize the data in the exclusive range `[begin, end)`. */ | |
| void DeepState_SymbolizeData(void *begin, void *end) { | |
| uintptr_t begin_addr = (uintptr_t) begin; | |
| uintptr_t end_addr = (uintptr_t) end; | |
| if (begin_addr > end_addr) { | |
| DeepState_Abandon("Invalid data bounds for DeepState_SymbolizeData"); | |
| } else if (begin_addr == end_addr) { | |
| return; | |
| } else { | |
| uint8_t *bytes = (uint8_t *) begin; | |
| for (uintptr_t i = 0, max_i = (end_addr - begin_addr); i < max_i; ++i) { | |
| if (DeepState_InputIndex >= DeepState_InputSize) { | |
| DeepState_Abandon("Exceeded set input limit. Set or expand DEEPSTATE_SIZE to write more bytes."); | |
| } | |
| if (FLAGS_verbose_reads) { | |
| printf("Reading byte at %u\n", DeepState_InputIndex); | |
| } | |
| bytes[i] = DeepState_Input[DeepState_InputIndex++]; | |
| } | |
| } | |
| } | |
| /* Symbolize the data in the exclusive range `[begin, end)` without null | |
| * characters included. Primarily useful for C strings. */ | |
| void DeepState_SymbolizeDataNoNull(void *begin, void *end) { | |
| uintptr_t begin_addr = (uintptr_t) begin; | |
| uintptr_t end_addr = (uintptr_t) end; | |
| if (begin_addr > end_addr) { | |
| DeepState_Abandon("Invalid data bounds for DeepState_SymbolizeData"); | |
| } else if (begin_addr == end_addr) { | |
| return; | |
| } else { | |
| uint8_t *bytes = (uint8_t *) begin; | |
| for (uintptr_t i = 0, max_i = (end_addr - begin_addr); i < max_i; ++i) { | |
| if (DeepState_InputIndex >= DeepState_InputSize) { | |
| DeepState_Abandon("Exceeded set input limit. Set or expand DEEPSTATE_SIZE to write more bytes."); | |
| } | |
| if (FLAGS_verbose_reads) { | |
| printf("Reading byte at %u\n", DeepState_InputIndex); | |
| } | |
| bytes[i] = DeepState_Input[DeepState_InputIndex++]; | |
| if (bytes[i] == 0) { | |
| bytes[i] = 1; | |
| } | |
| } | |
| } | |
| } | |
| /* Concretize some data in exclusive the range `[begin, end)`. */ | |
| void *DeepState_ConcretizeData(void *begin, void *end) { | |
| return begin; | |
| } | |
| /* Assign a symbolic C string of strlen length `len`. str should include | |
| * storage for both `len` characters AND the null terminator. Allowed | |
| * is a set of chars that are allowed (ignored if null). */ | |
| void DeepState_AssignCStr_C(char* str, size_t len, const char* allowed) { | |
| if (SIZE_MAX <= len) { | |
| DeepState_Abandon("Can't create a SIZE_MAX-length string."); | |
| } | |
| if (NULL == str) { | |
| DeepState_Abandon("Attempted to populate null pointer."); | |
| } | |
| if (len) { | |
| if (allowed == 0) { | |
| DeepState_SymbolizeDataNoNull(str, &(str[len])); | |
| } else { | |
| uint32_t allowed_size = strlen(allowed); | |
| for (int i = 0; i < len; i++) { | |
| str[i] = allowed[DeepState_UIntInRange(0, allowed_size-1)]; | |
| } | |
| } | |
| } | |
| str[len] = '\0'; | |
| } | |
| void DeepState_SwarmAssignCStr_C(const char* file, unsigned line, int stype, | |
| char* str, size_t len, const char* allowed) { | |
| if (SIZE_MAX <= len) { | |
| DeepState_Abandon("Can't create a SIZE_MAX-length string."); | |
| } | |
| if (NULL == str) { | |
| DeepState_Abandon("Attempted to populate null pointer."); | |
| } | |
| 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; | |
| } | |
| if (len) { | |
| uint32_t allowed_size = strlen(allowed); | |
| struct DeepState_SwarmConfig* sc = DeepState_GetSwarmConfig(allowed_size, file, line, stype); | |
| for (int i = 0; i < len; i++) { | |
| str[i] = allowed[sc->fmap[DeepState_UIntInRange(0U, sc->fcount-1)]]; | |
| } | |
| } | |
| str[len] = '\0'; | |
| } | |
| /* Return a symbolic C string of strlen `len`. */ | |
| char *DeepState_CStr_C(size_t len, const char* allowed) { | |
| if (SIZE_MAX <= len) { | |
| DeepState_Abandon("Can't create a SIZE_MAX-length string"); | |
| } | |
| char *str = (char *) malloc(sizeof(char) * (len + 1)); | |
| if (NULL == str) { | |
| DeepState_Abandon("Can't allocate memory"); | |
| } | |
| DeepState_GeneratedAllocs[DeepState_GeneratedAllocsIndex++] = str; | |
| if (len) { | |
| if (allowed == 0) { | |
| DeepState_SymbolizeDataNoNull(str, &(str[len])); | |
| } else { | |
| uint32_t allowed_size = strlen(allowed); | |
| for (int i = 0; i < len; i++) { | |
| str[i] = allowed[DeepState_UIntInRange(0, allowed_size-1)]; | |
| } | |
| } | |
| } | |
| str[len] = '\0'; | |
| return str; | |
| } | |
| char *DeepState_SwarmCStr_C(const char* file, unsigned line, int stype, | |
| size_t len, const char* allowed) { | |
| if (SIZE_MAX <= len) { | |
| DeepState_Abandon("Can't create a SIZE_MAX-length string"); | |
| } | |
| char *str = (char *) malloc(sizeof(char) * (len + 1)); | |
| if (NULL == str) { | |
| DeepState_Abandon("Can't allocate memory"); | |
| } | |
| 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; | |
| } | |
| DeepState_GeneratedAllocs[DeepState_GeneratedAllocsIndex++] = str; | |
| if (len) { | |
| uint32_t allowed_size = strlen(allowed); | |
| struct DeepState_SwarmConfig* sc = DeepState_GetSwarmConfig(allowed_size, file, line, stype); | |
| for (int i = 0; i < len; i++) { | |
| str[i] = allowed[sc->fmap[DeepState_UIntInRange(0U, sc->fcount-1)]]; | |
| } | |
| } | |
| str[len] = '\0'; | |
| return str; | |
| } | |
| /* Symbolize a C string; keeps the null terminator where it was. */ | |
| void DeepState_SymbolizeCStr_C(char *begin, const char* allowed) { | |
| if (begin && begin[0]) { | |
| if (allowed == 0) { | |
| DeepState_SymbolizeDataNoNull(begin, begin + strlen(begin)); | |
| } else { | |
| uint32_t allowed_size = strlen(allowed); | |
| uint8_t *bytes = (uint8_t *) begin; | |
| uintptr_t begin_addr = (uintptr_t) begin; | |
| uintptr_t end_addr = (uintptr_t) (begin + strlen(begin)); | |
| for (uintptr_t i = 0, max_i = (end_addr - begin_addr); i < max_i; ++i) { | |
| bytes[i] = allowed[DeepState_UIntInRange(0, allowed_size-1)]; | |
| } | |
| } | |
| } | |
| } | |
| void DeepState_SwarmSymbolizeCStr_C(const char* file, unsigned line, int stype, | |
| char *begin, const char* allowed) { | |
| if (begin && begin[0]) { | |
| 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; | |
| } | |
| uint32_t allowed_size = strlen(allowed); | |
| struct DeepState_SwarmConfig* sc = DeepState_GetSwarmConfig(allowed_size, file, line, stype); | |
| uint8_t *bytes = (uint8_t *) begin; | |
| uintptr_t begin_addr = (uintptr_t) begin; | |
| uintptr_t end_addr = (uintptr_t) (begin + strlen(begin)); | |
| for (uintptr_t i = 0, max_i = (end_addr - begin_addr); i < max_i; ++i) { | |
| bytes[i] = allowed[sc->fmap[DeepState_UIntInRange(0U, sc->fcount-1)]]; | |
| } | |
| } | |
| } | |
| /* Concretize a C string */ | |
| const char *DeepState_ConcretizeCStr(const char *begin) { | |
| return begin; | |
| } | |
| /* Allocate and return a pointer to `num_bytes` symbolic bytes. */ | |
| void *DeepState_Malloc(size_t num_bytes) { | |
| void *data = malloc(num_bytes); | |
| uintptr_t data_end = ((uintptr_t) data) + num_bytes; | |
| DeepState_SymbolizeData(data, (void *) data_end); | |
| return data; | |
| } | |
| /* Allocate and return a pointer to `num_bytes` symbolic bytes. */ | |
| void *DeepState_GCMalloc(size_t num_bytes) { | |
| void *data = malloc(num_bytes); | |
| uintptr_t data_end = ((uintptr_t) data) + num_bytes; | |
| DeepState_SymbolizeData(data, (void *) data_end); | |
| DeepState_GeneratedAllocs[DeepState_GeneratedAllocsIndex++] = data; | |
| return data; | |
| } | |
| /* Portable and architecture-independent memory scrub without dead store elimination. */ | |
| void *DeepState_MemScrub(void *pointer, size_t data_size) { | |
| volatile unsigned char *p = pointer; | |
| while (data_size--) { | |
| *p++ = 0; | |
| } | |
| return pointer; | |
| } | |
| /* Generate a new swarm configuration. */ | |
| struct DeepState_SwarmConfig *DeepState_NewSwarmConfig(unsigned fcount, const char* file, unsigned line, | |
| enum DeepState_SwarmType stype) { | |
| struct DeepState_SwarmConfig *new_config = malloc(sizeof(struct DeepState_SwarmConfig)); | |
| new_config->file = malloc(strlen(file) + 1); | |
| strncpy(new_config->file, file, strlen(file)); | |
| new_config->line = line; | |
| new_config->orig_fcount = fcount; | |
| new_config->fcount = 0; | |
| if (stype == DeepState_SwarmTypeProb) { | |
| new_config->fmap = malloc(sizeof(unsigned) * fcount * DEEPSTATE_SWARM_MAX_PROB_RATIO); | |
| for (int i = 0; i < fcount; i++) { | |
| unsigned int prob = DeepState_UIntInRange(0U, DEEPSTATE_SWARM_MAX_PROB_RATIO); | |
| for (int j = 0; j < prob; j++) { | |
| new_config->fmap[new_config->fcount++] = i; | |
| } | |
| } | |
| if (new_config->fcount == 0) { | |
| new_config->fmap[new_config->fcount++] = DeepState_UIntInRange(0, fcount-1); | |
| } | |
| } else { | |
| new_config->fmap = malloc(sizeof(unsigned) * fcount); | |
| /* In mix mode, "half" the time just use everything */ | |
| int full_config = (stype == DeepState_SwarmTypeMixed) && DeepState_Bool(); | |
| if ((stype == DeepState_SwarmTypeMixed) && DeepState_UsingSymExec) { | |
| /* We don't want to make additional pointless paths to explore for symex */ | |
| (void) DeepState_Assume(full_config); | |
| } | |
| for (int i = 0; i < fcount; i++) { | |
| if (full_config) { | |
| new_config->fmap[new_config->fcount++] = i; | |
| } else { | |
| int in_swarm = DeepState_Bool(); | |
| if (DeepState_UsingSymExec) { | |
| /* If not in mix mode, just allow everything in each configuration for symex */ | |
| (void) DeepState_Assume(in_swarm); | |
| } | |
| if (in_swarm) { | |
| new_config->fmap[new_config->fcount++] = i; | |
| } | |
| } | |
| } | |
| } | |
| /* We always need to allow at least one option! */ | |
| if (new_config->fcount == 0) { | |
| new_config->fmap[new_config->fcount++] = DeepState_UIntInRange(0, fcount-1); | |
| } | |
| return new_config; | |
| } | |
| /* Either fetch existing configuration, or generate a new one. */ | |
| struct DeepState_SwarmConfig *DeepState_GetSwarmConfig(unsigned fcount, const char* file, unsigned line, | |
| enum DeepState_SwarmType stype) { | |
| /* In general, there should be few enough OneOfs in a harness that linear search is fine. */ | |
| for (int i = 0; i < DeepState_SwarmConfigsIndex; i++) { | |
| struct DeepState_SwarmConfig* sc = DeepState_SwarmConfigs[i]; | |
| if ((sc->line == line) && (sc->orig_fcount == fcount) && (strncmp(sc->file, file, strlen(file)) == 0)) { | |
| return sc; | |
| } | |
| } | |
| if (DeepState_SwarmConfigsIndex == DEEPSTATE_MAX_SWARM_CONFIGS) { | |
| DeepState_Abandon("Exceeded swarm config limit. Set or expand DEEPSTATE_MAX_SWARM_CONFIGS. This is highly unusual."); | |
| } | |
| DeepState_SwarmConfigs[DeepState_SwarmConfigsIndex] = DeepState_NewSwarmConfig(fcount, file, line, stype); | |
| return DeepState_SwarmConfigs[DeepState_SwarmConfigsIndex++]; | |
| } | |
| DEEPSTATE_NOINLINE int DeepState_One(void) { | |
| return 1; | |
| } | |
| DEEPSTATE_NOINLINE int DeepState_Zero(void) { | |
| return 0; | |
| } | |
| /* Always returns `0`. */ | |
| int DeepState_ZeroSink(int sink) { | |
| (void) sink; | |
| return 0; | |
| } | |
| /* Returns `1` if `expr` is true, and `0` otherwise. This is kind of an indirect | |
| * way to take a symbolic value, introduce a fork, and on each size, replace its | |
| * value with a concrete value. */ | |
| int DeepState_IsTrue(int expr) { | |
| if (expr == DeepState_Zero()) { | |
| return DeepState_Zero(); | |
| } else { | |
| return DeepState_One(); | |
| } | |
| } | |
| /* Return a symbolic value of a given type. */ | |
| int DeepState_Bool(void) { | |
| if (DeepState_InputIndex >= DeepState_InputSize) { | |
| DeepState_Abandon("Exceeded set input limit. Set or expand DEEPSTATE_SIZE to write more bytes."); | |
| } | |
| if (FLAGS_verbose_reads) { | |
| printf("Reading byte as boolean at %u\n", DeepState_InputIndex); | |
| } | |
| return DeepState_Input[DeepState_InputIndex++] & 1; | |
| } | |
| /* Return a string path to an input file or directory without parsing it to a type. This is | |
| * useful method in the case where a tested function only takes a path input in order | |
| * to generate some specialized structured type. */ | |
| const char * DeepState_InputPath(char *testcase_path) { | |
| struct stat statbuf; | |
| char *abspath; | |
| /* Use specified path if no --input_test* flag specified. Override if --input_* args specified. */ | |
| if (testcase_path) { | |
| if (!HAS_FLAG_input_test_file && !HAS_FLAG_input_test_files_dir) { | |
| abspath = realpath(testcase_path, NULL); | |
| } | |
| } | |
| /* Prioritize using CLI-specified input paths, for the sake of fuzzing */ | |
| if (HAS_FLAG_input_test_file) { | |
| abspath = realpath(FLAGS_input_test_file, NULL); | |
| } else if (HAS_FLAG_input_test_files_dir) { | |
| abspath = realpath(FLAGS_input_test_files_dir, NULL); | |
| } else { | |
| DeepState_Abandon("No usable path specified for DeepState_InputPath."); | |
| } | |
| if (stat(abspath, &statbuf) != 0) { | |
| DeepState_Abandon("Specified input path does not exist."); | |
| } | |
| if (HAS_FLAG_input_test_files_dir) { | |
| if (!S_ISDIR(statbuf.st_mode)) { | |
| DeepState_Abandon("Specified input directory is not a directory."); | |
| } | |
| } | |
| DeepState_LogFormat(DeepState_LogInfo, "Using `%s` as input path.", abspath); | |
| return abspath; | |
| } | |
| #define MAKE_SYMBOL_FUNC(Type, type) \ | |
| type DeepState_ ## Type(void) { \ | |
| if ((DeepState_InputIndex + sizeof(type)) > DeepState_InputSize) { \ | |
| DeepState_Abandon("Exceeded set input limit. Set or expand DEEPSTATE_SIZE to write more bytes."); \ | |
| } \ | |
| type val = 0; \ | |
| if (FLAGS_verbose_reads) { \ | |
| printf("STARTING MULTI-BYTE READ\n"); \ | |
| } \ | |
| _Pragma("unroll") \ | |
| for (size_t i = 0; i < sizeof(type); ++i) { \ | |
| if (FLAGS_verbose_reads) { \ | |
| printf("Reading byte at %u\n", DeepState_InputIndex); \ | |
| } \ | |
| val = (val << 8) | ((type) DeepState_Input[DeepState_InputIndex++]); \ | |
| } \ | |
| if (FLAGS_verbose_reads) { \ | |
| printf("FINISHED MULTI-BYTE READ\n"); \ | |
| } \ | |
| return val; \ | |
| } | |
| MAKE_SYMBOL_FUNC(Size, size_t) | |
| MAKE_SYMBOL_FUNC(Long, long) | |
| float DeepState_Float(void) { | |
| float float_v; | |
| DeepState_SymbolizeData(&float_v, &float_v + 1); | |
| return float_v; | |
| } | |
| double DeepState_Double(void) { | |
| double double_v; | |
| DeepState_SymbolizeData(&double_v, &double_v + 1); | |
| return double_v; | |
| } | |
| MAKE_SYMBOL_FUNC(UInt64, uint64_t) | |
| int64_t DeepState_Int64(void) { | |
| return (int64_t) DeepState_UInt64(); | |
| } | |
| MAKE_SYMBOL_FUNC(UInt, uint32_t) | |
| int32_t DeepState_Int(void) { | |
| return (int32_t) DeepState_UInt(); | |
| } | |
| MAKE_SYMBOL_FUNC(UShort, uint16_t) | |
| int16_t DeepState_Short(void) { | |
| return (int16_t) DeepState_UShort(); | |
| } | |
| MAKE_SYMBOL_FUNC(UChar, uint8_t) | |
| int8_t DeepState_Char(void) { | |
| return (int8_t) DeepState_UChar(); | |
| } | |
| #undef MAKE_SYMBOL_FUNC | |
| float DeepState_FloatInRange(float low, float high) { | |
| if (low > high) { | |
| return DeepState_FloatInRange(high, low); | |
| } | |
| if (low < 0.0) { // Handle negatives differently | |
| if (high > 0.0) { | |
| if (DeepState_Bool()) { | |
| return -(DeepState_FloatInRange(0.0, -low)); | |
| } else { | |
| return DeepState_FloatInRange(0.0, high); | |
| } | |
| } else { | |
| return -(DeepState_FloatInRange(-high, -low)); | |
| } | |
| } | |
| int32_t int_v = DeepState_IntInRange(*(int32_t *)&low, *(int32_t *)&high); | |
| float float_v = *(float*)&int_v; | |
| assume (float_v >= low); | |
| assume (float_v <= high); | |
| return float_v; | |
| } | |
| double DeepState_DoubleInRange(double low, double high) { | |
| if (low > high) { | |
| return DeepState_DoubleInRange(high, low); | |
| } | |
| if (low < 0.0) { // Handle negatives differently | |
| if (high > 0.0) { | |
| if (DeepState_Bool()) { | |
| return -(DeepState_DoubleInRange(0.0, -low)); | |
| } else { | |
| return DeepState_DoubleInRange(0.0, high); | |
| } | |
| } else { | |
| return -(DeepState_DoubleInRange(-high, -low)); | |
| } | |
| } | |
| int64_t int_v = DeepState_Int64InRange(*(int64_t *)&low, *(int64_t *)&high); | |
| double double_v = *(double*)&int_v; | |
| assume (double_v >= low); | |
| assume (double_v <= high); | |
| return double_v; | |
| } | |
| int32_t DeepState_RandInt() { | |
| return DeepState_IntInRange(0, RAND_MAX); | |
| } | |
| /* Returns the minimum satisfiable value for a given symbolic value, given | |
| * the constraints present on that value. */ | |
| uint32_t DeepState_MinUInt(uint32_t v) { | |
| return v; | |
| } | |
| int32_t DeepState_MinInt(int32_t v) { | |
| return (int32_t) (DeepState_MinUInt(((uint32_t) v) + 0x80000000U) - | |
| 0x80000000U); | |
| } | |
| /* Returns the maximum satisfiable value for a given symbolic value, given | |
| * the constraints present on that value. */ | |
| uint32_t DeepState_MaxUInt(uint32_t v) { | |
| return v; | |
| } | |
| int32_t DeepState_MaxInt(int32_t v) { | |
| return (int32_t) (DeepState_MaxUInt(((uint32_t) v) + 0x80000000U) - | |
| 0x80000000U); | |
| } | |
| /* Function to clean up generated strings, and any other DeepState-managed data. */ | |
| extern void DeepState_CleanUp() { | |
| for (int i = 0; i < DeepState_GeneratedAllocsIndex; i++) { | |
| free(DeepState_GeneratedAllocs[i]); | |
| } | |
| DeepState_GeneratedAllocsIndex = 0; | |
| for (int i = 0; i < DeepState_SwarmConfigsIndex; i++) { | |
| free(DeepState_SwarmConfigs[i]->file); | |
| free(DeepState_SwarmConfigs[i]->fmap); | |
| free(DeepState_SwarmConfigs[i]); | |
| } | |
| DeepState_SwarmConfigsIndex = 0; | |
| } | |
| void _DeepState_Assume(int expr, const char *expr_str, const char *file, | |
| unsigned line) { | |
| if (!expr) { | |
| DeepState_LogFormat(DeepState_LogError, | |
| "%s(%u): Assumption %s failed", | |
| file, line, expr_str); | |
| DeepState_Abandon("Assumption failed"); | |
| } | |
| } | |
| int DeepState_IsSymbolicUInt(uint32_t x) { | |
| (void) x; | |
| return 0; | |
| } | |
| /* Defined in Stream.c */ | |
| extern void _DeepState_StreamInt(enum DeepState_LogLevel level, | |
| const char *format, | |
| const char *unpack, uint64_t *val); | |
| extern void _DeepState_StreamFloat(enum DeepState_LogLevel level, | |
| const char *format, | |
| const char *unpack, double *val); | |
| extern void _DeepState_StreamString(enum DeepState_LogLevel level, | |
| const char *format, | |
| const char *str); | |
| /* A DeepState-specific symbol that is needed for hooking. */ | |
| struct DeepState_IndexEntry { | |
| const char * const name; | |
| void * const address; | |
| }; | |
| /* An index of symbols that the symbolic executors will hook or | |
| * need access to. */ | |
| const struct DeepState_IndexEntry DeepState_API[] = { | |
| /* Control-flow during the test. */ | |
| {"Pass", (void *) DeepState_Pass}, | |
| {"Crash", (void *) DeepState_Crash}, | |
| {"Fail", (void *) DeepState_Fail}, | |
| {"SoftFail", (void *) DeepState_SoftFail}, | |
| {"Abandon", (void *) DeepState_Abandon}, | |
| /* Locating the tests. */ | |
| {"LastTestInfo", (void *) &DeepState_LastTestInfo}, | |
| /* Source of symbolic bytes. */ | |
| {"InputBegin", (void *) &(DeepState_Input[0])}, | |
| {"InputEnd", (void *) &(DeepState_Input[DeepState_InputSize])}, | |
| {"InputIndex", (void *) &DeepState_InputIndex}, | |
| /* Solver APIs. */ | |
| {"Assume", (void *) _DeepState_Assume}, | |
| {"IsSymbolicUInt", (void *) DeepState_IsSymbolicUInt}, | |
| {"ConcretizeData", (void *) DeepState_ConcretizeData}, | |
| {"ConcretizeCStr", (void *) DeepState_ConcretizeCStr}, | |
| {"MinUInt", (void *) DeepState_MinUInt}, | |
| {"MaxUInt", (void *) DeepState_MaxUInt}, | |
| /* Logging API. */ | |
| {"Log", (void *) DeepState_Log}, | |
| /* Streaming API for deferred logging. */ | |
| {"ClearStream", (void *) DeepState_ClearStream}, | |
| {"LogStream", (void *) DeepState_LogStream}, | |
| {"StreamInt", (void *) _DeepState_StreamInt}, | |
| {"StreamFloat", (void *) _DeepState_StreamFloat}, | |
| {"StreamString", (void *) _DeepState_StreamString}, | |
| {"UsingLibFuzzer", (void *) &DeepState_UsingLibFuzzer}, | |
| {"UsingSymExec", (void *) &DeepState_UsingSymExec}, | |
| {NULL, NULL}, | |
| }; | |
| /* Set up DeepState. */ | |
| DEEPSTATE_NOINLINE | |
| void DeepState_Setup(void) { | |
| static int was_setup = 0; | |
| if (!was_setup) { | |
| DeepState_AllocCurrentTestRun(); | |
| was_setup = 1; | |
| } | |
| /* Sort the test cases by line number. */ | |
| struct DeepState_TestInfo *current = DeepState_LastTestInfo; | |
| struct DeepState_TestInfo *min_node = current->prev; | |
| current->prev = NULL; | |
| while (min_node != NULL) { | |
| struct DeepState_TestInfo *temp = min_node; | |
| min_node = min_node->prev; | |
| temp->prev = current; | |
| current = temp; | |
| } | |
| DeepState_FirstTestInfo = current; | |
| } | |
| /* Tear down DeepState. */ | |
| void DeepState_Teardown(void) { | |
| } | |
| /* Notify that we're about to begin a test. */ | |
| void DeepState_Begin(struct DeepState_TestInfo *test) { | |
| DeepState_InitCurrentTestRun(test); | |
| DeepState_LogFormat(DeepState_LogTrace, "Running: %s from %s(%u)", | |
| test->test_name, test->file_name, test->line_number); | |
| } | |
| /* Save a failing test. */ | |
| /* Runs in a child process, under the control of Dr. Memory */ | |
| void DrMemFuzzFunc(volatile uint8_t *buff, size_t size) { | |
| struct DeepState_TestInfo *test = DeepState_DrFuzzTest; | |
| DeepState_InputIndex = 0; | |
| DeepState_SwarmConfigsIndex = 0; | |
| DeepState_InitCurrentTestRun(test); | |
| DeepState_LogFormat(DeepState_LogTrace, "Running: %s from %s(%u)", | |
| test->test_name, test->file_name, test->line_number); | |
| if (!setjmp(DeepState_ReturnToRun)) { | |
| /* Convert uncaught C++ exceptions into a test failure. */ | |
| #if defined(__cplusplus) && defined(__cpp_exceptions) | |
| try { | |
| #endif /* __cplusplus */ | |
| test->test_func(); | |
| DeepState_CleanUp(); | |
| DeepState_Pass(); | |
| #if defined(__cplusplus) && defined(__cpp_exceptions) | |
| } catch(...) { | |
| DeepState_Fail(); | |
| } | |
| #endif /* __cplusplus */ | |
| /* We caught a failure when running the test. */ | |
| } else if (DeepState_CatchFail()) { | |
| DeepState_LogFormat(DeepState_LogError, "Failed: %s", test->test_name); | |
| if (HAS_FLAG_output_test_dir) { | |
| DeepState_SaveFailingTest(); | |
| } | |
| /* The test was abandoned. We may have gotten soft failures before | |
| * abandoning, so we prefer to catch those first. */ | |
| } else if (DeepState_CatchAbandoned()) { | |
| DeepState_LogFormat(DeepState_LogError, "Abandoned: %s", test->test_name); | |
| /* The test passed. */ | |
| } else { | |
| DeepState_LogFormat(DeepState_LogTrace, "Passed: %s", test->test_name); | |
| if (HAS_FLAG_output_test_dir) { | |
| DeepState_SavePassingTest(); | |
| } | |
| } | |
| } | |
| void DeepState_Warn_srand(unsigned int seed) { | |
| DeepState_LogFormat(DeepState_LogWarning, | |
| "srand under DeepState has no effect: rand is re-defined as DeepState_Int"); | |
| } | |
| void DeepState_RunSavedTakeOverCases(jmp_buf env, | |
| struct DeepState_TestInfo *test) { | |
| int num_failed_tests = 0; | |
| const char *test_case_dir = FLAGS_input_test_dir; | |
| DIR *dir_fd = opendir(test_case_dir); | |
| if (dir_fd == NULL) { | |
| DeepState_LogFormat(DeepState_LogInfo, | |
| "Skipping test `%s`, no saved test cases", | |
| test->test_name); | |
| return; | |
| } | |
| struct dirent *dp; | |
| /* Read generated test cases and run a test for each file found. */ | |
| while ((dp = readdir(dir_fd)) != NULL) { | |
| if (DeepState_IsTestCaseFile(dp->d_name)) { | |
| DeepState_InitCurrentTestRun(test); | |
| pid_t case_pid = fork(); | |
| if (!case_pid) { | |
| DeepState_Begin(test); | |
| size_t path_len = 2 + sizeof(char) * (strlen(test_case_dir) + | |
| strlen(dp->d_name)); | |
| char *path = (char *) malloc(path_len); | |
| if (path == NULL) { | |
| DeepState_Abandon("Error allocating memory"); | |
| } | |
| snprintf(path, path_len, "%s/%s", test_case_dir, dp->d_name); | |
| DeepState_InitInputFromFile(path); | |
| free(path); | |
| longjmp(env, 1); | |
| } | |
| int wstatus; | |
| waitpid(case_pid, &wstatus, 0); | |
| /* If we exited normally, the status code tells us if the test passed. */ | |
| if (WIFEXITED(wstatus)) { | |
| switch (DeepState_CurrentTestRun->result) { | |
| case DeepState_TestRunPass: | |
| DeepState_LogFormat(DeepState_LogTrace, | |
| "Passed: TakeOver test with data from `%s`", | |
| dp->d_name); | |
| break; | |
| case DeepState_TestRunFail: | |
| DeepState_LogFormat(DeepState_LogError, | |
| "Failed: TakeOver test with data from `%s`", | |
| dp->d_name); | |
| break; | |
| case DeepState_TestRunCrash: | |
| DeepState_LogFormat(DeepState_LogError, | |
| "Crashed: TakeOver test with data from `%s`", | |
| dp->d_name); | |
| break; | |
| case DeepState_TestRunAbandon: | |
| DeepState_LogFormat(DeepState_LogError, | |
| "Abandoned: TakeOver test with data from `%s`", | |
| dp->d_name); | |
| break; | |
| default: /* Should never happen */ | |
| DeepState_LogFormat(DeepState_LogError, | |
| "Error: Invalid test run result %d from `%s`", | |
| DeepState_CurrentTestRun->result, dp->d_name); | |
| } | |
| } else { | |
| /* If here, we exited abnormally but didn't catch it in the signal | |
| * handler, and thus the test failed due to a crash. */ | |
| DeepState_LogFormat(DeepState_LogError, | |
| "Crashed: TakeOver test with data from `%s`", | |
| dp->d_name); | |
| } | |
| } | |
| } | |
| closedir(dir_fd); | |
| } | |
| int DeepState_TakeOver(void) { | |
| struct DeepState_TestInfo test = { | |
| .prev = NULL, | |
| .test_func = NULL, | |
| .test_name = "__takeover_test", | |
| .file_name = "__takeover_file", | |
| .line_number = 0, | |
| }; | |
| DeepState_AllocCurrentTestRun(); | |
| jmp_buf env; | |
| if (!setjmp(env)) { | |
| DeepState_RunSavedTakeOverCases(env, &test); | |
| exit(0); | |
| } | |
| return 0; | |
| } | |
| /* Notify that we're about to begin a test while running under Dr. Fuzz. */ | |
| void DeepState_BeginDrFuzz(struct DeepState_TestInfo *test) { | |
| DeepState_DrFuzzTest = test; | |
| DrMemFuzzFunc(DeepState_Input, DeepState_InputSize); | |
| } | |
| /* Right now "fake" a hexdigest by just using random bytes. Not ideal. */ | |
| void makeFilename(char *name, size_t size) { | |
| const char *entities = "0123456789abcdef"; | |
| for (int i = 0; i < size; i++) { | |
| name[i] = entities[rand()%16]; | |
| } | |
| } | |
| void writeInputData(char* name, int important) { | |
| size_t path_len = 2 + sizeof(char) * (strlen(FLAGS_output_test_dir) + strlen(name)); | |
| char *path = (char *) malloc(path_len); | |
| snprintf(path, path_len, "%s/%s", FLAGS_output_test_dir, name); | |
| FILE *fp = fopen(path, "wb"); | |
| if (fp == NULL) { | |
| DeepState_LogFormat(DeepState_LogError, "Failed to create file `%s`", path); | |
| free(path); | |
| return; | |
| } | |
| size_t written = fwrite((void *)DeepState_Input, 1, DeepState_InputSize, fp); | |
| if (written != DeepState_InputSize) { | |
| DeepState_LogFormat(DeepState_LogError, "Failed to write to file `%s`", path); | |
| } else { | |
| if (important) { | |
| DeepState_LogFormat(DeepState_LogInfo, "Saved test case in file `%s`", path); | |
| } else { | |
| DeepState_LogFormat(DeepState_LogTrace, "Saved test case in file `%s`", path); | |
| } | |
| } | |
| free(path); | |
| fclose(fp); | |
| } | |
| /* Save a passing test to the output test directory. */ | |
| void DeepState_SavePassingTest(void) { | |
| char name[48]; | |
| makeFilename(name, 40); | |
| name[40] = 0; | |
| strncat(name, ".pass", 48); | |
| writeInputData(name, 0); | |
| } | |
| /* Save a failing test to the output test directory. */ | |
| void DeepState_SaveFailingTest(void) { | |
| char name[48]; | |
| makeFilename(name, 40); | |
| name[40] = 0; | |
| strncat(name, ".fail", 48); | |
| writeInputData(name, 1); | |
| } | |
| /* Save a crashing test to the output test directory. */ | |
| void DeepState_SaveCrashingTest(void) { | |
| char name[48]; | |
| makeFilename(name, 40); | |
| name[40] = 0; | |
| strncat(name, ".crash", 48); | |
| writeInputData(name, 1); | |
| } | |
| /* Return the first test case to run. */ | |
| struct DeepState_TestInfo *DeepState_FirstTest(void) { | |
| return DeepState_FirstTestInfo; | |
| } | |
| /* Returns `true` if a failure was caught for the current test case. */ | |
| bool DeepState_CatchFail(void) { | |
| return DeepState_CurrentTestRun->result == DeepState_TestRunFail; | |
| } | |
| /* Returns `true` if the current test case was abandoned. */ | |
| bool DeepState_CatchAbandoned(void) { | |
| return DeepState_CurrentTestRun->result == DeepState_TestRunAbandon; | |
| } | |
| /* Fuzz test `FLAGS_input_which_test` or first test, if not defined. | |
| Has to be defined here since we redefine rand in the header. */ | |
| int DeepState_Fuzz(void){ | |
| DeepState_LogFormat(DeepState_LogInfo, "Starting fuzzing"); | |
| if (!HAS_FLAG_min_log_level) { | |
| FLAGS_min_log_level = 2; | |
| } | |
| if (HAS_FLAG_seed) { | |
| srand(FLAGS_seed); | |
| } else { | |
| unsigned int seed = time(NULL); | |
| DeepState_LogFormat(DeepState_LogWarning, "No seed provided; using %u", seed); | |
| srand(seed); | |
| } | |
| long start = (long)time(NULL); | |
| long current = (long)time(NULL); | |
| unsigned diff = 0; | |
| unsigned int i = 0; | |
| int num_failed_tests = 0; | |
| int num_passed_tests = 0; | |
| int num_abandoned_tests = 0; | |
| struct DeepState_TestInfo *test = NULL; | |
| for (test = DeepState_FirstTest(); test != NULL; test = test->prev) { | |
| if (HAS_FLAG_input_which_test) { | |
| if (strcmp(FLAGS_input_which_test, test->test_name) == 0) { | |
| break; | |
| } | |
| } else { | |
| DeepState_LogFormat(DeepState_LogWarning, | |
| "No test specified, defaulting to first test defined (%s)", | |
| test->test_name); | |
| break; | |
| } | |
| } | |
| if (test == NULL) { | |
| DeepState_LogFormat(DeepState_LogInfo, | |
| "Could not find matching test for %s", | |
| FLAGS_input_which_test); | |
| return 0; | |
| } | |
| unsigned int last_status = 0; | |
| while (diff < FLAGS_timeout) { | |
| i++; | |
| if ((diff != last_status) && ((diff % 30) == 0) ) { | |
| time_t t = time(NULL); | |
| struct tm tm = *localtime(&t); | |
| DeepState_LogFormat(DeepState_LogInfo, "%d-%02d-%02d %02d:%02d:%02d: %u tests/second: %d failed/%d passed/%d abandoned", | |
| tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, i/diff, | |
| num_failed_tests, num_passed_tests, num_abandoned_tests); | |
| last_status = diff; | |
| } | |
| enum DeepState_TestRunResult result = DeepState_FuzzOneTestCase(test); | |
| if ((result == DeepState_TestRunFail) || (result == DeepState_TestRunCrash)) { | |
| num_failed_tests++; | |
| } else if (result == DeepState_TestRunPass) { | |
| num_passed_tests++; | |
| } else if (result == DeepState_TestRunAbandon) { | |
| num_abandoned_tests++; | |
| } | |
| current = (long)time(NULL); | |
| diff = current-start; | |
| } | |
| DeepState_LogFormat(DeepState_LogInfo, "Done fuzzing! Ran %u tests (%u tests/second) with %d failed/%d passed/%d abandoned tests", | |
| i, i/diff, num_failed_tests, num_passed_tests, num_abandoned_tests); | |
| return num_failed_tests; | |
| } | |
| /* Run a test case with input initialized by fuzzing. | |
| Has to be defined here since we redefine rand in the header. */ | |
| enum DeepState_TestRunResult DeepState_FuzzOneTestCase(struct DeepState_TestInfo *test) { | |
| DeepState_InputIndex = 0; | |
| DeepState_SwarmConfigsIndex = 0; | |
| for (int i = 0; i < DeepState_InputSize; i++) { | |
| DeepState_Input[i] = (char)rand(); | |
| } | |
| DeepState_Begin(test); | |
| enum DeepState_TestRunResult result = DeepState_ForkAndRunTest(test); | |
| if (result == DeepState_TestRunCrash) { | |
| DeepState_LogFormat(DeepState_LogError, "Crashed: %s", test->test_name); | |
| if (HAS_FLAG_output_test_dir) { | |
| DeepState_SaveCrashingTest(); | |
| } | |
| DeepState_Crash(); | |
| } | |
| if (FLAGS_abort_on_fail && ((result == DeepState_TestRunCrash) || | |
| (result == DeepState_TestRunFail))) { | |
| DeepState_HardCrash(); | |
| } | |
| if (FLAGS_exit_on_fail && ((result == DeepState_TestRunCrash) || | |
| (result == DeepState_TestRunFail))) { | |
| exit(255); // Terminate the testing | |
| } | |
| return result; | |
| } | |
| extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { | |
| if (Size > sizeof(DeepState_Input)) { | |
| return 0; // Just ignore any too-big inputs | |
| } | |
| DeepState_UsingLibFuzzer = 1; | |
| const char* loud = getenv("LIBFUZZER_LOUD"); | |
| if (loud != NULL) { | |
| FLAGS_min_log_level = 0; | |
| DeepState_LibFuzzerLoud = 1; | |
| } | |
| struct DeepState_TestInfo *test = NULL; | |
| DeepState_InitOptions(0, ""); | |
| DeepState_Setup(); | |
| /* we also want to manually allocate CurrentTestRun */ | |
| void *mem = malloc(sizeof(struct DeepState_TestRunInfo)); | |
| DeepState_CurrentTestRun = (struct DeepState_TestRunInfo *) mem; | |
| test = DeepState_FirstTest(); | |
| const char* which_test = getenv("LIBFUZZER_WHICH_TEST"); | |
| if (which_test != NULL) { | |
| for (test = DeepState_FirstTest(); test != NULL; test = test->prev) { | |
| if (strncmp(which_test, test->test_name, strnlen(which_test, 1024)) == 0) { | |
| break; | |
| } | |
| } | |
| } | |
| if (test == NULL) { | |
| DeepState_LogFormat(DeepState_LogFatal, | |
| "Could not find matching test for %s (from LIBFUZZER_WHICH_TEST)", | |
| which_test); | |
| exit(255); | |
| } | |
| DeepState_MemScrub((void *) DeepState_Input, sizeof(DeepState_Input)); | |
| DeepState_InputIndex = 0; | |
| DeepState_SwarmConfigsIndex = 0; | |
| memcpy((void *) DeepState_Input, (void *) Data, Size); | |
| DeepState_Begin(test); | |
| enum DeepState_TestRunResult result = DeepState_RunTestNoFork(test); | |
| DeepState_CleanUp(); | |
| const char* abort_check = getenv("LIBFUZZER_ABORT_ON_FAIL"); | |
| if (abort_check != NULL) { | |
| if ((result == DeepState_TestRunFail) || (result == DeepState_TestRunCrash)) { | |
| assert(0); // Terminate the testing more permanently | |
| } | |
| } | |
| const char* exit_check = getenv("LIBFUZZER_EXIT_ON_FAIL"); | |
| if (exit_check != NULL) { | |
| if ((result == DeepState_TestRunFail) || (result == DeepState_TestRunCrash)) { | |
| exit(255); // Terminate the testing | |
| } | |
| } | |
| DeepState_Teardown(); | |
| DeepState_CurrentTestRun = NULL; | |
| free(mem); | |
| return 0; // Non-zero return values are reserved for future use. | |
| } | |
| extern int FuzzerEntrypoint(const uint8_t *data, size_t size) { | |
| LLVMFuzzerTestOneInput(data, size); | |
| return 0; | |
| } | |
| /* Overwrite libc's abort. */ | |
| void abort(void) { | |
| DeepState_Fail(); | |
| } | |
| void __assert_fail(const char * assertion, const char * file, | |
| unsigned int line, const char * function) { | |
| DeepState_LogFormat(DeepState_LogFatal, | |
| "%s(%u): Assertion %s failed in function %s", | |
| file, line, assertion, function); | |
| if (FLAGS_abort_on_fail) { | |
| DeepState_HardCrash(); | |
| } | |
| __builtin_unreachable(); | |
| } | |
| void __stack_chk_fail(void) { | |
| DeepState_Log(DeepState_LogFatal, "Stack smash detected"); | |
| __builtin_unreachable(); | |
| } | |
| #ifndef LIBFUZZER | |
| #ifndef HEADLESS | |
| __attribute__((weak)) | |
| int main(int argc, char *argv[]) { | |
| int ret = 0; | |
| DeepState_Setup(); | |
| DeepState_InitOptions(argc, argv); | |
| ret = DeepState_Run(); | |
| DeepState_Teardown(); | |
| return ret; | |
| } | |
| #endif | |
| #endif | |
| DEEPSTATE_END_EXTERN_C |