Made it so that tests can be run on their own, independent of a symbolic executor. This will open up libFuzzer support, and concrete execution of solved-for test case inputs. Removed all stuff related to sections. Made tests get registered via initializers. Working on exposing the API functions to be hooked by Manticore via a special system call with addres 0x41414141. Split the Angr version out into the mctest-angr binary, and going to try to make the mctest binary use Manticore.

This commit is contained in:
Peter Goodman
2017-10-28 19:13:59 -04:00
parent 0d336bd4d6
commit 89da3e8e94
9 changed files with 525 additions and 258 deletions

View File

@@ -0,0 +1,96 @@
/*
* Copyright (c) 2017 Trail of Bits, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDE_MCTEST_COMPILER_H_
#define INCLUDE_MCTEST_COMPILER_H_
#include <stdio.h>
#include <stdlib.h>
/* Stringify a macro parameter. */
#define MCTEST_TO_STR(a) _MCTEST_TO_STR(a)
#define _MCTEST_TO_STR(a) __MCTEST_TO_STR(a)
#define __MCTEST_TO_STR(a) #a
/* Mark a function as not returning. */
#if defined(_MSC_VER)
# define MCTEST_NORETURN __declspec(noreturn)
#else
# define MCTEST_NORETURN __attribute__((noreturn))
#endif
/* Mark a function for inlining. */
#if defined(_MSC_VER)
# define MCTEST_INLINE __forceinline
# define MCTEST_NOINLINE __declspec(noinline)
#else
# define MCTEST_INLINE inline __attribute__((always_inline))
# define MCTEST_NOINLINE __attribute__((noinline))
#endif
/* Introduce a trap instruction to halt execution. */
#if defined(_MSC_VER)
# include <intrin.h>
# define McTest_Trap __debugbreak
#else
# define McTest_Trap __builtin_trap
#endif
/* Wrap a block of code in `extern "C"` if we are compiling with a C++
* compiler. */
#ifdef __cplusplus
# define MCTEST_BEGIN_EXTERN_C extern "C" {
# define MCTEST_END_EXTERN_C }
#else
# define MCTEST_BEGIN_EXTERN_C
# define MCTEST_END_EXTERN_C
#endif
/* Initializer/finalizer sample for MSVC and GCC/Clang.
* 2010-2016 Joe Lowe. Released into the public domain.
*
* See: https://stackoverflow.com/a/2390626/247591 */
#ifdef __cplusplus
# define MCTEST_INITIALIZER(f) \
static void f(void); \
struct f ##_t_ { \
f##_t_(void) { \
f(); \
} \
}; \
static f##_t_ f##_; \
static void f(void)
#elif defined(_MSC_VER)
# pragma section(".CRT$XCU",read)
# define MCTEST_INITIALIZER2_(f, p) \
static void f(void); \
__declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
__pragma(comment(linker,"/include:" p #f "_")) \
static void f(void)
# ifdef _WIN64
# define MCTEST_INITIALIZER(f) MCTEST_INITIALIZER2_(f,"")
# else
# define MCTEST_INITIALIZER(f) MCTEST_INITIALIZER2_(f,"_")
# endif
#else
# define MCTEST_INITIALIZER(f) \
static void f(void) __attribute__((constructor)); \
static void f(void)
#endif
#endif /* INCLUDE_MCTEST_COMPILER_H_ */

View File

@@ -22,14 +22,14 @@
#include <stdint.h>
#include <stdlib.h>
#include <mctest/Compiler.h>
#ifdef assert
# undef assert
#endif
#define assert McTest_Assert
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
MCTEST_BEGIN_EXTERN_C
/* Return a symbolic value of a given type. */
extern int McTest_Bool(void);
@@ -46,7 +46,7 @@ extern int8_t McTest_Char(void);
/* Symbolize the data in the range `[begin, end)`. */
extern void McTest_SymbolizeData(void *begin, void *end);
inline static void *McTest_Malloc(size_t num_bytes) {
MCTEST_INLINE static void *McTest_Malloc(size_t num_bytes) {
void *data = malloc(num_bytes);
uintptr_t data_end = ((uintptr_t) data) + num_bytes;
McTest_SymbolizeData(data, (void *) data_end);
@@ -54,7 +54,8 @@ inline static void *McTest_Malloc(size_t num_bytes) {
}
#define MCTEST_MAKE_SYMBOLIC_ARRAY(Tname, tname) \
inline static tname *McTest_Symbolic ## Tname ## Array(size_t num_elms) { \
MCTEST_INLINE static \
tname *McTest_Symbolic ## Tname ## Array(size_t num_elms) { \
tname *arr = (tname *) malloc(sizeof(tname) * num_elms); \
McTest_SymbolizeData(arr, &(arr[num_elms])); \
return arr; \
@@ -72,7 +73,7 @@ MCTEST_MAKE_SYMBOLIC_ARRAY(UChar, unsigned char)
#undef MCTEST_MAKE_SYMBOLIC_ARRAY
/* Return a symbolic C string. */
inline static char *McTest_CStr(size_t len) {
MCTEST_INLINE static char *McTest_CStr(size_t len) {
char *str = (char *) malloc(sizeof(char) * len);
if (len) {
McTest_SymbolizeData(str, &(str[len - 1]));
@@ -87,14 +88,14 @@ extern void _McTest_Assume(int expr);
#define McTest_Assume(x) _McTest_Assume(!!(x))
__attribute__((noreturn))
MCTEST_NORETURN
extern void McTest_Fail(void);
__attribute__((noreturn))
MCTEST_NORETURN
extern void McTest_Pass(void);
/* Asserts that `expr` must hold. */
inline static void McTest_Assert(int expr) {
MCTEST_INLINE static void McTest_Assert(int expr) {
if (!expr) {
McTest_Fail();
}
@@ -102,14 +103,13 @@ inline static void McTest_Assert(int expr) {
/* 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( \
MCTEST_INLINE static tname McTest_ ## Tname ## InRange( \
tname low, tname high) { \
tname x = McTest_ ## Tname(); \
(void) McTest_Assume(low <= x && x <= high); \
return x; \
}
MCTEST_MAKE_SYMBOLIC_RANGE(Size, size_t)
MCTEST_MAKE_SYMBOLIC_RANGE(Int64, int64_t)
MCTEST_MAKE_SYMBOLIC_RANGE(UInt64, uint64_t)
@@ -125,88 +125,96 @@ MCTEST_MAKE_SYMBOLIC_RANGE(UChar, unsigned char)
/* Predicates to check whether or not a particular value is symbolic */
extern int McTest_IsSymbolicUInt(uint32_t x);
inline static int McTest_IsSymbolicInt(int x) {
/* The following predicates are implemented in terms of `McTest_IsSymbolicUInt`.
* This simplifies the portability of hooking this predicate interface across
* architectures, because basically all hooking mechanisms know how to get at
* the first integer argument. Passing in floating point values, or 64-bit
* integers on 32-bit architectures, can be more subtle. */
MCTEST_INLINE static int McTest_IsSymbolicInt(int x) {
return McTest_IsSymbolicUInt((uint32_t) x);
}
inline static int McTest_IsSymbolicUShort(uint16_t x) {
MCTEST_INLINE static int McTest_IsSymbolicUShort(uint16_t x) {
return McTest_IsSymbolicUInt((uint32_t) x);
}
inline static int McTest_IsSymbolicShort(int16_t x) {
MCTEST_INLINE static int McTest_IsSymbolicShort(int16_t x) {
return McTest_IsSymbolicUInt((uint32_t) (uint16_t) x);
}
inline static int McTest_IsSymbolicUChar(unsigned char x) {
MCTEST_INLINE static int McTest_IsSymbolicUChar(unsigned char x) {
return McTest_IsSymbolicUInt((uint32_t) x);
}
inline static int McTest_IsSymbolicChar(char x) {
MCTEST_INLINE static int McTest_IsSymbolicChar(char x) {
return McTest_IsSymbolicUInt((uint32_t) (unsigned char) x);
}
inline static int McTest_IsSymbolicUInt64(uint64_t x) {
MCTEST_INLINE static int McTest_IsSymbolicUInt64(uint64_t x) {
return McTest_IsSymbolicUInt((uint32_t) x) ||
McTest_IsSymbolicUInt((uint32_t) (x >> 32U));
}
inline static int McTest_IsSymbolicInt64(int64_t x) {
MCTEST_INLINE static int McTest_IsSymbolicInt64(int64_t x) {
return McTest_IsSymbolicUInt64((uint64_t) x);
}
inline static int McTest_IsSymbolicBool(int x) {
MCTEST_INLINE static int McTest_IsSymbolicBool(int x) {
return McTest_IsSymbolicInt(x);
}
inline static int McTest_IsSymbolicFloat(float x) {
MCTEST_INLINE static int McTest_IsSymbolicFloat(float x) {
return McTest_IsSymbolicUInt(*((uint32_t *) &x));
}
inline static int McTest_IsSymbolicDouble(double x) {
MCTEST_INLINE static int McTest_IsSymbolicDouble(double x) {
return McTest_IsSymbolicUInt64(*((uint64_t *) &x));
}
#define _MCTEST_TO_STR(a) __MCTEST_TO_STR(a)
#define __MCTEST_TO_STR(a) #a
#ifdef __cplusplus
# define MCTEST_BEGIN_EXTERN_C extern "C" {
# define MCTEST_END_EXTERN_C }
#else
# define MCTEST_BEGIN_EXTERN_C
# define MCTEST_END_EXTERN_C
#endif
/* Used to define the entrypoint of a test case. */
#define McTest_EntryPoint(test_name) \
_McTest_EntryPoint(test_name, __FILE__, __LINE__)
struct __attribute__((packed)) McTest_TestInfo {
/* Contains information about a test case */
struct McTest_TestInfo {
struct McTest_TestInfo *prev;
void (*test_func)(void);
const char *test_name;
const char *file_name;
unsigned line_number;
uint8_t padding[28 - (3 * sizeof(void *))];
};
/* Pointer to the last registered `TestInfo` structure. */
extern struct McTest_TestInfo *McTest_LastTestInfo;
/* Defines the entrypoint of a test case. This creates a data structure that
* contains the information about the test, and then creates an initializer
* function that runs before `main` that registers the test entrypoint with
* McTest. */
#define _McTest_EntryPoint(test_name, file, line) \
static void McTest_Test_ ## test_name (void); \
static void McTest_Run_ ## test_name (void) { \
McTest_Test_ ## test_name(); \
McTest_Pass(); \
} \
MCTEST_BEGIN_EXTERN_C \
struct McTest_TestInfo McTest_Register_ ## test_name \
__attribute__((section(".mctest_funcs"))) = { \
static struct McTest_TestInfo McTest_Info_ ## test_name = { \
NULL, \
McTest_Run_ ## test_name, \
_MCTEST_TO_STR(test_name), \
MCTEST_TO_STR(test_name), \
file, \
line \
line, \
}; \
MCTEST_END_EXTERN_C \
MCTEST_INITIALIZER(McTest_Register_ ## test_name) { \
McTest_Info_ ## test_name.prev = McTest_LastTestInfo; \
McTest_LastTestInfo = &(McTest_Info_ ## test_name); \
} \
void McTest_Test_ ## test_name(void)
#ifdef __cplusplus
} /* extern C */
#endif /* __cplusplus */
/* Start McTest and run the tests. Returns the number of failed tests. */
extern int McTest_Run(void);
MCTEST_END_EXTERN_C
#endif /* INCLUDE_MCTEST_MCTEST_H_ */

View File

@@ -25,95 +25,95 @@
namespace mctest {
inline static void *Malloc(size_t num_bytes) {
MCTEST_INLINE static void *Malloc(size_t num_bytes) {
return McTest_Malloc(num_bytes);
}
inline static void SymbolizeData(void *begin, void *end) {
MCTEST_INLINE static void SymbolizeData(void *begin, void *end) {
McTest_SymbolizeData(begin, end);
}
inline static bool Bool(void) {
MCTEST_INLINE static bool Bool(void) {
return static_cast<bool>(McTest_Bool());
}
inline static size_t Size(void) {
MCTEST_INLINE static size_t Size(void) {
return McTest_Size();
}
inline static uint64_t UInt64(void) {
MCTEST_INLINE static uint64_t UInt64(void) {
return McTest_UInt64();
}
inline static int64_t Int64(void) {
MCTEST_INLINE static int64_t Int64(void) {
return McTest_Int64();
}
inline static uint32_t UInt(void) {
MCTEST_INLINE static uint32_t UInt(void) {
return McTest_UInt();
}
inline static int32_t Int(void) {
MCTEST_INLINE static int32_t Int(void) {
return McTest_Int();
}
inline static uint16_t UShort(void) {
MCTEST_INLINE static uint16_t UShort(void) {
return McTest_UShort();
}
inline static int16_t Short(void) {
MCTEST_INLINE static int16_t Short(void) {
return McTest_Short();
}
inline static unsigned char UChar(void) {
MCTEST_INLINE static unsigned char UChar(void) {
return McTest_UChar();
}
inline static char Char(void) {
MCTEST_INLINE static char Char(void) {
return McTest_Char();
}
inline static bool IsSymbolic(uint64_t x) {
MCTEST_INLINE static bool IsSymbolic(uint64_t x) {
return McTest_IsSymbolicUInt64(x);
}
inline static int IsSymbolic(int64_t x) {
MCTEST_INLINE static int IsSymbolic(int64_t x) {
return McTest_IsSymbolicInt64(x);
}
inline static bool IsSymbolic(uint32_t x) {
MCTEST_INLINE static bool IsSymbolic(uint32_t x) {
return McTest_IsSymbolicUInt(x);
}
inline static bool IsSymbolic(int32_t x) {
MCTEST_INLINE static bool IsSymbolic(int32_t x) {
return McTest_IsSymbolicInt(x);
}
inline static int IsSymbolic(uint16_t x) {
MCTEST_INLINE static int IsSymbolic(uint16_t x) {
return McTest_IsSymbolicUShort(x);
}
inline static bool IsSymbolic(int16_t x) {
MCTEST_INLINE static bool IsSymbolic(int16_t x) {
return McTest_IsSymbolicShort(x);
}
inline static bool IsSymbolic(unsigned char x) {
MCTEST_INLINE static bool IsSymbolic(unsigned char x) {
return McTest_IsSymbolicUChar(x);
}
inline static bool IsSymbolic(char x) {
MCTEST_INLINE static bool IsSymbolic(char x) {
return McTest_IsSymbolicChar(x);
}
inline static bool IsSymbolic(float x) {
MCTEST_INLINE static bool IsSymbolic(float x) {
return McTest_IsSymbolicFloat(x);
}
inline static bool IsSymbolic(double x) {
MCTEST_INLINE static bool IsSymbolic(double x) {
return McTest_IsSymbolicDouble(x);
}
inline static bool IsSymbolic(void *x) {
MCTEST_INLINE static bool IsSymbolic(void *x) {
return IsSymbolic((uintptr_t) x);
}
@@ -121,15 +121,15 @@ template <typename T>
class Symbolic {
public:
template <typename... Args>
inline Symbolic(Args&& ...args)
MCTEST_INLINE Symbolic(Args&& ...args)
: value(std::forward<Args...>(args)...) {}
inline Symbolic(void) {
MCTEST_INLINE Symbolic(void) {
T *val_ptr = &value;
McTest_SymbolizeData(val_ptr, &(val_ptr[1]));
}
inline operator T (void) const {
MCTEST_INLINE operator T (void) const {
return value;
}
@@ -139,17 +139,17 @@ class Symbolic {
template <typename T>
class SymbolicLinearContainer {
public:
inline explicit SymbolicLinearContainer(size_t len)
MCTEST_INLINE explicit SymbolicLinearContainer(size_t len)
: value(len) {
if (len) {
McTest_SymbolizeData(&(value.begin()), &(value.end()));
}
}
inline SymbolicLinearContainer(void)
MCTEST_INLINE SymbolicLinearContainer(void)
: SymbolicLinearContainer(McTest_SizeInRange(0, 32)) {}
inline operator T (void) const {
MCTEST_INLINE operator T (void) const {
return value;
}
@@ -174,9 +174,9 @@ class Symbolic<std::vector<T>> :
template <> \
class Symbolic<tname> { \
public: \
inline Symbolic(void) \
MCTEST_INLINE Symbolic(void) \
: value(McTest_ ## Tname()) {} \
inline operator tname (void) const { \
MCTEST_INLINE operator tname (void) const { \
return value; \
} \
tname value; \

View File

@@ -17,24 +17,47 @@
#include <mctest/McTest.h>
#include <assert.h>
#include <setjmp.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#if defined(unix) || defined(__unix) || defined(__unix__)
# define _GNU_SOURCE
# include <unistd.h> /* For `syscall` */
#endif
volatile uint8_t McTest_Input[8192]
__attribute__((section(".mctest_data")));
MCTEST_BEGIN_EXTERN_C
uint32_t McTest_InputIndex = 0;
/* Pointer to the last registers McTest_TestInfo data structure */
struct McTest_TestInfo *McTest_LastTestInfo = NULL;
__attribute__((noreturn))
enum {
McTest_InputLength = 8192
};
/* Byte buffer that will contain symbolic data that is used to supply requests
* for symbolic values (e.g. `int`s). */
static volatile uint8_t McTest_Input[McTest_InputLength];
/* Index into the `McTest_Input` array that tracks how many input bytes have
* been consumed. */
static uint32_t McTest_InputIndex = 0;
/* Jump buffer for returning to `McTest_Main`. */
static jmp_buf McTest_ReturnToMain;
static int McTest_TestPassed = 0;
/* Mark this test as failing. */
MCTEST_NORETURN
extern void McTest_Fail(void) {
exit(EXIT_FAILURE);
McTest_TestPassed = 0;
longjmp(McTest_ReturnToMain, 1);
}
__attribute__((noreturn))
/* Mark this test as passing. */
MCTEST_NORETURN
extern void McTest_Pass(void) {
exit(EXIT_SUCCESS);
McTest_TestPassed = 1;
longjmp(McTest_ReturnToMain, 0);
}
void McTest_SymbolizeData(void *begin, void *end) {
@@ -99,15 +122,56 @@ int McTest_IsSymbolicUInt(uint32_t x) {
return 0;
}
void McTest_DoneTestCase(void) {
exit(EXIT_SUCCESS);
/* A McTest-specific symbol that is needed for hooking. */
struct McTest_IndexEntry {
const char * const name;
void * const address;
};
/* An index of symbols that the symbolic executors will hook or
* need access to. */
const struct McTest_IndexEntry McTest_API[] = {
{"Pass", (void *) McTest_Pass},
{"Fail", (void *) McTest_Fail},
{"Assume", (void *) _McTest_Assume},
{"IsSymbolicUInt", (void *) McTest_IsSymbolicUInt},
{"InputBegin", (void *) &(McTest_Input[0])},
{"InputEnd", (void *) &(McTest_Input[McTest_InputLength])},
{"InputIndex", (void *) &McTest_InputIndex},
{"LastTestInfo", (void *) &McTest_LastTestInfo},
{NULL, NULL},
};
int McTest_Run(void) {
/* Manticore entrypoint. Manticore doesn't (yet?) support symbol lookups, so
* we instead interpose on this fake system call, and discover the API table
* via the first argument to the system call. */
#if defined(_MSC_VER)
# warning "TODO: Implement Windows interception support for Manticore."
#else
syscall(0x41414141, &McTest_API);
#endif
int num_failed_tests = 0;
for (struct McTest_TestInfo *info = McTest_LastTestInfo;
info != NULL;
info = info->prev) {
McTest_TestPassed = 0;
if (!setjmp(McTest_ReturnToMain)) {
printf("Running %s from %s:%u\n", info->test_name, info->file_name,
info->line_number);
info->test_func();
} else if (McTest_TestPassed) {
printf(" %s Passed\n", info->test_name);
} else {
printf(" %s Failed\n", info->test_name);
num_failed_tests += 1;
}
}
return num_failed_tests;
}
/* McTest implements the `main` function so that test code can focus on tests */
int main(void) {
return EXIT_SUCCESS;
}
#ifdef __cplusplus
} /* extern C */
#endif /* __cplusplus */
MCTEST_END_EXTERN_C