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

@@ -13,177 +13,47 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import angr
import logging
import manticore
import sys
L = logging.getLogger("mctest")
L.setLevel(logging.INFO)
def hook_function(project, name, cls):
"""Hook the function `name` with the SimProcedure `cls`."""
project.hook(project.kb.labels.lookup(name),
cls(project=project))
class EntryPointPlugin(manticore.core.plugin.Plugin):
"""Interpose on system calls. When we come across McTest's special system
call that is invoked at the beginnning of McTest_Run, then we stop execution
there and take over."""
def on_syscall_callback(self, state, index):
if 0x41414141 == index:
print 'here!!!!!'
state_id = self._executor._workspace.save_state(state)
self._executor.put(state_id)
raise manticore.TerminateState("Canceled", testcase=False)
def read_c_string(state, ea):
"""Read a concrete NUL-terminated string from `ea`."""
assert isinstance(ea, (int, long))
chars = []
i = 0
while True:
char = state.mem[ea + i].char.resolved
char = state.solver.eval(char, cast_to=str)
if not ord(char[0]):
break
chars.append(char)
i += 1
return "".join(chars)
class McTest(manticore.Manticore):
def __init__(self, argv):
assert isinstance(argv, (list, tuple))
super(McTest, self).__init__(argv[0], argv=argv)
self._unregister_default_plugins()
self.register_plugin(EntryPointPlugin())
def read_uintptr_t(state, ea):
"""Read a uint64_t value from memory."""
return state.solver.eval(state.mem[ea].uintptr_t.resolved, cast_to=int)
def read_uint32_t(state, ea):
"""Read a uint64_t value from memory."""
return state.solver.eval(state.mem[ea].uint32_t.resolved, cast_to=int)
def find_test_cases(project, state):
"""Find the test case descriptors."""
obj = project.loader.main_object
tests = []
addr_size_bytes = state.arch.bits // 8
for sec in obj.sections:
if sec.name != ".mctest_funcs":
continue
for ea in xrange(sec.vaddr, sec.vaddr + sec.memsize, 32):
test_func_ea = read_uintptr_t(state, ea + 0 * addr_size_bytes)
test_name_ea = read_uintptr_t(state, ea + 1 * addr_size_bytes)
file_name_ea = read_uintptr_t(state, ea + 2 * addr_size_bytes)
file_line_num = read_uint32_t(state, ea + 3 * addr_size_bytes)
if not test_func_ea or \
not test_name_ea or \
not file_name_ea or \
not file_line_num: # `__LINE__` in C always starts at `1` ;-)
continue
test_name = read_c_string(state, test_name_ea)
file_name = read_c_string(state, file_name_ea)
L.info("Test case {} at {:x} is at {}:{}".format(
test_name, test_func_ea, file_name, file_line_num))
tests.append((test_func_ea, test_name, file_name, file_line_num))
return tests
def make_symbolic_input(project, state):
"""Fill in the input data array with symbolic data."""
obj = project.loader.main_object
for sec in obj.sections:
if sec.name == ".mctest_data":
data = state.se.Unconstrained('MCTEST_INPUT', sec.memsize * 8)
state.memory.store(sec.vaddr, data)
return data
def hook_predicate_int_func(project, state, name, num_bits):
"""Hook a McTest function that checks whether or not its integer argument
is symbolic."""
class Hook(angr.SimProcedure):
def run(self, arg):
return int(self.state.se.symbolic(arg))
hook_function(project, "McTest_IsSymbolic{}".format(name), Hook)
class Assume(angr.SimProcedure):
"""Implements _McTest_CanAssume, which tries to inject a constraint."""
def run(self, arg):
constraint = arg != 0
self.state.solver.add(constraint)
if not self.state.solver.satisfiable():
L.error("Failed to assert assumption {}".format(constraint))
self.exit(2)
class Pass(angr.SimProcedure):
"""Implements McTest_Pass, which notifies us of a passing test."""
def run(self):
L.info("Passed test case")
self.exit(0)
class Fail(angr.SimProcedure):
"""Implements McTest_Fail, which notifies us of a passing test."""
def run(self):
L.error("Failed test case")
self.exit(1)
def _unregister_default_plugins(self):
"""Unregister the default plugins."""
for plugin in tuple(self.plugins):
self.unregister_plugin(plugin)
def main():
"""Run McTest."""
if 2 > len(sys.argv):
return 1
project = angr.Project(
sys.argv[1],
use_sim_procedures=True,
translation_cache=True,
support_selfmodifying_code=False,
auto_load_libs=False)
entry_state = project.factory.entry_state()
addr_size_bits = entry_state.arch.bits
# Find the test cases that we want to run.
tests = find_test_cases(project, entry_state)
# Concretely execute up until `main`.
concrete_manager = angr.SimulationManager(
project=project,
active_states=[entry_state])
ea_of_main = project.kb.labels.lookup('main')
concrete_manager.explore(find=ea_of_main)
main_state = concrete_manager.found[0]
# Introduce symbolic input that the tested code will use.
symbolic_input = make_symbolic_input(project, main_state)
# Hook predicate functions that should return 1 or 0 depending on whether
# or not their argument is symbolic.
hook_predicate_int_func(project, main_state, 'UInt', 32)
hook_function(project, '_McTest_Assume', Assume)
hook_function(project, 'McTest_Pass', Pass)
hook_function(project, 'McTest_Fail', Fail)
# For each test, create a simulation manager whose initial state calls into
# the test case function, and returns to `McTest_DoneTestCase`.
test_managers = []
for entry_ea, test_name, file_name, line_num in tests:
test_state = project.factory.call_state(
entry_ea,
base_state=main_state)
# NOTE(pag): Enabling Veritesting seems to miss some cases where the
# tests fail.
test_manager = angr.SimulationManager(
project=project,
active_states=[test_state])
L.info("Running test case {}".format(test_name))
test_manager.run()
for state in test_manager.deadended:
last_event = state.history.events[-1]
if 'terminate' == last_event.type:
code = last_event.objects['exit_code']._model_concrete.value
return 0
print 'here'
McTest.verbosity(1)
m = McTest(sys.argv[1:])
print 'running...'
m.run()
if "__main__" == __name__:
exit(main())

