Merge pull request #156 from trailofbits/add_deepstate_string

stab at string generator
This commit is contained in:
Alex Groce 2019-01-03 17:25:04 -07:00 committed by GitHub
commit d29654832e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 232 additions and 13 deletions

View File

@ -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

View File

@ -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)

63
examples/Runlen.cpp Normal file
View File

@ -0,0 +1,63 @@
#include <deepstate/DeepState.hpp>
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";
}
}

View File

@ -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));
}

View File

@ -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. */

View File

@ -314,7 +314,7 @@ static T Pump(T val, unsigned max=10) {
}
return Minimize(val);
}
template <typename... Args>
inline static void ForAll(void (*func)(Args...)) {
func(Symbolic<Args>()...);
@ -345,7 +345,7 @@ inline static char OneOf(const char *str) {
}
return str[DeepState_IntInRange(0, strlen(str) - 1)];
}
template <typename T>
inline static const T &OneOf(const std::vector<T> &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

View File

@ -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) {

20
tests/test_runlen.py Normal file
View File

@ -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)