Improvements. Now hooking fewer functions, and instead making on big array symbolic. This will make it easier to run the inputs concretely (by eventually filling in the array with the concrete bytes), and will also make possible fuzzer integration (where the fuzzer mutates the bytes of the array) possible.
This commit is contained in:
@@ -25,6 +25,18 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Return a symbolic value of a given type. */
|
||||
extern int McTest_Bool(void);
|
||||
extern size_t McTest_Size(void);
|
||||
extern uint64_t McTest_UInt64(void);
|
||||
extern int64_t McTest_Int64(void);
|
||||
extern uint32_t McTest_UInt(void);
|
||||
extern int32_t McTest_Int(void);
|
||||
extern uint16_t McTest_UShort(void);
|
||||
extern int16_t McTest_Short(void);
|
||||
extern uint8_t McTest_UChar(void);
|
||||
extern int8_t McTest_Char(void);
|
||||
|
||||
/* Symbolize the data in the range `[begin, end)`. */
|
||||
extern void McTest_SymbolizeData(void *begin, void *end);
|
||||
|
||||
@@ -65,45 +77,23 @@ inline static char *McTest_CStr(size_t len) {
|
||||
|
||||
/* Creates an assumption about a symbolic value. Returns `1` if the assumption
|
||||
* can hold and was asserted. */
|
||||
extern int McTest_Assume(int expr);
|
||||
extern void _McTest_Assume(int expr);
|
||||
|
||||
#define McTest_Assume(x) _McTest_Assume(!!(x))
|
||||
|
||||
__attribute__((noreturn))
|
||||
extern void McTest_Fail(void);
|
||||
|
||||
__attribute__((noreturn))
|
||||
extern void McTest_Pass(void);
|
||||
|
||||
/* Asserts that `expr` must hold. */
|
||||
inline static void McTest_Assert(int expr) {
|
||||
if (McTest_Assume(!expr)) {
|
||||
abort();
|
||||
if (!expr) {
|
||||
McTest_Fail();
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a symbolic value of a given type. */
|
||||
extern int McTest_Bool(void);
|
||||
extern size_t McTest_Size(void);
|
||||
extern uint64_t McTest_UInt64(void);
|
||||
extern uint32_t McTest_UInt(void);
|
||||
|
||||
inline static int64_t McTest_Int64(void) {
|
||||
return (int64_t) McTest_UInt64();
|
||||
}
|
||||
|
||||
inline static int32_t McTest_Int(void) {
|
||||
return (int32_t) McTest_UInt();
|
||||
}
|
||||
|
||||
inline static uint16_t McTest_UShort(void) {
|
||||
return (uint16_t) McTest_UInt();
|
||||
}
|
||||
|
||||
inline static int16_t McTest_Short(void) {
|
||||
return (int16_t) McTest_UInt();
|
||||
}
|
||||
|
||||
inline static unsigned char McTest_UChar(void) {
|
||||
return (unsigned char) McTest_UInt();
|
||||
}
|
||||
|
||||
inline static char McTest_Char(void) {
|
||||
return (char) McTest_UInt();
|
||||
}
|
||||
|
||||
/* Return a symbolic value in a the range `[low_inc, high_inc]`. */
|
||||
#define MCTEST_MAKE_SYMBOLIC_RANGE(Tname, tname) \
|
||||
inline static tname McTest_ ## Tname ## InRange( \
|
||||
@@ -124,19 +114,6 @@ MCTEST_MAKE_SYMBOLIC_RANGE(UChar, unsigned char)
|
||||
|
||||
#undef MCTEST_MAKE_SYMBOLIC_RANGE
|
||||
|
||||
|
||||
/* Return a symbolic value of a given type. */
|
||||
extern int McTest_Bool(void);
|
||||
extern size_t McTest_Size(void);
|
||||
extern uint64_t McTest_UInt64(void);
|
||||
extern int64_t McTest_Int64(void);
|
||||
extern uint32_t McTest_UInt(void);
|
||||
extern int32_t McTest_Int(void);
|
||||
extern uint16_t McTest_UShort(void);
|
||||
extern int16_t McTest_Short(void);
|
||||
extern unsigned char McTest_UChar(void);
|
||||
extern char McTest_Char(void);
|
||||
|
||||
/* Predicates to check whether or not a particular value is symbolic */
|
||||
extern int McTest_IsSymbolicUInt(uint32_t x);
|
||||
|
||||
@@ -210,29 +187,28 @@ inline static int McTest_IsSymbolicDouble(double x) {
|
||||
#define McTest_EntryPoint(test_name) \
|
||||
_McTest_EntryPoint(test_name, __FILE__, __LINE__)
|
||||
|
||||
struct __attribute__((packed)) McTest_TestInfo {
|
||||
void (*test_func)(void);
|
||||
const char *test_name;
|
||||
const char *file_name;
|
||||
unsigned line_number;
|
||||
uint8_t padding[28 - (3 * sizeof(void *))];
|
||||
};
|
||||
|
||||
#define _McTest_EntryPoint(test_name, file, line) \
|
||||
static void McTest_Run_ ## test_name (void); \
|
||||
__attribute__((noinline, used)) \
|
||||
MCTEST_EXTERN_C void McTest_Register_ ## test_name (void) { \
|
||||
__asm__ __volatile__ ( \
|
||||
".pushsection .mctest_strtab,\"a\" \n" \
|
||||
"1: \n" \
|
||||
".asciz \"" _MCTEST_TO_STR(test_name) "\" \n" \
|
||||
"2: \n" \
|
||||
".asciz \"" file "\" \n" \
|
||||
".popsection \n" \
|
||||
".pushsection .mctest_entrypoints,\"a\" \n" \
|
||||
".balign 16 \n" \
|
||||
".quad %p0 \n" \
|
||||
".quad 1b \n" \
|
||||
".quad 2b \n" \
|
||||
".quad " _MCTEST_TO_STR(line) " \n" \
|
||||
".popsection \n" \
|
||||
: \
|
||||
: "i"(McTest_Run_ ## test_name) \
|
||||
); \
|
||||
static void McTest_Test_ ## test_name (void); \
|
||||
static void McTest_Run_ ## test_name (void) { \
|
||||
McTest_Test_ ## test_name(); \
|
||||
McTest_Pass(); \
|
||||
} \
|
||||
void McTest_Run_ ## test_name(void)
|
||||
MCTEST_EXTERN_C struct McTest_TestInfo McTest_Register_ ## test_name \
|
||||
__attribute__((section(".mctest_entrypoints"))) = { \
|
||||
McTest_Run_ ## test_name, \
|
||||
_MCTEST_TO_STR(test_name), \
|
||||
file, \
|
||||
line \
|
||||
}; \
|
||||
void McTest_Test_ ## test_name(void)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -117,32 +117,77 @@ inline static int IsSymbolic(void *x) {
|
||||
return McTest_IsSymbolicPtr(x);
|
||||
}
|
||||
|
||||
#if 199711L < __cplusplus
|
||||
template <typename T>
|
||||
class Symbolic {
|
||||
public:
|
||||
template <typename... Args>
|
||||
inline Symbolic(Args&& ...args)
|
||||
: value(std::forward<Args...>(args)...) {}
|
||||
|
||||
/* Returns a symbolic container (where it makes sense). Container entries
|
||||
* do not necessarily need to be contiguous. */
|
||||
template <typename ContainerT, typename... Args>
|
||||
inline static ContainerT Symbolic(Args&& ...args) {
|
||||
ContainerT cont(std::forward<Args...>(args)...);
|
||||
if (!cont.empty()) {
|
||||
for (auto &entry : cont) {
|
||||
auto entry_ptr = &entry;
|
||||
McTest_SymbolizeData(entry_ptr, &(entry_ptr[1]));
|
||||
inline Symbolic(void) {
|
||||
T *val_ptr = &value;
|
||||
McTest_SymbolizeData(val_ptr, &(val_ptr[1]));
|
||||
}
|
||||
|
||||
inline operator T (void) const {
|
||||
return value;
|
||||
}
|
||||
|
||||
T value;
|
||||
};
|
||||
|
||||
template <T>
|
||||
class SymbolicLinearContainer {
|
||||
public:
|
||||
inline explicit Symbolic(size_t len)
|
||||
: value(len) {
|
||||
if (len) {
|
||||
McTest_SymbolizeData(&(str.begin()), &(str.end()));
|
||||
}
|
||||
}
|
||||
return cont;
|
||||
}
|
||||
|
||||
#endif // 199711L < __cplusplus
|
||||
|
||||
// Returns a symbolic std::string of length `len`.
|
||||
inline static std::string Str(size_t len) {
|
||||
std::string str(len);
|
||||
if (len) {
|
||||
McTest_SymbolizeData(&(str.begin()), &(str.end()));
|
||||
inline operator T (void) const {
|
||||
return value;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
T value;
|
||||
|
||||
private:
|
||||
Symblic(void) = delete;
|
||||
};
|
||||
|
||||
template <>
|
||||
class Symbolic<std::string> : public SymbolicLinearContainer<std::string> {};
|
||||
|
||||
template <>
|
||||
class Symbolic<std::wstring> : public SymbolicLinearContainer<std::wstring> {};
|
||||
|
||||
template <typename T>
|
||||
class Symbolic<std::vector<T>> :
|
||||
public SymbolicLinearContainer<std::vector<T>> {};
|
||||
|
||||
#define MAKE_SYMBOL_SPECIALIZATION(Tname, tname) \
|
||||
template <> \
|
||||
class Symbolic<tname> { \
|
||||
public: \
|
||||
inline Symbolic(void)
|
||||
: value(McTest_ ## Tname) {} \
|
||||
inline operator tname (void) const { \
|
||||
return value; \
|
||||
} \
|
||||
tname value; \
|
||||
};
|
||||
|
||||
MAKE_SYMBOL_SPECIALIZATION(UInt64, uint64_t)
|
||||
MAKE_SYMBOL_SPECIALIZATION(Int64, int64_t)
|
||||
MAKE_SYMBOL_SPECIALIZATION(UInt, uint32_t)
|
||||
MAKE_SYMBOL_SPECIALIZATION(Int, int32_t)
|
||||
MAKE_SYMBOL_SPECIALIZATION(UShort, uint16_t)
|
||||
MAKE_SYMBOL_SPECIALIZATION(Short, int16_t)
|
||||
MAKE_SYMBOL_SPECIALIZATION(UChar, uint8_t)
|
||||
MAKE_SYMBOL_SPECIALIZATION(Char, int8_t)
|
||||
|
||||
#undef MAKE_SYMBOL_SPECIALIZATION
|
||||
|
||||
} // namespace mctest
|
||||
|
||||
|
||||
@@ -22,34 +22,76 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
static uint32_t gPlaybookIndex = 0;
|
||||
static uint32_t gPlaybook[8192] = {};
|
||||
volatile uint8_t McTest_Input[8192]
|
||||
__attribute__((section(".mctest_input_data")));
|
||||
|
||||
uint32_t McTest_InputIndex = 0;
|
||||
|
||||
__attribute__((noreturn))
|
||||
extern void McTest_Fail(void) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
__attribute__((noreturn))
|
||||
extern void McTest_Pass(void) {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void McTest_SymbolizeData(void *begin, void *end) {
|
||||
(void) begin;
|
||||
(void) end;
|
||||
uintptr_t begin_addr = (uintptr_t) begin;
|
||||
uintptr_t end_addr = (uintptr_t) end;
|
||||
|
||||
if (begin_addr > end_addr) {
|
||||
abort();
|
||||
} 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) {
|
||||
bytes[i] = McTest_Input[McTest_InputIndex++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a symbolic value of a given type. */
|
||||
int McTest_Bool(void) {
|
||||
return 0;
|
||||
return McTest_Input[McTest_InputIndex++] & 1;
|
||||
}
|
||||
|
||||
size_t McTest_Size(void) {
|
||||
return 0;
|
||||
#define MAKE_SYMBOL_FUNC(Type, type) \
|
||||
type McTest_ ## Type(void) { \
|
||||
type val = 0; \
|
||||
_Pragma("unroll") \
|
||||
for (size_t i = 0; i < sizeof(type); ++i) { \
|
||||
val = (val << 8) | ((type) McTest_Input[McTest_InputIndex++]); \
|
||||
} \
|
||||
return val; \
|
||||
}
|
||||
|
||||
MAKE_SYMBOL_FUNC(UInt64, uint64_t)
|
||||
int64_t McTest_Int64(void) {
|
||||
return (int64_t) McTest_UInt64();
|
||||
}
|
||||
|
||||
uint64_t McTest_UInt64(void) {
|
||||
return 0;
|
||||
MAKE_SYMBOL_FUNC(UInt, uint32_t)
|
||||
int32_t McTest_Int(void) {
|
||||
return (int32_t) McTest_UInt();
|
||||
}
|
||||
|
||||
uint32_t McTest_UInt(void) {
|
||||
return 0;
|
||||
MAKE_SYMBOL_FUNC(UShort, uint16_t)
|
||||
int16_t McTest_Short(void) {
|
||||
return (int16_t) McTest_UShort();
|
||||
}
|
||||
|
||||
int McTest_Assume(int expr) {
|
||||
MAKE_SYMBOL_FUNC(UChar, uint8_t)
|
||||
int8_t McTest_Char(void) {
|
||||
return (int8_t) McTest_UChar();
|
||||
}
|
||||
|
||||
#undef MAKE_SYMBOL_FUNC
|
||||
|
||||
void _McTest_Assume(int expr) {
|
||||
assert(expr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int McTest_IsSymbolicUInt(uint32_t x) {
|
||||
@@ -63,11 +105,6 @@ void McTest_DoneTestCase(void) {
|
||||
|
||||
/* McTest implements the `main` function so that test code can focus on tests */
|
||||
int main(void) {
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X86)
|
||||
asm("ud2; .asciz \"I'm McTest'in it\";");
|
||||
#else
|
||||
# error "Unsupported platform (for now)."
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user