From 8a7b1c4faa8e9406983ae9355a440d3b2cefef0f Mon Sep 17 00:00:00 2001 From: Alex Groce Date: Sun, 30 Dec 2018 12:26:43 -0700 Subject: [PATCH] stab at string generator --- src/include/deepstate/DeepState.h | 20 +++++++++++- src/lib/DeepState.c | 54 +++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/include/deepstate/DeepState.h b/src/include/deepstate/DeepState.h index 73a9166..aa57282 100644 --- a/src/include/deepstate/DeepState.h +++ b/src/include/deepstate/DeepState.h @@ -139,9 +139,25 @@ DEEPSTATE_INLINE static int8_t DeepState_MaxChar(int8_t v) { return (int8_t) DeepState_MaxInt(v); } +/* Given a char pointer (assumed to have enough storage), places a string + * value in that location, with size characters (before null). If allowed is + * non-null, chooses characters from allowed char array. If null_terminated + * is true, the string will be null-terminated at size+1, and no prior character + * will be null. For null-terminated strings, storage must have space for the + * null terminator, so size should be 1 less than actual size. */ +extern void DeepState_AssignString(char *dest, size_t max_size, const char* allowed, + size_t allowed_size, int null_terminated); + +/* Returns a null-terminated string of size characters (before null), allocated on + * heap. DeepState will handle freeing these strings at termination of the test. */ +extern char* DeepState_String(size_t max_size); + +/* 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`. */ @@ -588,6 +604,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; @@ -595,6 +612,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/lib/DeepState.c b/src/lib/DeepState.c index b8445c2..425e80a 100644 --- a/src/lib/DeepState.c +++ b/src/lib/DeepState.c @@ -54,6 +54,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; @@ -311,6 +316,53 @@ int32_t DeepState_MaxInt(int32_t v) { 0x80000000U); } +/* Given a char pointer (assumed to have enough storage), places a string + * value in that location, with size characters (before null). If allowed is + * non-null, chooses characters from allowed char array. If null_terminated + * is true, the string will be null-terminated at size+1, and no prior character + * will be null. For null-terminated strings, storage must have space for the + * null terminator, so size should be 1 less than actual size. */ +void DeepState_AssignString(char *dest, size_t size, const char* allowed, + size_t allowed_size, int null_terminated) { + for(int i = 0; i < size; i++) { + if (allowed) { + size_t index = DeepState_UIntInRange(0, allowed_size); + dest[i] = allowed[index]; + } else if (null_terminated) { + dest[i] = DeepState_UCharInRange(1, UCHAR_MAX); /* Disallow null termination till end. */ + } else { + dest[i] = DeepState_UChar(); + } + } + if (null_terminated) { + dest[size] = 0; + } +} + +/* Returns a null-terminated string of size characters (before null), allocated on + * heap. DeepState will handle freeing these strings at termination of the test. */ +char* DeepState_String(size_t size) { + char* rstr = malloc(size+1); + if (rstr) { + DeepState_GeneratedStrings[DeepState_GeneratedStringsIndex++] = rstr; + for (int i = 0; i < size; i++) { + rstr[i] = DeepState_UCharInRange(1, UCHAR_MAX); /* Disallow null termination till end. */ + } + rstr[size] = 0; + } else { + DeepState_LogFormat(DeepState_LogFatal, "Unable to allocate to generate string!"); + } + return rstr; +} + +/* 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) { @@ -429,6 +481,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) @@ -759,6 +812,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) {