221
bin/mctest/angr.py Normal file
View File

@@ -0,0 +1,221 @@
#!/usr/bin/env python
# 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.
from __future__ import absolute_import
import angr
import collections
import logging
import sys
L = logging.getLogger("mctest")
L.setLevel(logging.INFO)
def hook_function(project, ea, cls):
"""Hook the function `name` with the SimProcedure `cls`."""
project.hook(ea, cls(project=project))
def read_c_string(state, ea):
"""Read a concrete NUL-terminated string from `ea`."""
assert isinstance(ea, (int, long))
chars = []
i = 0
while True:
char = state.mem[ea + i].char.resolved
char = state.solver.eval(char, cast_to=str)
if not ord(char[0]):
break
chars.append(char)
i += 1
return "".join(chars)
def read_uintptr_t(state, ea):
"""Read a uint64_t value from memory."""
next_ea = ea + (state.arch.bits // 8)
val = state.solver.eval(state.mem[ea].uintptr_t.resolved, cast_to=int)
return val, next_ea
def read_uint32_t(state, ea):
"""Read a uint64_t value from memory."""
next_ea = ea + (state.arch.bits // 8)
val = state.solver.eval(state.mem[ea].uint32_t.resolved, cast_to=int)
return val, next_ea
TestInfo = collections.namedtuple(
'TestInfo', 'ea name file_name line_number')
def read_test_info(state, ea):
"""Read in a `McTest_TestInfo` info structure from memory."""
prev_test_ea, ea = read_uintptr_t(state, ea)
test_func_ea, ea = read_uintptr_t(state, ea)
test_name_ea, ea = read_uintptr_t(state, ea)
file_name_ea, ea = read_uintptr_t(state, ea)
file_line_num, _ = read_uint32_t(state, ea)
if not test_func_ea or \
not test_name_ea or \
not file_name_ea or \
not file_line_num: # `__LINE__` in C always starts at `1` ;-)
return None, prev_test_ea
test_name = read_c_string(state, test_name_ea)
file_name = read_c_string(state, file_name_ea)
info = TestInfo(test_func_ea, test_name, file_name, file_line_num)
return info, prev_test_ea
def read_api_table(state, ea):
"""Reads in the API table."""
apis = {}
while True:
api_name_ea, ea = read_uintptr_t(state, ea)
api_ea, ea = read_uintptr_t(state, ea)
if not api_name_ea or not api_ea:
break
api_name = read_c_string(state, api_name_ea)
apis[api_name] = api_ea
return apis
def find_test_cases(state, info_ea):
"""Find the test case descriptors."""
tests = []
while info_ea:
test, info_ea = read_test_info(state, info_ea)
if test:
tests.append(test)
tests.sort(key=lambda t: (t.file_name, t.line_number))
return tests
def make_symbolic_input(state, input_begin_ea, input_end_ea):
"""Fill in the input data array with symbolic data."""
input_size = input_end_ea - input_begin_ea
data = state.se.Unconstrained('MCTEST_INPUT', input_size * 8)
state.memory.store(input_begin_ea, data)
return data
class IsSymbolicUInt(angr.SimProcedure):
"""Implements McTest_IsSymblicUInt, which returns 1 if its input argument
has more then one solutions, and zero otherwise."""
def run(self, arg):
solutions = self.state.solver.eval_upto(arg, 2)
if not solutions:
return 0
elif 1 == len(solutions):
if self.state.se.symbolic(arg):
self.state.solver.add(arg == solutions[0])
return 0
else:
return 1
class Assume(angr.SimProcedure):
"""Implements _McTest_CanAssume, which tries to inject a constraint."""
def run(self, arg):
constraint = arg != 0
self.state.solver.add(constraint)
if not self.state.solver.satisfiable():
L.error("Failed to assert assumption {}".format(constraint))
self.exit(2)
class Pass(angr.SimProcedure):
"""Implements McTest_Pass, which notifies us of a passing test."""
def run(self):
L.info("Passed test case")
self.exit(0)
class Fail(angr.SimProcedure):
"""Implements McTest_Fail, which notifies us of a passing test."""
def run(self):
L.error("Failed test case")
self.exit(1)
def main():
"""Run McTest."""
if 2 > len(sys.argv):
return 1
project = angr.Project(
sys.argv[1],
use_sim_procedures=True,
translation_cache=True,
support_selfmodifying_code=False,
auto_load_libs=False)
entry_state = project.factory.entry_state()
addr_size_bits = entry_state.arch.bits
# Concretely execute up until `McTest_InjectAngr`.
concrete_manager = angr.SimulationManager(
project=project,
active_states=[entry_state])
run_ea = project.kb.labels.lookup('McTest_Run')
concrete_manager.explore(find=run_ea)
run_state = concrete_manager.found[0]
# Read the API table, which will tell us about the location of various
# symbols. Technically we can look these up with the `labels.lookup` API,
# but we have the API table for Manticore-compatibility, so we may as well
# use it.
ea_of_api_table = project.kb.labels.lookup('McTest_API')
apis = read_api_table(run_state, ea_of_api_table)
# Introduce symbolic input that the tested code will use.
symbolic_input = make_symbolic_input(
run_state, apis['InputBegin'], apis['InputEnd'])
# Hook various functions.
hook_function(project, apis['IsSymbolicUInt'], IsSymbolicUInt)
hook_function(project, apis['Assume'], Assume)
hook_function(project, apis['Pass'], Pass)
hook_function(project, apis['Fail'], Fail)
# Find the test cases that we want to run.
tests = find_test_cases(run_state, apis['LastTestInfo'])
# For each test, create a simulation manager whose initial state calls into
# the test case function.
test_managers = []
for test in tests:
test_state = project.factory.call_state(
test.ea,
base_state=run_state)
test_manager = angr.SimulationManager(
project=project,
active_states=[test_state])
L.info("Running test case {} from {}:{}".format(
test.name, test.file_name, test.line_number))
test_manager.run()
for state in test_manager.deadended:
last_event = state.history.events[-1]
if 'terminate' == last_event.type:
code = last_event.objects['exit_code']._model_concrete.value
return 0
if "__main__" == __name__:
exit(main())

View File

@@ -30,9 +30,10 @@ setuptools.setup(
author_email="peter@trailofbits.com",
license="Apache-2.0",
keywords="tdd testing symbolic execution",
install_requires=['angr'],
install_requires=['angr', 'manticore'],
entry_points={
'console_scripts': [
'mctest = mctest.__main__:main'
'mctest = mctest.__main__:main',
'mctest-angr = mctest.angr:main'
]
})

View File

@@ -18,8 +18,7 @@
using namespace mctest;
__attribute__((noinline))
int add(int x, int y) {
MCTEST_NOINLINE int add(int x, int y) {
return x + y;
}
@@ -28,3 +27,7 @@ McTest_EntryPoint(AdditionIsCommutative) {
McTest_Assert(add(x, y) == add(y, x));
});
}
int main(int argc, char *argv[]) {
return McTest_Run();
}

View File

@@ -25,5 +25,9 @@ McTest_EntryPoint(YIsAlwaysPositive) {
McTest_EntryPoint(YIsAlwaysPositive_CanFail) {
int x = McTest_IntInRange(-10, 10);
int y = x * x * x;
McTest_Assert(y >= 0); /* This will fail */
McTest_Assert(y >= 0); /* This can fail */
}
int main(int argc, char *argv[]) {
return McTest_Run();
}

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