diff --git a/.travis.yml b/.travis.yml index 2cc49e8..fb8741a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ env: - TASK=ONEOF - TASK=OVERFLOW - TASK=PRIMES +- TASK=RUNLEN - TASK=STREAMINGANDFORMATTING - TASK=TAKEOVER script: @@ -34,6 +35,7 @@ script: - if [ $TASK = KLEE ]; then nosetests tests/test_klee.py ; fi - if [ $TASK = LISTS ]; then nosetests tests/test_lists.py ; fi - if [ $TASK = ONEOF ]; then nosetests tests/test_oneof.py ; fi +- if [ $TASK = RUNLEN ]; then nosetests tests/test_runlen.py ; fi - if [ $TASK = OVERFLOW ]; then nosetests tests/test_overflow.py ; fi - if [ $TASK = PRIMES ]; then nosetests tests/test_primes.py ; fi - if [ $TASK = STREAMINGANDFORMATTING ]; then nosetests tests/test_streamingandformatting.py ; fi diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f8e7a21..264bc36 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -102,6 +102,16 @@ if (BUILD_LIBFUZZER) set_target_properties(StreamingAndFormatting_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER") endif() +add_executable(Runlen Runlen.cpp) +target_link_libraries(Runlen deepstate) + +if (BUILD_LIBFUZZER) + add_executable(Runlen_LF Runlen.cpp) + target_link_libraries(Runlen_LF deepstate_LF) + target_link_libraries (Runlen_LF "-fsanitize=fuzzer,undefined") + set_target_properties(Runlen_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER") +endif() + if (NOT APPLE) add_executable(Squares Squares.c) target_link_libraries(Squares deepstate) diff --git a/examples/Runlen.cpp b/examples/Runlen.cpp new file mode 100644 index 0000000..b7d12aa --- /dev/null +++ b/examples/Runlen.cpp @@ -0,0 +1,63 @@ +#include + +using namespace deepstate; + +/* Simple, buggy, run-length encoding that creates "human readable" + * encodings by adding 'A'-1 to the count, and splitting at 26 */ + +char* encode(const char* input) { + unsigned int len = strlen(input); + char* encoded = (char*)malloc((len*2)+1); + int pos = 0; + if (strlen(input) > 0) { + unsigned char last = input[0]; int count = 1; + for (int i = 1; i < len; i++) { + if (((unsigned char)input[i] == last) && (count < 26)) + count++; + else { + encoded[pos++] = last; encoded[pos++] = 64 + count; + last = (unsigned char)input[i]; count = 1; + } + } + encoded[pos++] = last; encoded[pos++] = 65; // Should be 64 + count + } + encoded[pos] = '\0'; + return encoded; +} + +char* decode(const char* output) { + unsigned int len = strlen(output); + char* decoded = (char*)malloc((len/2)*26); + int pos = 0; + if (strlen(output) > 0) { + for (int i = 0; i < len; i += 2) + for (int j = 0; j < (output[i+1] - 64); j++) + decoded[pos++] = output[i]; + } + decoded[pos] = '\0'; + return decoded; +} + +void printBytes(const char* bytes) { + unsigned int len = strlen(bytes); + for (int i = 0; i < len; i++) + LOG(ERROR) << "[" << i << "] = " << (unsigned int)(unsigned char)bytes[i]; +} + +// Can be (much) higher (e.g., > 1024) if we're using fuzzing, not symbolic execution +#define MAX_STR_LEN 6 + +TEST(Runlength, EncodeDecode) { + char* original = DeepState_CStrUpToLen(MAX_STR_LEN); + char* encoded = encode(original); + char* roundtrip = decode(encoded); + if (!(strncmp(roundtrip, original, MAX_STR_LEN) == 0)) { + LOG(ERROR) << "ORIGINAL:"; + printBytes(original); + LOG(ERROR) << "ENCODED:"; + printBytes(encoded); + LOG(ERROR) << "ROUNDTRIP:"; + printBytes(roundtrip); + ASSERT (0) << "Round trip check failed"; + } +} diff --git a/examples/Squares.c b/examples/Squares.c index 466ba61..5483512 100644 --- a/examples/Squares.c +++ b/examples/Squares.c @@ -10,7 +10,7 @@ int square(int x) { DeepState_EntryPoint(test_main) { const char *new_args[2]; new_args[0] = "deepstate"; - new_args[1] = DeepState_CStr(8); + new_args[1] = DeepState_CStr_C(8, 0); DeepState_Assert(0 == old_main(2, new_args)); } diff --git a/src/include/deepstate/DeepState.h b/src/include/deepstate/DeepState.h index 2912763..1f9f1a7 100644 --- a/src/include/deepstate/DeepState.h +++ b/src/include/deepstate/DeepState.h @@ -142,9 +142,12 @@ DEEPSTATE_INLINE static int8_t DeepState_MaxChar(int8_t v) { return (int8_t) DeepState_MaxInt(v); } +/* Function to clean up generated strings, and any other DeepState-managed data. */ +extern void DeepState_CleanUp(); + /* 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. */ + * value with a concrete value. */ extern int DeepState_IsTrue(int expr); /* Always returns `1`. */ @@ -159,15 +162,22 @@ extern int DeepState_ZeroSink(int); /* Symbolize the data in the exclusive range `[begin, end)`. */ extern void DeepState_SymbolizeData(void *begin, void *end); +/* Symbolize the data in the exclusive range `[begin, end)` with no nulls. */ +extern void DeepState_SymbolizeDataNoNull(void *begin, void *end); + /* Concretize some data in exclusive the range `[begin, end)`. Returns a * concrete pointer to the beginning of the concretized data. */ extern void *DeepState_ConcretizeData(void *begin, void *end); -/* Return a symbolic C string of length `len`. */ -extern char *DeepState_CStr(size_t len); +/* Assign a symbolic C string of _strlen_ `len` -- with only chars in allowed, + * if `allowed` is non-null; needs space for null + len bytes */ +extern void DeepState_AssignCStr_C(char* str, size_t len, const char* allowed); + +/* Return a symbolic C string of strlen `len`. */ +extern char *DeepState_CStr_C(size_t len, const char* allowed); /* Symbolize a C string */ -void DeepState_SymbolizeCStr(char *begin); +void DeepState_SymbolizeCStr_C(char *begin, const char* allowed); /* Concretize a C string. Returns a pointer to the beginning of the * concretized C string. */ @@ -591,6 +601,7 @@ DeepState_ForkAndRunTest(struct DeepState_TestInfo *test) { test_pid = fork(); if (!test_pid) { DeepState_RunTest(test); + /* No need to clean up in a fork; exit() is the ultimate garbage collector */ } } int wstatus = 0; @@ -598,6 +609,7 @@ DeepState_ForkAndRunTest(struct DeepState_TestInfo *test) { waitpid(test_pid, &wstatus, 0); } else { wstatus = DeepState_RunTestNoFork(test); + DeepState_CleanUp(); } /* If we exited normally, the status code tells us if the test passed. */ diff --git a/src/include/deepstate/DeepState.hpp b/src/include/deepstate/DeepState.hpp index fc8ae98..5e51760 100644 --- a/src/include/deepstate/DeepState.hpp +++ b/src/include/deepstate/DeepState.hpp @@ -314,7 +314,7 @@ static T Pump(T val, unsigned max=10) { } return Minimize(val); } - + template inline static void ForAll(void (*func)(Args...)) { func(Symbolic()...); @@ -345,7 +345,7 @@ inline static char OneOf(const char *str) { } return str[DeepState_IntInRange(0, strlen(str) - 1)]; } - + template inline static const T &OneOf(const std::vector &arr) { if (arr.empty()) { @@ -471,6 +471,35 @@ struct Comparer { } }; +/* Like DeepState_AssignCStr_C, but fills in a null `allowed` value. */ +inline static void DeepState_AssignCStr(char* str, size_t len, + const char* allowed = 0) { + DeepState_AssignCStr_C(str, len, allowed); +} + +/* Like DeepState_AssignCStr, but Pumps through possible string sizes. */ +inline static void DeepState_AssignCStrUpToLen(char* str, size_t max_len, + const char* allowed = 0) { + uint32_t len = DeepState_UIntInRange(0, max_len); + DeepState_AssignCStr_C(str, Pump(len, max_len+1), allowed); +} + +/* Like DeepState_CStr_C, but fills in a null `allowed` value. */ +inline static char* DeepState_CStr(size_t len, const char* allowed = 0) { + return DeepState_CStr_C(len, allowed); +} + +/* Like DeepState_CStr, but Pumps through possible string sizes. */ +inline static char* DeepState_CStrUpToLen(size_t max_len, const char* allowed = 0) { + uint32_t len = DeepState_UIntInRange(0, max_len); + return DeepState_CStr_C(Pump(len, max_len+1), allowed); +} + +/* Like DeepState_Symbolize_CStr, but fills in null `allowed` value. */ +inline static void DeepState_SymbolizeCStr(char *begin, const char* allowed = 0) { + DeepState_SymbolizeCStr_C(begin, allowed); +} + } // namespace deepstate #define ONE_OF ::deepstate::OneOf diff --git a/src/lib/DeepState.c b/src/lib/DeepState.c index dd2c84e..751ef11 100644 --- a/src/lib/DeepState.c +++ b/src/lib/DeepState.c @@ -56,6 +56,11 @@ int DeepState_UsingSymExec = 0; /* Set to 1 when we're using libFuzzer. */ int DeepState_UsingLibFuzzer = 0; +/* Array of DeepState generated strings. Impossible for there to + * be more than there are input bytes. Index stores where we are. */ +char* DeepState_GeneratedStrings[DeepState_InputSize]; +uint32_t DeepState_GeneratedStringsIndex = 0; + /* Pointer to the last registers DeepState_TestInfo data structure */ struct DeepState_TestInfo *DeepState_LastTestInfo = NULL; @@ -167,13 +172,63 @@ void DeepState_SymbolizeData(void *begin, void *end) { } } +/* 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("Read too many symbols"); + } + 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; } -/* Return a symbolic C string of length `len`. */ -char *DeepState_CStr(size_t len) { +/* 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 an SIZE_MAX-length string."); + } + if (NULL == str) { + DeepState_Abandon("Attempted to populate null pointer."); + } + if (len) { + if (!allowed) { + 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 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 an SIZE_MAX-length string"); } @@ -181,17 +236,35 @@ char *DeepState_CStr(size_t len) { if (NULL == str) { DeepState_Abandon("Can't allocate memory"); } + DeepState_GeneratedStrings[DeepState_GeneratedStringsIndex++] = str; if (len) { - DeepState_SymbolizeData(str, &(str[len - 1])); + if (!allowed) { + 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; } -/* Symbolize a C string */ -void DeepState_SymbolizeCStr(char *begin) { +/* Symbolize a C string; keeps the null terminator where it was. */ +void DeepState_SymbolizeCStr_C(char *begin, const char* allowed) { if (begin && begin[0]) { - DeepState_SymbolizeData(begin, begin + strlen(begin)); + if (!allowed) { + 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)]; + } + } } } @@ -317,6 +390,14 @@ int32_t DeepState_MaxInt(int32_t v) { 0x80000000U); } +/* Function to clean up generated strings, and any other DeepState-managed data. */ +extern void DeepState_CleanUp() { + for (int i = 0; i < DeepState_GeneratedStringsIndex; i++) { + free(DeepState_GeneratedStrings[i]); + } + DeepState_GeneratedStringsIndex = 0; +} + void _DeepState_Assume(int expr, const char *expr_str, const char *file, unsigned line) { if (!expr) { @@ -435,6 +516,7 @@ void DrMemFuzzFunc(volatile uint8_t *buff, size_t size) { #endif /* __cplusplus */ test->test_func(); + DeepState_CleanUp(); DeepState_Pass(); #if defined(__cplusplus) && defined(__cpp_exceptions) @@ -785,6 +867,7 @@ extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t 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) { diff --git a/tests/test_runlen.py b/tests/test_runlen.py new file mode 100644 index 0000000..a378329 --- /dev/null +++ b/tests/test_runlen.py @@ -0,0 +1,20 @@ +from __future__ import print_function +import deepstate_base +import logrun + + +class RunlenTest(deepstate_base.DeepStateTestCase): + def run_deepstate(self, deepstate): + if deepstate == "deepstate-manticore": + return # Just skip for now, we know it's too slow + (r, output) = logrun.logrun([deepstate, "build/examples/Runlen"], + "deepstate.out", 2900) + self.assertEqual(r, 0) + + self.assertTrue("Passed: Runlength_EncodeDecode" in output) + foundFailSave = False + for line in output.split("\n"): + if ("Saving input to" in line) and (".fail" in line): + foundFailSave = True + self.assertTrue(foundFailSave) +