Merge pull request #156 from trailofbits/add_deepstate_string
stab at string generator
This commit is contained in:
commit
d29654832e
@ -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
|
||||
|
||||
@ -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
63
examples/Runlen.cpp
Normal 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";
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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. */
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
20
tests/test_runlen.py
Normal 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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user