Renaming from McTest to DeepState.

This commit is contained in:
Peter Goodman 2017-11-01 13:38:32 -04:00
parent f7f029965b
commit d2bc82fc35
27 changed files with 1325 additions and 1337 deletions

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
project(mctest)
project(deepstate)
cmake_minimum_required(VERSION 2.8)
enable_language(C)
@ -39,7 +39,7 @@ if (NOT WIN32)
endif ()
add_library(${PROJECT_NAME} STATIC
src/lib/McTest.c
src/lib/DeepState.c
src/lib/Log.c
src/lib/Stream.c
)
@ -50,7 +50,7 @@ target_include_directories(${PROJECT_NAME}
# Install the
install(
DIRECTORY "${CMAKE_SOURCE_DIR}/src/include/mctest"
DIRECTORY "${CMAKE_SOURCE_DIR}/src/include/deepstate"
DESTINATION include
)
@ -75,7 +75,7 @@ add_custom_command(
# Install the Manticore harness.
add_custom_target(target ALL DEPENDS ${OUTPUT})
# Install McTest via PIP.
# Install DeepState via PIP.
install(CODE "execute_process(COMMAND ${PYTHON} ${SETUP_PY} install)")
add_subdirectory(examples)

View File

@ -28,7 +28,7 @@ LOG_LEVEL_ERROR = 3
LOG_LEVEL_FATAL = 4
LOGGER = logging.getLogger("mctest")
LOGGER = logging.getLogger("deepstate")
LOGGER.setLevel(logging.DEBUG)
@ -46,8 +46,8 @@ class Stream(object):
self.entries = entries
class McTest(object):
"""Wrapper around a symbolic executor for making it easy to do common McTest-
class DeepState(object):
"""Wrapper around a symbolic executor for making it easy to do common DeepState-
specific things."""
def __init__(self):
pass
@ -120,7 +120,7 @@ class McTest(object):
return chars, next_ea
def read_test_info(self, ea):
"""Read in a `McTest_TestInfo` info structure from memory."""
"""Read in a `DeepState_TestInfo` info structure from memory."""
prev_test_ea, ea = self.read_uintptr_t(ea)
test_func_ea, ea = self.read_uintptr_t(ea)
test_name_ea, ea = self.read_uintptr_t(ea)
@ -249,7 +249,7 @@ class McTest(object):
self.context['abandoned'] = True
def api_is_symbolic_uint(self, arg):
"""Implements the `McTest_IsSymbolicUInt` API, which returns whether or
"""Implements the `DeepState_IsSymbolicUInt` API, which returns whether or
not a given value is symbolic."""
solutions = self.concretize_many(arg, 2)
if not solutions:
@ -262,7 +262,7 @@ class McTest(object):
return 1
def api_assume(self, arg):
"""Implements the `McTest_Assume` API function, which injects a constraint
"""Implements the `DeepState_Assume` API function, which injects a constraint
into the solver."""
constraint = arg != 0
if not self.add_constraint(constraint):
@ -271,7 +271,7 @@ class McTest(object):
self.abandon_test()
def api_pass(self):
"""Implements the `McTest_Pass` API function, which marks this test as
"""Implements the `DeepState_Pass` API function, which marks this test as
having passed, and stops further execution."""
if self.context['failed']:
self.api_fail()
@ -281,7 +281,7 @@ class McTest(object):
self.pass_test()
def api_fail(self):
"""Implements the `McTest_Fail` API function, which marks this test as
"""Implements the `DeepState_Fail` API function, which marks this test as
having failed, and stops further execution."""
self.context['failed'] = True
info = self.context['info']
@ -289,12 +289,12 @@ class McTest(object):
self.fail_test()
def api_soft_fail(self):
"""Implements the `McTest_SoftFail` API function, which marks this test
"""Implements the `DeepState_SoftFail` API function, which marks this test
as having failed, but lets execution continue."""
self.context['failed'] = True
def api_abandon(self, arg):
"""Implements the `McTest_Abandon` API function, which marks this test
"""Implements the `DeepState_Abandon` API function, which marks this test
as having aborted due to some unrecoverable error."""
info = self.context['info']
ea = self.concretize(arg, constrain=True)
@ -303,7 +303,7 @@ class McTest(object):
self.abandon_test()
def api_log(self, level, ea):
"""Implements the `McTest_Log` API function, which prints a C string
"""Implements the `DeepState_Log` API function, which prints a C string
to a specific log level."""
self.api_log_stream(level)
@ -339,19 +339,19 @@ class McTest(object):
self.context[stream_id] = stream
def api_stream_int(self, level, format_ea, unpack_ea, uint64_ea):
"""Implements the `_McTest_StreamInt`, which streams an integer into a
"""Implements the `_DeepState_StreamInt`, which streams an integer into a
holding buffer for the log."""
return self._api_stream_int_float(level, format_ea, unpack_ea,
uint64_ea, int)
def api_stream_float(self, level, format_ea, unpack_ea, double_ea):
"""Implements the `_McTest_StreamFloat`, which streams an integer into a
"""Implements the `_DeepState_StreamFloat`, which streams an integer into a
holding buffer for the log."""
return self._api_stream_int_float(level, format_ea, unpack_ea,
double_ea, float)
def api_stream_string(self, level, format_ea, str_ea):
"""Implements the `_McTest_StreamString`, which streams a C-string into a
"""Implements the `_DeepState_StreamString`, which streams a C-string into a
holding buffer for the log."""
level = self.concretize(level, constrain=True)
assert level in LOG_LEVEL_TO_LOGGER

View File

@ -20,13 +20,13 @@ import logging
import multiprocessing
import sys
import traceback
from .common import McTest
from .common import DeepState
L = logging.getLogger("mctest.angr")
L = logging.getLogger("deepstate.angr")
L.setLevel(logging.INFO)
class AngrTest(McTest):
class AngrTest(DeepState):
def __init__(self, state=None, procedure=None):
super(AngrTest, self).__init__()
if procedure:
@ -131,57 +131,57 @@ def hook_function(project, ea, cls):
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)
data = state.se.Unconstrained('DEEPSTATE_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
"""Implements DeepState_IsSymblicUInt, which returns 1 if its input argument
has more then one solutions, and zero otherwise."""
def run(self, arg):
return AngrTest(procedure=self).api_is_symbolic_uint(arg)
class Assume(angr.SimProcedure):
"""Implements _McTest_Assume, which tries to inject a constraint."""
"""Implements _DeepState_Assume, which tries to inject a constraint."""
def run(self, arg):
AngrTest(procedure=self).api_assume(arg)
class Pass(angr.SimProcedure):
"""Implements McTest_Pass, which notifies us of a passing test."""
"""Implements DeepState_Pass, which notifies us of a passing test."""
def run(self):
AngrTest(procedure=self).api_pass()
class Fail(angr.SimProcedure):
"""Implements McTest_Fail, which notifies us of a failing test."""
"""Implements DeepState_Fail, which notifies us of a failing test."""
def run(self):
AngrTest(procedure=self).api_fail()
class Abandon(angr.SimProcedure):
"""Implements McTest_Fail, which notifies us of a failing test."""
"""Implements DeepState_Fail, which notifies us of a failing test."""
def run(self, reason):
AngrTest(procedure=self).api_abandon(reason)
class SoftFail(angr.SimProcedure):
"""Implements McTest_SoftFail, which notifies us of a failing test."""
"""Implements DeepState_SoftFail, which notifies us of a failing test."""
def run(self):
AngrTest(procedure=self).api_soft_fail()
class StreamInt(angr.SimProcedure):
"""Implements _McTest_StreamInt, which gives us an integer to stream, and
"""Implements _DeepState_StreamInt, which gives us an integer to stream, and
the format to use for streaming."""
def run(self, level, format_ea, unpack_ea, uint64_ea):
AngrTest(procedure=self).api_stream_int(level, format_ea, unpack_ea,
uint64_ea)
class StreamFloat(angr.SimProcedure):
"""Implements _McTest_StreamFloat, which gives us an double to stream, and
"""Implements _DeepState_StreamFloat, which gives us an double to stream, and
the format to use for streaming."""
def run(self, level, format_ea, unpack_ea, double_ea):
AngrTest(procedure=self).api_stream_float(level, format_ea, unpack_ea,
@ -189,21 +189,21 @@ class StreamFloat(angr.SimProcedure):
class StreamString(angr.SimProcedure):
"""Implements _McTest_StreamString, which gives us an double to stream, and
"""Implements _DeepState_StreamString, which gives us an double to stream, and
the format to use for streaming."""
def run(self, level, format_ea, str_ea):
AngrTest(procedure=self).api_stream_string(level, format_ea, str_ea)
class LogStream(angr.SimProcedure):
"""Implements McTest_LogStream, which converts the contents of a stream for
"""Implements DeepState_LogStream, which converts the contents of a stream for
level `level` into a log for level `level`."""
def run(self, level):
AngrTest(procedure=self).api_log_stream(level)
class Log(angr.SimProcedure):
"""Implements McTest_Log, which lets Angr intercept and handle the
"""Implements DeepState_Log, which lets Angr intercept and handle the
printing of log messages from the simulated tests."""
def run(self, level, ea):
AngrTest(procedure=self).api_log(level, ea)
@ -250,7 +250,7 @@ def run_test(project, test, apis, run_state):
def main():
"""Run McTest."""
"""Run DeepState."""
parser = argparse.ArgumentParser(
description="Symbolically execute unit tests with Angr")
@ -277,11 +277,11 @@ def main():
addr_size_bits = entry_state.arch.bits
# Concretely execute up until `McTest_InjectAngr`.
# Concretely execute up until `DeepState_InjectAngr`.
concrete_manager = angr.SimulationManager(
project=project,
active_states=[entry_state])
setup_ea = project.kb.labels.lookup('McTest_Setup')
setup_ea = project.kb.labels.lookup('DeepState_Setup')
concrete_manager.explore(find=setup_ea)
run_state = concrete_manager.found[0]
@ -289,7 +289,7 @@ def main():
# 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')
ea_of_api_table = project.kb.labels.lookup('DeepState_API')
mc = AngrTest(state=run_state)
apis = mc.read_api_table(ea_of_api_table)

View File

@ -20,18 +20,18 @@ import manticore
import multiprocessing
import sys
import traceback
from .common import McTest
from .common import DeepState
from manticore.core.state import TerminateState
from manticore.utils.helpers import issymbolic
L = logging.getLogger("mctest.mcore")
L = logging.getLogger("deepstate.mcore")
L.setLevel(logging.INFO)
OUR_TERMINATION_REASON = "I McTest'd it"
OUR_TERMINATION_REASON = "I DeepState'd it"
class MCoreTest(McTest):
class MCoreTest(DeepState):
def __init__(self, state):
super(MCoreTest, self).__init__()
self.state = state
@ -128,7 +128,7 @@ def make_symbolic_input(state, input_begin_ea, input_end_ea):
input_size = input_end_ea - input_begin_ea
data = []
for i in xrange(input_end_ea - input_begin_ea):
input_byte = state.new_symbolic_value(8, "MCTEST_INPUT_{}".format(i))
input_byte = state.new_symbolic_value(8, "DEEPSTATE_INPUT_{}".format(i))
data.append(input_byte)
state.cpu.write_int(input_begin_ea + i, input_byte, 8)
@ -136,63 +136,63 @@ def make_symbolic_input(state, input_begin_ea, input_end_ea):
def hook_IsSymbolicUInt(state, arg):
"""Implements McTest_IsSymblicUInt, which returns 1 if its input argument
"""Implements DeepState_IsSymblicUInt, which returns 1 if its input argument
has more then one solutions, and zero otherwise."""
return MCoreTest(state).api_is_symbolic_uint(arg)
def hook_Assume(state, arg):
"""Implements _McTest_Assume, which tries to inject a constraint."""
"""Implements _DeepState_Assume, which tries to inject a constraint."""
MCoreTest(state).api_assume(arg)
def hook_StreamInt(state, level, format_ea, unpack_ea, uint64_ea):
"""Implements _McTest_StreamInt, which gives us an integer to stream, and
"""Implements _DeepState_StreamInt, which gives us an integer to stream, and
the format to use for streaming."""
MCoreTest(state).api_stream_int(level, format_ea, unpack_ea, uint64_ea)
def hook_StreamFloat(state, level, format_ea, unpack_ea, double_ea):
"""Implements _McTest_StreamFloat, which gives us an double to stream, and
"""Implements _DeepState_StreamFloat, which gives us an double to stream, and
the format to use for streaming."""
MCoreTest(state).api_stream_float(level, format_ea, unpack_ea, double_ea)
def hook_StreamString(state, level, format_ea, str_ea):
"""Implements _McTest_StreamString, which gives us an double to stream, and
"""Implements _DeepState_StreamString, which gives us an double to stream, and
the format to use for streaming."""
MCoreTest(state).api_stream_string(level, format_ea, str_ea)
def hook_LogStream(state, level):
"""Implements McTest_LogStream, which converts the contents of a stream for
"""Implements DeepState_LogStream, which converts the contents of a stream for
level `level` into a log for level `level`."""
MCoreTest(state).api_log_stream(level)
def hook_Pass(state):
"""Implements McTest_Pass, which notifies us of a passing test."""
"""Implements DeepState_Pass, which notifies us of a passing test."""
MCoreTest(state).api_pass()
def hook_Fail(state):
"""Implements McTest_Fail, which notifies us of a passing test."""
"""Implements DeepState_Fail, which notifies us of a passing test."""
MCoreTest(state).api_fail()
def hook_Abandon(state, reason):
"""Implements McTest_Abandon, which notifies us that a problem happened
in McTest."""
"""Implements DeepState_Abandon, which notifies us that a problem happened
in DeepState."""
MCoreTest(state).api_abandon(reason)
def hook_SoftFail(state):
"""Implements McTest_Fail, which notifies us of a passing test."""
"""Implements DeepState_Fail, which notifies us of a passing test."""
MCoreTest(state).api_soft_fail()
def hook_Log(state, level, ea):
"""Implements McTest_Log, which lets Manticore intercept and handle the
"""Implements DeepState_Log, which lets Manticore intercept and handle the
printing of log messages from the simulated tests."""
MCoreTest(state).api_log(level, ea)
@ -288,12 +288,12 @@ def main():
m._binary_type = 'not elf'
m._binary_obj = m._initial_state.platform.elf
setup_ea = m._get_symbol_address('McTest_Setup')
setup_ea = m._get_symbol_address('DeepState_Setup')
setup_state = m._initial_state
mc = MCoreTest(setup_state)
ea_of_api_table = m._get_symbol_address('McTest_API')
ea_of_api_table = m._get_symbol_address('DeepState_API')
apis = mc.read_api_table(ea_of_api_table)
del mc
m.add_hook(setup_ea, lambda state: run_tests(args, state, apis))

View File

@ -17,15 +17,15 @@ import distutils.core
import os
import setuptools
MCTEST_DIR = os.path.dirname(os.path.realpath(__file__))
DEEPSTATE_DIR = os.path.dirname(os.path.realpath(__file__))
setuptools.setup(
name="mctest",
name="deepstate",
version="0.1",
package_dir={"": "${CMAKE_SOURCE_DIR}/bin"},
packages=['mctest'],
description="McTest augments C/C++ Test-Driven Development with Symbolic Execution",
url="https://github.com/trailofbits/mctest",
packages=['deepstate'],
description="DeepState augments C/C++ Test-Driven Development with Symbolic Execution",
url="https://github.com/trailofbits/deepstate",
author="Peter Goodman",
author_email="peter@trailofbits.com",
license="Apache-2.0",
@ -33,7 +33,7 @@ setuptools.setup(
install_requires=['angr', 'manticore'],
entry_points={
'console_scripts': [
'mctest = mctest.__main__:main',
'mctest-angr = mctest.main_angr:main'
'deepstate = deepstate.main_manticore:main',
'deepstate-angr = deepstate.main_angr:main'
]
})

View File

@ -12,14 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
add_executable(OutOfBoundsInt OutOfBoundsInt.cpp)
target_link_libraries(OutOfBoundsInt mctest)
add_executable(IntegerOverflow IntegerOverflow.cpp)
target_link_libraries(IntegerOverflow deepstate)
add_executable(ArithmeticProperties ArithmeticProperties.cpp)
target_link_libraries(ArithmeticProperties mctest)
add_executable(IntegerArtihmetic IntegerArithmetic.cpp)
target_link_libraries(IntegerArtihmetic deepstate)
add_executable(Lists Lists.cpp)
target_link_libraries(Lists mctest)
target_link_libraries(Lists deepstate)
add_executable(StreamingAndFormatting StreamingAndFormatting.cpp)
target_link_libraries(StreamingAndFormatting mctest)
target_link_libraries(StreamingAndFormatting deepstate)

View File

@ -14,12 +14,11 @@
* limitations under the License.
*/
#include <mctest/McUnit.hpp>
#include <mctest/Quantified.hpp>
#include <deepstate/DeepState.hpp>
using namespace mctest;
using namespace deepstate;
MCTEST_NOINLINE int add(int x, int y) {
DEEPSTATE_NOINLINE int add(int x, int y) {
return x + y;
}
@ -47,5 +46,5 @@ TEST(Arithmetic, InvertibleMultiplication_CanFail) {
}
int main(void) {
return McTest_Run();
return DeepState_Run();
}

View File

@ -14,30 +14,32 @@
* limitations under the License.
*/
#include <mctest/McUnit.hpp>
#include <deepstate/DeepState.hpp>
MCTEST_NOINLINE int ident1(int x) {
using namespace deepstate;
DEEPSTATE_NOINLINE int ident1(int x) {
return x;
}
MCTEST_NOINLINE int ident2(int x) {
DEEPSTATE_NOINLINE int ident2(int x) {
return x;
}
TEST(BoundsCheck, YIsAlwaysPositive) {
int x = McTest_IntInRange(-10, 10);
int y = ident1(x) * ident2(x);
TEST(SignedInteger, AdditionOverflow) {
Symbolic<int> x;
int y = ident1(x) + ident2(x); // Can overflow!
ASSERT_GE(y, 0)
<< "Found y=" << y << " was not always positive.";
}
TEST(BoundsCheck, YIsAlwaysPositive_CanFail) {
int x = McTest_Int();
TEST(SignedInteger, MultiplicationOverflow) {
Symbolic<int> x;
int y = ident1(x) * ident2(x); // Can overflow!
ASSERT_GE(y, 0)
<< "Found y=" << y << " was not always positive.";
<< x << " squared overflowed.";
}
int main(int argc, char *argv[]) {
return McTest_Run();
return DeepState_Run();
}

View File

@ -14,13 +14,12 @@
* limitations under the License.
*/
#include <mctest/McUnit.hpp>
#include <mctest/Quantified.hpp>
#include <deepstate/DeepState.hpp>
#include <algorithm>
#include <vector>
using namespace mctest;
using namespace deepstate;
TEST(Vector, DoubleReversal) {
ForAll<std::vector<int>>([] (const std::vector<int> &vec1) {
@ -33,5 +32,5 @@ TEST(Vector, DoubleReversal) {
}
int main(void) {
McTest_Run();
DeepState_Run();
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
#include <mctest/McUnit.hpp>
#include <deepstate/DeepState.hpp>
TEST(Streaming, BasicLevels) {
LOG(DEBUG) << "This is a debug message";
@ -40,5 +40,5 @@ TEST(Formatting, OverridePrintf) {
}
int main(void) {
return McTest_Run();
return DeepState_Run();
}

View File

@ -14,49 +14,49 @@
* limitations under the License.
*/
#ifndef SRC_INCLUDE_MCTEST_COMPILER_H_
#define SRC_INCLUDE_MCTEST_COMPILER_H_
#ifndef SRC_INCLUDE_DEEPSTATE_COMPILER_H_
#define SRC_INCLUDE_DEEPSTATE_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
#define DEEPSTATE_TO_STR(a) _DEEPSTATE_TO_STR(a)
#define _DEEPSTATE_TO_STR(a) __DEEPSTATE_TO_STR(a)
#define __DEEPSTATE_TO_STR(a) #a
/* Mark a function as not returning. */
#if defined(_MSC_VER)
# define MCTEST_NORETURN __declspec(noreturn)
# define DEEPSTATE_NORETURN __declspec(noreturn)
#else
# define MCTEST_NORETURN __attribute__((noreturn))
# define DEEPSTATE_NORETURN __attribute__((noreturn))
#endif
/* Mark a function for inlining. */
#if defined(_MSC_VER)
# define MCTEST_INLINE __forceinline
# define MCTEST_NOINLINE __declspec(noinline)
# define DEEPSTATE_INLINE __forceinline
# define DEEPSTATE_NOINLINE __declspec(noinline)
#else
# define MCTEST_INLINE inline __attribute__((always_inline))
# define MCTEST_NOINLINE __attribute__((noinline))
# define DEEPSTATE_INLINE inline __attribute__((always_inline))
# define DEEPSTATE_NOINLINE __attribute__((noinline))
#endif
/* Introduce a trap instruction to halt execution. */
#if defined(_MSC_VER)
# include <intrin.h>
# define McTest_Trap __debugbreak
# define DeepState_Trap __debugbreak
#else
# define McTest_Trap __builtin_trap
# define DeepState_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 }
# define DEEPSTATE_BEGIN_EXTERN_C extern "C" {
# define DEEPSTATE_END_EXTERN_C }
#else
# define MCTEST_BEGIN_EXTERN_C
# define MCTEST_END_EXTERN_C
# define DEEPSTATE_BEGIN_EXTERN_C
# define DEEPSTATE_END_EXTERN_C
#endif
/* Initializer/finalizer sample for MSVC and GCC/Clang.
@ -64,7 +64,7 @@
*
* See: https://stackoverflow.com/a/2390626/247591 */
#ifdef __cplusplus
# define MCTEST_INITIALIZER(f) \
# define DEEPSTATE_INITIALIZER(f) \
static void f(void); \
struct f ##_t_ { \
f##_t_(void) { \
@ -76,21 +76,21 @@
#elif defined(_MSC_VER)
# pragma section(".CRT$XCU",read)
# define MCTEST_INITIALIZER2_(f, p) \
# define DEEPSTATE_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,"")
# define DEEPSTATE_INITIALIZER(f) DEEPSTATE_INITIALIZER2_(f,"")
# else
# define MCTEST_INITIALIZER(f) MCTEST_INITIALIZER2_(f,"_")
# define DEEPSTATE_INITIALIZER(f) DEEPSTATE_INITIALIZER2_(f,"_")
# endif
#else
# define MCTEST_INITIALIZER(f) \
# define DEEPSTATE_INITIALIZER(f) \
static void f(void) __attribute__((constructor)); \
static void f(void)
#endif
#endif /* SRC_INCLUDE_MCTEST_COMPILER_H_ */
#endif /* SRC_INCLUDE_DEEPSTATE_COMPILER_H_ */

View File

@ -0,0 +1,312 @@
/*
* 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 SRC_INCLUDE_DEEPSTATE_DEEPSTATE_H_
#define SRC_INCLUDE_DEEPSTATE_DEEPSTATE_H_
#include <assert.h>
#include <setjmp.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <deepstate/Log.h>
#include <deepstate/Compiler.h>
#include <deepstate/Stream.h>
#ifdef assert
# undef assert
#endif
#define assert DeepState_Assert
#define assume DeepState_Assume
#define check DeepState_Check
DEEPSTATE_BEGIN_EXTERN_C
/* Return a symbolic value of a given type. */
extern int DeepState_Bool(void);
extern size_t DeepState_Size(void);
extern uint64_t DeepState_UInt64(void);
extern int64_t DeepState_Int64(void);
extern uint32_t DeepState_UInt(void);
extern int32_t DeepState_Int(void);
extern uint16_t DeepState_UShort(void);
extern int16_t DeepState_Short(void);
extern uint8_t DeepState_UChar(void);
extern int8_t DeepState_Char(void);
/* 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. */
extern int DeepState_IsTrue(int expr);
/* Symbolize the data in the exclusive range `[begin, end)`. */
extern void DeepState_SymbolizeData(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);
/* Concretize a C string. Returns a pointer to the beginning of the
* concretized C string. */
extern const char *DeepState_ConcretizeCStr(const char *begin);
/* Allocate and return a pointer to `num_bytes` symbolic bytes. */
extern void *DeepState_Malloc(size_t num_bytes);
#define DEEPSTATE_MAKE_SYMBOLIC_ARRAY(Tname, tname) \
DEEPSTATE_INLINE static \
tname *DeepState_Symbolic ## Tname ## Array(size_t num_elms) { \
tname *arr = (tname *) malloc(sizeof(tname) * num_elms); \
DeepState_SymbolizeData(arr, &(arr[num_elms])); \
return arr; \
}
DEEPSTATE_MAKE_SYMBOLIC_ARRAY(Int64, int64_t)
DEEPSTATE_MAKE_SYMBOLIC_ARRAY(UInt64, uint64_t)
DEEPSTATE_MAKE_SYMBOLIC_ARRAY(Int, int)
DEEPSTATE_MAKE_SYMBOLIC_ARRAY(UInt, uint32_t)
DEEPSTATE_MAKE_SYMBOLIC_ARRAY(Short, int16_t)
DEEPSTATE_MAKE_SYMBOLIC_ARRAY(UShort, uint16_t)
DEEPSTATE_MAKE_SYMBOLIC_ARRAY(Char, char)
DEEPSTATE_MAKE_SYMBOLIC_ARRAY(UChar, unsigned char)
#undef DEEPSTATE_MAKE_SYMBOLIC_ARRAY
/* Creates an assumption about a symbolic value. Returns `1` if the assumption
* can hold and was asserted. */
extern void _DeepState_Assume(int expr);
#define DeepState_Assume(x) _DeepState_Assume(!!(x))
/* Abandon this test. We've hit some kind of internal problem. */
DEEPSTATE_NORETURN
extern void DeepState_Abandon(const char *reason);
DEEPSTATE_NORETURN
extern void DeepState_Fail(void);
/* Mark this test as failing, but don't hard exit. */
extern void DeepState_SoftFail(void);
DEEPSTATE_NORETURN
extern void DeepState_Pass(void);
/* Asserts that `expr` must hold. If it does not, then the test fails and
* immediately stops. */
DEEPSTATE_INLINE static void DeepState_Assert(int expr) {
if (!expr) {
DeepState_Fail();
}
}
/* Asserts that `expr` must hold. If it does not, then the test fails, but
* nonetheless continues on. */
DEEPSTATE_INLINE static void DeepState_Check(int expr) {
if (!expr) {
DeepState_SoftFail();
}
}
/* Return a symbolic value in a the range `[low_inc, high_inc]`. */
#define DEEPSTATE_MAKE_SYMBOLIC_RANGE(Tname, tname) \
DEEPSTATE_INLINE static tname DeepState_ ## Tname ## InRange( \
tname low, tname high) { \
tname x = DeepState_ ## Tname(); \
(void) DeepState_Assume(low <= x && x <= high); \
return x; \
}
DEEPSTATE_MAKE_SYMBOLIC_RANGE(Size, size_t)
DEEPSTATE_MAKE_SYMBOLIC_RANGE(Int64, int64_t)
DEEPSTATE_MAKE_SYMBOLIC_RANGE(UInt64, uint64_t)
DEEPSTATE_MAKE_SYMBOLIC_RANGE(Int, int)
DEEPSTATE_MAKE_SYMBOLIC_RANGE(UInt, uint32_t)
DEEPSTATE_MAKE_SYMBOLIC_RANGE(Short, int16_t)
DEEPSTATE_MAKE_SYMBOLIC_RANGE(UShort, uint16_t)
DEEPSTATE_MAKE_SYMBOLIC_RANGE(Char, char)
DEEPSTATE_MAKE_SYMBOLIC_RANGE(UChar, unsigned char)
#undef DEEPSTATE_MAKE_SYMBOLIC_RANGE
/* Predicates to check whether or not a particular value is symbolic */
extern int DeepState_IsSymbolicUInt(uint32_t x);
/* The following predicates are implemented in terms of `DeepState_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. */
DEEPSTATE_INLINE static int DeepState_IsSymbolicInt(int x) {
return DeepState_IsSymbolicUInt((uint32_t) x);
}
DEEPSTATE_INLINE static int DeepState_IsSymbolicUShort(uint16_t x) {
return DeepState_IsSymbolicUInt((uint32_t) x);
}
DEEPSTATE_INLINE static int DeepState_IsSymbolicShort(int16_t x) {
return DeepState_IsSymbolicUInt((uint32_t) (uint16_t) x);
}
DEEPSTATE_INLINE static int DeepState_IsSymbolicUChar(unsigned char x) {
return DeepState_IsSymbolicUInt((uint32_t) x);
}
DEEPSTATE_INLINE static int DeepState_IsSymbolicChar(char x) {
return DeepState_IsSymbolicUInt((uint32_t) (unsigned char) x);
}
DEEPSTATE_INLINE static int DeepState_IsSymbolicUInt64(uint64_t x) {
return DeepState_IsSymbolicUInt((uint32_t) x) ||
DeepState_IsSymbolicUInt((uint32_t) (x >> 32U));
}
DEEPSTATE_INLINE static int DeepState_IsSymbolicInt64(int64_t x) {
return DeepState_IsSymbolicUInt64((uint64_t) x);
}
DEEPSTATE_INLINE static int DeepState_IsSymbolicBool(int x) {
return DeepState_IsSymbolicInt(x);
}
DEEPSTATE_INLINE static int DeepState_IsSymbolicFloat(float x) {
return DeepState_IsSymbolicUInt(*((uint32_t *) &x));
}
DEEPSTATE_INLINE static int DeepState_IsSymbolicDouble(double x) {
return DeepState_IsSymbolicUInt64(*((uint64_t *) &x));
}
/* Used to define the entrypoint of a test case. */
#define DeepState_EntryPoint(test_name) \
_DeepState_EntryPoint(test_name, __FILE__, __LINE__)
/* Contains information about a test case */
struct DeepState_TestInfo {
struct DeepState_TestInfo *prev;
void (*test_func)(void);
const char *test_name;
const char *file_name;
unsigned line_number;
};
/* Pointer to the last registered `TestInfo` structure. */
extern struct DeepState_TestInfo *DeepState_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
* DeepState. */
#define _DeepState_EntryPoint(test_name, file, line) \
static void DeepState_Test_ ## test_name (void); \
static void DeepState_Run_ ## test_name (void) { \
DeepState_Test_ ## test_name(); \
DeepState_Pass(); \
} \
static struct DeepState_TestInfo DeepState_Info_ ## test_name = { \
NULL, \
DeepState_Run_ ## test_name, \
DEEPSTATE_TO_STR(test_name), \
file, \
line, \
}; \
DEEPSTATE_INITIALIZER(DeepState_Register_ ## test_name) { \
DeepState_Info_ ## test_name.prev = DeepState_LastTestInfo; \
DeepState_LastTestInfo = &(DeepState_Info_ ## test_name); \
} \
void DeepState_Test_ ## test_name(void)
/* Set up DeepState. */
extern void DeepState_Setup(void);
/* Tear down DeepState. */
extern void DeepState_Teardown(void);
/* Notify that we're about to begin a test. */
extern void DeepState_Begin(struct DeepState_TestInfo *info);
/* Return the first test case to run. */
extern struct DeepState_TestInfo *DeepState_FirstTest(void);
/* Returns 1 if a failure was caught, otherwise 0. */
extern int DeepState_CatchFail(void);
/* Returns 1 if this test case was abandoned. */
extern int DeepState_CatchAbandoned(void);
/* Jump buffer for returning to `DeepState_Run`. */
extern jmp_buf DeepState_ReturnToRun;
/* Start DeepState and run the tests. Returns the number of failed tests. */
static int DeepState_Run(void) {
int num_failed_tests = 0;
struct DeepState_TestInfo *test = NULL;
DeepState_Setup();
for (test = DeepState_FirstTest(); test != NULL; test = test->prev) {
DeepState_Begin(test);
/* Run the test. */
if (!setjmp(DeepState_ReturnToRun)) {
/* Convert uncaught C++ exceptions into a test failure. */
#if defined(__cplusplus) && defined(__cpp_exceptions)
try {
#endif /* __cplusplus */
test->test_func(); /* Run the test function. */
DeepState_Pass();
#if defined(__cplusplus) && defined(__cpp_exceptions)
} catch(...) {
DeepState_Fail();
}
#endif /* __cplusplus */
/* We caught a failure when running the test. */
} else if (DeepState_CatchFail()) {
++num_failed_tests;
DeepState_LogFormat(DeepState_LogError, "Failed: %s", test->test_name);
/* The test was abandoned. We may have gotten soft failures before
* abandoning, so we prefer to catch those first. */
} else if (DeepState_CatchAbandoned()) {
DeepState_LogFormat(DeepState_LogFatal, "Abandoned: %s", test->test_name);
/* The test passed. */
} else {
DeepState_LogFormat(DeepState_LogInfo, "Passed: %s", test->test_name);
}
}
DeepState_Teardown();
return num_failed_tests;
}
DEEPSTATE_END_EXTERN_C
#endif /* SRC_INCLUDE_DEEPSTATE_DEEPSTATE_H_ */

View File

@ -0,0 +1,280 @@
/*
* 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 SRC_INCLUDE_DEEPSTATE_DEEPSTATE_HPP_
#define SRC_INCLUDE_DEEPSTATE_DEEPSTATE_HPP_
#include <deepstate/DeepState.h>
#include <deepstate/Stream.hpp>
#include <string>
#include <utility>
#include <vector>
namespace deepstate {
DEEPSTATE_INLINE static void *Malloc(size_t num_bytes) {
return DeepState_Malloc(num_bytes);
}
DEEPSTATE_INLINE static void SymbolizeData(void *begin, void *end) {
DeepState_SymbolizeData(begin, end);
}
DEEPSTATE_INLINE static bool Bool(void) {
return static_cast<bool>(DeepState_Bool());
}
DEEPSTATE_INLINE static size_t Size(void) {
return DeepState_Size();
}
DEEPSTATE_INLINE static uint64_t UInt64(void) {
return DeepState_UInt64();
}
DEEPSTATE_INLINE static int64_t Int64(void) {
return DeepState_Int64();
}
DEEPSTATE_INLINE static uint32_t UInt(void) {
return DeepState_UInt();
}
DEEPSTATE_INLINE static int32_t Int(void) {
return DeepState_Int();
}
DEEPSTATE_INLINE static uint16_t UShort(void) {
return DeepState_UShort();
}
DEEPSTATE_INLINE static int16_t Short(void) {
return DeepState_Short();
}
DEEPSTATE_INLINE static unsigned char UChar(void) {
return DeepState_UChar();
}
DEEPSTATE_INLINE static char Char(void) {
return DeepState_Char();
}
DEEPSTATE_INLINE static bool IsSymbolic(uint64_t x) {
return DeepState_IsSymbolicUInt64(x);
}
DEEPSTATE_INLINE static int IsSymbolic(int64_t x) {
return DeepState_IsSymbolicInt64(x);
}
DEEPSTATE_INLINE static bool IsSymbolic(uint32_t x) {
return DeepState_IsSymbolicUInt(x);
}
DEEPSTATE_INLINE static bool IsSymbolic(int32_t x) {
return DeepState_IsSymbolicInt(x);
}
DEEPSTATE_INLINE static int IsSymbolic(uint16_t x) {
return DeepState_IsSymbolicUShort(x);
}
DEEPSTATE_INLINE static bool IsSymbolic(int16_t x) {
return DeepState_IsSymbolicShort(x);
}
DEEPSTATE_INLINE static bool IsSymbolic(unsigned char x) {
return DeepState_IsSymbolicUChar(x);
}
DEEPSTATE_INLINE static bool IsSymbolic(char x) {
return DeepState_IsSymbolicChar(x);
}
DEEPSTATE_INLINE static bool IsSymbolic(float x) {
return DeepState_IsSymbolicFloat(x);
}
DEEPSTATE_INLINE static bool IsSymbolic(double x) {
return DeepState_IsSymbolicDouble(x);
}
template <typename T>
class Symbolic {
public:
template <typename... Args>
DEEPSTATE_INLINE Symbolic(Args&& ...args)
: value(std::forward<Args...>(args)...) {}
DEEPSTATE_INLINE Symbolic(void) {
T *val_ptr = &value;
DeepState_SymbolizeData(val_ptr, &(val_ptr[1]));
}
DEEPSTATE_INLINE operator T (void) const {
return value;
}
T value;
};
template <typename T>
class SymbolicLinearContainer {
public:
DEEPSTATE_INLINE explicit SymbolicLinearContainer(size_t len)
: value(len) {
if (!value.empty()) {
DeepState_SymbolizeData(&(value.front()), &(value.back()));
}
}
DEEPSTATE_INLINE SymbolicLinearContainer(void)
: SymbolicLinearContainer(DeepState_SizeInRange(0, 32)) {}
DEEPSTATE_INLINE operator T (void) const {
return value;
}
T value;
};
template <>
class Symbolic<std::string> : public SymbolicLinearContainer<std::string> {
using SymbolicLinearContainer::SymbolicLinearContainer;
};
template <>
class Symbolic<std::wstring> : public SymbolicLinearContainer<std::wstring> {
using SymbolicLinearContainer::SymbolicLinearContainer;
};
template <typename T>
class Symbolic<std::vector<T>> :
public SymbolicLinearContainer<std::vector<T>> {};
#define MAKE_SYMBOL_SPECIALIZATION(Tname, tname) \
template <> \
class Symbolic<tname> { \
public: \
DEEPSTATE_INLINE Symbolic(void) \
: value(DeepState_ ## Tname()) {} \
DEEPSTATE_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
template <typename... Args>
inline static void ForAll(void (*func)(Args...)) {
func(Symbolic<Args>()...);
}
template <typename... Args, typename Closure>
inline static void ForAll(Closure func) {
func(Symbolic<Args>()...);
}
} // namespace deepstate
#define TEST(category, name) \
DeepState_EntryPoint(category ## _ ## name)
#define LOG_DEBUG(cond) \
::deepstate::Stream(DeepState_LogDebug, (cond), __FILE__, __LINE__)
#define LOG_INFO(cond) \
::deepstate::Stream(DeepState_LogInfo, (cond), __FILE__, __LINE__)
#define LOG_WARNING(cond) \
::deepstate::Stream(DeepState_LogWarning, (cond), __FILE__, __LINE__)
#define LOG_WARN(cond) \
::deepstate::Stream(DeepState_LogWarning, (cond), __FILE__, __LINE__)
#define LOG_ERROR(cond) \
::deepstate::Stream(DeepState_LogError, (cond), __FILE__, __LINE__)
#define LOG_FATAL(cond) \
::deepstate::Stream(DeepState_LogFatal, (cond), __FILE__, __LINE__)
#define LOG_CRITICAl(cond) \
::deepstate::Stream(DeepState_LogFatal, (cond), __FILE__, __LINE__)
#define LOG(LEVEL) LOG_ ## LEVEL(true)
#define LOG_IF(LEVEL, cond) LOG_ ## LEVEL(cond)
#define DEEPSTATE_LOG_BINOP(a, b, op, level) \
::deepstate::Stream( \
level, !((a) op (b)), __FILE__, __LINE__)
#define ASSERT_EQ(a, b) DEEPSTATE_LOG_BINOP(a, b, ==, DeepState_LogFatal)
#define ASSERT_NE(a, b) DEEPSTATE_LOG_BINOP(a, b, !=, DeepState_LogFatal)
#define ASSERT_LT(a, b) DEEPSTATE_LOG_BINOP(a, b, <, DeepState_LogFatal)
#define ASSERT_LE(a, b) DEEPSTATE_LOG_BINOP(a, b, <=, DeepState_LogFatal)
#define ASSERT_GT(a, b) DEEPSTATE_LOG_BINOP(a, b, >, DeepState_LogFatal)
#define ASSERT_GE(a, b) DEEPSTATE_LOG_BINOP(a, b, >=, DeepState_LogFatal)
#define CHECK_EQ(a, b) DEEPSTATE_LOG_BINOP(a, b, ==, DeepState_LogError)
#define CHECK_NE(a, b) DEEPSTATE_LOG_BINOP(a, b, !=, DeepState_LogError)
#define CHECK_LT(a, b) DEEPSTATE_LOG_BINOP(a, b, <, DeepState_LogError)
#define CHECK_LE(a, b) DEEPSTATE_LOG_BINOP(a, b, <=, DeepState_LogError)
#define CHECK_GT(a, b) DEEPSTATE_LOG_BINOP(a, b, >, DeepState_LogError)
#define CHECK_GE(a, b) DEEPSTATE_LOG_BINOP(a, b, >=, DeepState_LogError)
#define ASSERT(expr) \
::deepstate::Stream( \
DeepState_LogFatal, !(expr), __FILE__, __LINE__)
#define ASSERT_TRUE ASSERT
#define ASSERT_FALSE(expr) ASSERT(!(expr))
#define CHECK(expr) \
::deepstate::Stream( \
DeepState_LogError, !(expr), __FILE__, __LINE__)
#define CHECK_TRUE CHECK
#define CHECK_FALSE(expr) CHECK(!(expr))
#define ASSUME(expr) \
DeepState_Assume(expr), ::deepstate::Stream( \
DeepState_LogInfo, true, __FILE__, __LINE__)
#define DEEPSTATE_ASSUME_BINOP(a, b, op) \
DeepState_Assume(((a) op (b))), ::deepstate::Stream( \
DeepState_LogInfo, true, __FILE__, __LINE__)
#define ASSUME_EQ(a, b) DEEPSTATE_ASSUME_BINOP(a, b, ==)
#define ASSUME_NE(a, b) DEEPSTATE_ASSUME_BINOP(a, b, !=)
#define ASSUME_LT(a, b) DEEPSTATE_ASSUME_BINOP(a, b, <)
#define ASSUME_LE(a, b) DEEPSTATE_ASSUME_BINOP(a, b, <=)
#define ASSUME_GT(a, b) DEEPSTATE_ASSUME_BINOP(a, b, >)
#define ASSUME_GE(a, b) DEEPSTATE_ASSUME_BINOP(a, b, >=)
#endif // SRC_INCLUDE_DEEPSTATE_DEEPSTATE_HPP_

View File

@ -14,38 +14,38 @@
* limitations under the License.
*/
#ifndef SRC_INCLUDE_MCTEST_LOG_H_
#define SRC_INCLUDE_MCTEST_LOG_H_
#ifndef SRC_INCLUDE_DEEPSTATE_LOG_H_
#define SRC_INCLUDE_DEEPSTATE_LOG_H_
#include <stdarg.h>
#include <mctest/Compiler.h>
#include <deepstate/Compiler.h>
MCTEST_BEGIN_EXTERN_C
DEEPSTATE_BEGIN_EXTERN_C
struct McTest_Stream;
struct DeepState_Stream;
enum McTest_LogLevel {
McTest_LogDebug = 0,
McTest_LogInfo = 1,
McTest_LogWarning = 2,
McTest_LogWarn = McTest_LogWarning,
McTest_LogError = 3,
McTest_LogFatal = 4,
McTest_LogCritical = McTest_LogFatal
enum DeepState_LogLevel {
DeepState_LogDebug = 0,
DeepState_LogInfo = 1,
DeepState_LogWarning = 2,
DeepState_LogWarn = DeepState_LogWarning,
DeepState_LogError = 3,
DeepState_LogFatal = 4,
DeepState_LogCritical = DeepState_LogFatal
};
/* Log a C string. */
extern void McTest_Log(enum McTest_LogLevel level, const char *str);
extern void DeepState_Log(enum DeepState_LogLevel level, const char *str);
/* Log some formatted output. */
extern void McTest_LogFormat(enum McTest_LogLevel level,
extern void DeepState_LogFormat(enum DeepState_LogLevel level,
const char *format, ...);
/* Log some formatted output. */
extern void McTest_LogVFormat(enum McTest_LogLevel level,
extern void DeepState_LogVFormat(enum DeepState_LogLevel level,
const char *format, va_list args);
MCTEST_END_EXTERN_C
DEEPSTATE_END_EXTERN_C
#endif /* SRC_INCLUDE_MCTEST_LOG_H_ */
#endif /* SRC_INCLUDE_DEEPSTATE_LOG_H_ */

View File

@ -0,0 +1,79 @@
/*
* 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 SRC_INCLUDE_DEEPSTATE_STREAM_H_
#define SRC_INCLUDE_DEEPSTATE_STREAM_H_
#include <stdarg.h>
#include <stdint.h>
#include <deepstate/Compiler.h>
#include <deepstate/Log.h>
DEEPSTATE_BEGIN_EXTERN_C
/* Flush the contents of the stream to a log. */
extern void DeepState_LogStream(enum DeepState_LogLevel level);
/* Stream a C string into the stream's message. */
extern void DeepState_StreamCStr(enum DeepState_LogLevel level, const char *begin);
/* TODO(pag): Implement `DeepState_StreamWCStr` with `wchar_t`. */
/* Stream a some data in the inclusive range `[begin, end]` into the
* stream's message. */
/*extern void DeepState_StreamData(enum DeepState_LogLevel level, const void *begin,
const void *end);*/
/* Stream some formatted input */
extern void DeepState_StreamFormat(enum DeepState_LogLevel level, const char *format,
...);
/* Stream some formatted input */
extern void DeepState_StreamVFormat(enum DeepState_LogLevel level, const char *format,
va_list args);
#define DEEPSTATE_DECLARE_STREAMER(Type, type) \
extern void DeepState_Stream ## Type(enum DeepState_LogLevel level, type val);
DEEPSTATE_DECLARE_STREAMER(Double, double);
DEEPSTATE_DECLARE_STREAMER(Pointer, void *);
DEEPSTATE_DECLARE_STREAMER(UInt64, uint64_t)
DEEPSTATE_DECLARE_STREAMER(Int64, int64_t)
DEEPSTATE_DECLARE_STREAMER(UInt32, uint32_t)
DEEPSTATE_DECLARE_STREAMER(Int32, int32_t)
DEEPSTATE_DECLARE_STREAMER(UInt16, uint16_t)
DEEPSTATE_DECLARE_STREAMER(Int16, int16_t)
DEEPSTATE_DECLARE_STREAMER(UInt8, uint8_t)
DEEPSTATE_DECLARE_STREAMER(Int8, int8_t)
#undef DEEPSTATE_DECLARE_STREAMER
DEEPSTATE_INLINE static void DeepState_StreamFloat(enum DeepState_LogLevel level,
float val) {
DeepState_StreamDouble(level, (double) val);
}
/* Reset the formatting in a stream. */
extern void DeepState_StreamResetFormatting(enum DeepState_LogLevel level);
DEEPSTATE_END_EXTERN_C
#endif /* SRC_INCLUDE_DEEPSTATE_STREAM_H_ */

View File

@ -0,0 +1,102 @@
/*
* 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 SRC_INCLUDE_DEEPSTATE_STREAM_HPP_
#define SRC_INCLUDE_DEEPSTATE_STREAM_HPP_
#include <deepstate/DeepState.h>
#include <deepstate/Stream.h>
#include <cstddef>
#include <string>
namespace deepstate {
/* Conditionally stream output to a log using the streaming APIs. */
class Stream {
public:
DEEPSTATE_INLINE Stream(DeepState_LogLevel level_, bool do_log_,
const char *file, unsigned line)
: level(level_),
do_log(DeepState_IsTrue(do_log_)) {
DeepState_LogStream(level);
if (do_log) {
DeepState_StreamFormat(level, "%s(%u): ", file, line);
}
}
DEEPSTATE_INLINE ~Stream(void) {
if (do_log) {
DeepState_LogStream(level);
}
}
#define DEEPSTATE_DEFINE_STREAMER(Type, type, expr) \
DEEPSTATE_INLINE const Stream &operator<<(type val) const { \
if (do_log) { \
DeepState_Stream ## Type(level, expr); \
} \
return *this; \
}
DEEPSTATE_DEFINE_STREAMER(UInt64, uint64_t, val)
DEEPSTATE_DEFINE_STREAMER(Int64, int64_t, val)
DEEPSTATE_DEFINE_STREAMER(UInt32, uint32_t, val)
DEEPSTATE_DEFINE_STREAMER(Int32, int32_t, val)
DEEPSTATE_DEFINE_STREAMER(UInt16, uint16_t, val)
DEEPSTATE_DEFINE_STREAMER(Int16, int16_t, val)
DEEPSTATE_DEFINE_STREAMER(UInt8, uint8_t, val)
DEEPSTATE_DEFINE_STREAMER(Int8, int8_t, val)
DEEPSTATE_DEFINE_STREAMER(Float, float, val)
DEEPSTATE_DEFINE_STREAMER(Double, double, val)
DEEPSTATE_DEFINE_STREAMER(CStr, const char *, val)
DEEPSTATE_DEFINE_STREAMER(CStr, char *, const_cast<const char *>(val))
DEEPSTATE_DEFINE_STREAMER(Pointer, nullptr_t, nullptr)
template <typename T>
DEEPSTATE_DEFINE_STREAMER(Pointer, T *, val);
template <typename T>
DEEPSTATE_DEFINE_STREAMER(Pointer, const T *, const_cast<T *>(val));
#undef DEEPSTATE_DEFINE_INT_STREAMER
DEEPSTATE_INLINE const Stream &operator<<(const std::string &str) const {
if (do_log && !str.empty()) {
DeepState_StreamCStr(level, str.c_str());
}
return *this;
}
// TODO(pag): Implement a `std::wstring` streamer.
private:
Stream(void) = delete;
Stream(const Stream &) = delete;
Stream &operator=(const Stream &) = delete;
const DeepState_LogLevel level;
const int do_log;
};
} // namespace deepstate
#endif // SRC_INCLUDE_DEEPSTATE_STREAM_HPP_

View File

@ -1,318 +0,0 @@
/*
* 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 SRC_INCLUDE_MCTEST_MCTEST_H_
#define SRC_INCLUDE_MCTEST_MCTEST_H_
#include <assert.h>
#include <setjmp.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mctest/Log.h>
#include <mctest/Compiler.h>
#include <mctest/Stream.h>
#ifdef assert
# undef assert
#endif
#define assert McTest_Assert
MCTEST_BEGIN_EXTERN_C
/* 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);
/* 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. */
extern int McTest_IsTrue(int expr);
/* Symbolize the data in the range `[begin, end)`. */
extern void McTest_SymbolizeData(void *begin, void *end);
/* Concretize some data i nthe range `[begin, end)`. */
extern void McTest_ConcretizeData(void *begin, void *end);
/* Concretize a C string */
extern void McTest_ConcretizeCStr(const char *begin);
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);
return data;
}
#define MCTEST_MAKE_SYMBOLIC_ARRAY(Tname, tname) \
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; \
}
MCTEST_MAKE_SYMBOLIC_ARRAY(Int64, int64_t)
MCTEST_MAKE_SYMBOLIC_ARRAY(UInt64, uint64_t)
MCTEST_MAKE_SYMBOLIC_ARRAY(Int, int)
MCTEST_MAKE_SYMBOLIC_ARRAY(UInt, uint32_t)
MCTEST_MAKE_SYMBOLIC_ARRAY(Short, int16_t)
MCTEST_MAKE_SYMBOLIC_ARRAY(UShort, uint16_t)
MCTEST_MAKE_SYMBOLIC_ARRAY(Char, char)
MCTEST_MAKE_SYMBOLIC_ARRAY(UChar, unsigned char)
#undef MCTEST_MAKE_SYMBOLIC_ARRAY
/* Return a symbolic C string. */
MCTEST_INLINE static char *McTest_CStr(size_t len) {
char *str = (char *) malloc(sizeof(char) * len);
if (len) {
McTest_SymbolizeData(str, &(str[len - 1]));
str[len - 1] = '\0';
}
return str;
}
/* Creates an assumption about a symbolic value. Returns `1` if the assumption
* can hold and was asserted. */
extern void _McTest_Assume(int expr);
#define McTest_Assume(x) _McTest_Assume(!!(x))
/* Abandon this test. We've hit some kind of internal problem. */
MCTEST_NORETURN
extern void McTest_Abandon(const char *reason);
MCTEST_NORETURN
extern void McTest_Fail(void);
/* Mark this test as failing, but don't hard exit. */
extern void McTest_SoftFail(void);
MCTEST_NORETURN
extern void McTest_Pass(void);
/* Asserts that `expr` must hold. If it does not, then the test fails and
* immediately stops. */
MCTEST_INLINE static void McTest_Assert(int expr) {
if (!expr) {
McTest_Fail();
}
}
/* Asserts that `expr` must hold. If it does not, then the test fails, but
* nonetheless continues on. */
MCTEST_INLINE static void McTest_Check(int expr) {
if (!expr) {
McTest_SoftFail();
}
}
/* Return a symbolic value in a the range `[low_inc, high_inc]`. */
#define MCTEST_MAKE_SYMBOLIC_RANGE(Tname, tname) \
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)
MCTEST_MAKE_SYMBOLIC_RANGE(Int, int)
MCTEST_MAKE_SYMBOLIC_RANGE(UInt, uint32_t)
MCTEST_MAKE_SYMBOLIC_RANGE(Short, int16_t)
MCTEST_MAKE_SYMBOLIC_RANGE(UShort, uint16_t)
MCTEST_MAKE_SYMBOLIC_RANGE(Char, char)
MCTEST_MAKE_SYMBOLIC_RANGE(UChar, unsigned char)
#undef MCTEST_MAKE_SYMBOLIC_RANGE
/* Predicates to check whether or not a particular value is symbolic */
extern int McTest_IsSymbolicUInt(uint32_t 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);
}
MCTEST_INLINE static int McTest_IsSymbolicUShort(uint16_t x) {
return McTest_IsSymbolicUInt((uint32_t) x);
}
MCTEST_INLINE static int McTest_IsSymbolicShort(int16_t x) {
return McTest_IsSymbolicUInt((uint32_t) (uint16_t) x);
}
MCTEST_INLINE static int McTest_IsSymbolicUChar(unsigned char x) {
return McTest_IsSymbolicUInt((uint32_t) x);
}
MCTEST_INLINE static int McTest_IsSymbolicChar(char x) {
return McTest_IsSymbolicUInt((uint32_t) (unsigned char) x);
}
MCTEST_INLINE static int McTest_IsSymbolicUInt64(uint64_t x) {
return McTest_IsSymbolicUInt((uint32_t) x) ||
McTest_IsSymbolicUInt((uint32_t) (x >> 32U));
}
MCTEST_INLINE static int McTest_IsSymbolicInt64(int64_t x) {
return McTest_IsSymbolicUInt64((uint64_t) x);
}
MCTEST_INLINE static int McTest_IsSymbolicBool(int x) {
return McTest_IsSymbolicInt(x);
}
MCTEST_INLINE static int McTest_IsSymbolicFloat(float x) {
return McTest_IsSymbolicUInt(*((uint32_t *) &x));
}
MCTEST_INLINE static int McTest_IsSymbolicDouble(double x) {
return McTest_IsSymbolicUInt64(*((uint64_t *) &x));
}
/* Used to define the entrypoint of a test case. */
#define McTest_EntryPoint(test_name) \
_McTest_EntryPoint(test_name, __FILE__, __LINE__)
/* 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;
};
/* 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(); \
} \
static struct McTest_TestInfo McTest_Info_ ## test_name = { \
NULL, \
McTest_Run_ ## test_name, \
MCTEST_TO_STR(test_name), \
file, \
line, \
}; \
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)
/* Set up McTest. */
extern void McTest_Setup(void);
/* Tear down McTest. */
extern void McTest_Teardown(void);
/* Notify that we're about to begin a test. */
extern void McTest_Begin(struct McTest_TestInfo *info);
/* Return the first test case to run. */
extern struct McTest_TestInfo *McTest_FirstTest(void);
/* Returns 1 if a failure was caught, otherwise 0. */
extern int McTest_CatchFail(void);
/* Returns 1 if this test case was abandoned. */
extern int McTest_CatchAbandoned(void);
/* Jump buffer for returning to `McTest_Run`. */
extern jmp_buf McTest_ReturnToRun;
/* Start McTest and run the tests. Returns the number of failed tests. */
static int McTest_Run(void) {
int num_failed_tests = 0;
struct McTest_TestInfo *test = NULL;
McTest_Setup();
for (test = McTest_FirstTest(); test != NULL; test = test->prev) {
McTest_Begin(test);
/* Run the test. */
if (!setjmp(McTest_ReturnToRun)) {
/* Convert uncaught C++ exceptions into a test failure. */
#if defined(__cplusplus) && defined(__cpp_exceptions)
try {
#endif /* __cplusplus */
test->test_func(); /* Run the test function. */
McTest_Pass();
#if defined(__cplusplus) && defined(__cpp_exceptions)
} catch(...) {
McTest_Fail();
}
#endif /* __cplusplus */
/* We caught a failure when running the test. */
} else if (McTest_CatchFail()) {
++num_failed_tests;
McTest_LogFormat(McTest_LogError, "Failed: %s", test->test_name);
/* The test was abandoned. We may have gotten soft failures before
* abandoning, so we prefer to catch those first. */
} else if (McTest_CatchAbandoned()) {
McTest_LogFormat(McTest_LogFatal, "Abandoned: %s", test->test_name);
/* The test passed. */
} else {
McTest_LogFormat(McTest_LogInfo, "Passed: %s", test->test_name);
}
}
McTest_Teardown();
return num_failed_tests;
}
MCTEST_END_EXTERN_C
#endif /* SRC_INCLUDE_MCTEST_MCTEST_H_ */

View File

@ -1,194 +0,0 @@
/*
* 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 SRC_INCLUDE_MCTEST_MCTEST_HPP_
#define SRC_INCLUDE_MCTEST_MCTEST_HPP_
#include <mctest/McTest.h>
#include <string>
#include <utility>
#include <vector>
namespace mctest {
MCTEST_INLINE static void *Malloc(size_t num_bytes) {
return McTest_Malloc(num_bytes);
}
MCTEST_INLINE static void SymbolizeData(void *begin, void *end) {
McTest_SymbolizeData(begin, end);
}
MCTEST_INLINE static bool Bool(void) {
return static_cast<bool>(McTest_Bool());
}
MCTEST_INLINE static size_t Size(void) {
return McTest_Size();
}
MCTEST_INLINE static uint64_t UInt64(void) {
return McTest_UInt64();
}
MCTEST_INLINE static int64_t Int64(void) {
return McTest_Int64();
}
MCTEST_INLINE static uint32_t UInt(void) {
return McTest_UInt();
}
MCTEST_INLINE static int32_t Int(void) {
return McTest_Int();
}
MCTEST_INLINE static uint16_t UShort(void) {
return McTest_UShort();
}
MCTEST_INLINE static int16_t Short(void) {
return McTest_Short();
}
MCTEST_INLINE static unsigned char UChar(void) {
return McTest_UChar();
}
MCTEST_INLINE static char Char(void) {
return McTest_Char();
}
MCTEST_INLINE static bool IsSymbolic(uint64_t x) {
return McTest_IsSymbolicUInt64(x);
}
MCTEST_INLINE static int IsSymbolic(int64_t x) {
return McTest_IsSymbolicInt64(x);
}
MCTEST_INLINE static bool IsSymbolic(uint32_t x) {
return McTest_IsSymbolicUInt(x);
}
MCTEST_INLINE static bool IsSymbolic(int32_t x) {
return McTest_IsSymbolicInt(x);
}
MCTEST_INLINE static int IsSymbolic(uint16_t x) {
return McTest_IsSymbolicUShort(x);
}
MCTEST_INLINE static bool IsSymbolic(int16_t x) {
return McTest_IsSymbolicShort(x);
}
MCTEST_INLINE static bool IsSymbolic(unsigned char x) {
return McTest_IsSymbolicUChar(x);
}
MCTEST_INLINE static bool IsSymbolic(char x) {
return McTest_IsSymbolicChar(x);
}
MCTEST_INLINE static bool IsSymbolic(float x) {
return McTest_IsSymbolicFloat(x);
}
MCTEST_INLINE static bool IsSymbolic(double x) {
return McTest_IsSymbolicDouble(x);
}
template <typename T>
class Symbolic {
public:
template <typename... Args>
MCTEST_INLINE Symbolic(Args&& ...args)
: value(std::forward<Args...>(args)...) {}
MCTEST_INLINE Symbolic(void) {
T *val_ptr = &value;
McTest_SymbolizeData(val_ptr, &(val_ptr[1]));
}
MCTEST_INLINE operator T (void) const {
return value;
}
T value;
};
template <typename T>
class SymbolicLinearContainer {
public:
MCTEST_INLINE explicit SymbolicLinearContainer(size_t len)
: value(len) {
if (!value.empty()) {
McTest_SymbolizeData(&(value.front()), &(value.back()));
}
}
MCTEST_INLINE SymbolicLinearContainer(void)
: SymbolicLinearContainer(McTest_SizeInRange(0, 32)) {}
MCTEST_INLINE operator T (void) const {
return value;
}
T value;
};
template <>
class Symbolic<std::string> : public SymbolicLinearContainer<std::string> {
using SymbolicLinearContainer::SymbolicLinearContainer;
};
template <>
class Symbolic<std::wstring> : public SymbolicLinearContainer<std::wstring> {
using SymbolicLinearContainer::SymbolicLinearContainer;
};
template <typename T>
class Symbolic<std::vector<T>> :
public SymbolicLinearContainer<std::vector<T>> {};
#define MAKE_SYMBOL_SPECIALIZATION(Tname, tname) \
template <> \
class Symbolic<tname> { \
public: \
MCTEST_INLINE Symbolic(void) \
: value(McTest_ ## Tname()) {} \
MCTEST_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
#endif // SRC_INCLUDE_MCTEST_MCTEST_HPP_

View File

@ -1,99 +0,0 @@
/*
* 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 SRC_INCLUDE_MCTEST_MCUNIT_HPP_
#define SRC_INCLUDE_MCTEST_MCUNIT_HPP_
#include <mctest/McTest.hpp>
#include <mctest/Stream.hpp>
#define TEST(category, name) \
McTest_EntryPoint(category ## _ ## name)
#define LOG_DEBUG(cond) \
::mctest::Stream(McTest_LogDebug, (cond), __FILE__, __LINE__)
#define LOG_INFO(cond) \
::mctest::Stream(McTest_LogInfo, (cond), __FILE__, __LINE__)
#define LOG_WARNING(cond) \
::mctest::Stream(McTest_LogWarning, (cond), __FILE__, __LINE__)
#define LOG_WARN(cond) \
::mctest::Stream(McTest_LogWarning, (cond), __FILE__, __LINE__)
#define LOG_ERROR(cond) \
::mctest::Stream(McTest_LogError, (cond), __FILE__, __LINE__)
#define LOG_FATAL(cond) \
::mctest::Stream(McTest_LogFatal, (cond), __FILE__, __LINE__)
#define LOG_CRITICAl(cond) \
::mctest::Stream(McTest_LogFatal, (cond), __FILE__, __LINE__)
#define LOG(LEVEL) LOG_ ## LEVEL(true)
#define LOG_IF(LEVEL, cond) LOG_ ## LEVEL(cond)
#define MCTEST_LOG_BINOP(a, b, op, level) \
::mctest::Stream( \
level, !((a) op (b)), __FILE__, __LINE__)
#define ASSERT_EQ(a, b) MCTEST_LOG_BINOP(a, b, ==, McTest_LogFatal)
#define ASSERT_NE(a, b) MCTEST_LOG_BINOP(a, b, !=, McTest_LogFatal)
#define ASSERT_LT(a, b) MCTEST_LOG_BINOP(a, b, <, McTest_LogFatal)
#define ASSERT_LE(a, b) MCTEST_LOG_BINOP(a, b, <=, McTest_LogFatal)
#define ASSERT_GT(a, b) MCTEST_LOG_BINOP(a, b, >, McTest_LogFatal)
#define ASSERT_GE(a, b) MCTEST_LOG_BINOP(a, b, >=, McTest_LogFatal)
#define CHECK_EQ(a, b) MCTEST_LOG_BINOP(a, b, ==, McTest_LogError)
#define CHECK_NE(a, b) MCTEST_LOG_BINOP(a, b, !=, McTest_LogError)
#define CHECK_LT(a, b) MCTEST_LOG_BINOP(a, b, <, McTest_LogError)
#define CHECK_LE(a, b) MCTEST_LOG_BINOP(a, b, <=, McTest_LogError)
#define CHECK_GT(a, b) MCTEST_LOG_BINOP(a, b, >, McTest_LogError)
#define CHECK_GE(a, b) MCTEST_LOG_BINOP(a, b, >=, McTest_LogError)
#define ASSERT(expr) \
::mctest::Stream( \
McTest_LogFatal, !(expr), __FILE__, __LINE__)
#define ASSERT_TRUE ASSERT
#define ASSERT_FALSE(expr) ASSERT(!(expr))
#define CHECK(expr) \
::mctest::Stream( \
McTest_LogError, !(expr), __FILE__, __LINE__)
#define CHECK_TRUE CHECK
#define CHECK_FALSE(expr) CHECK(!(expr))
#define ASSUME(expr) \
McTest_Assume(expr), ::mctest::Stream( \
McTest_LogInfo, true, __FILE__, __LINE__)
#define MCTEST_ASSUME_BINOP(a, b, op) \
McTest_Assume(((a) op (b))), ::mctest::Stream( \
McTest_LogInfo, true, __FILE__, __LINE__)
#define ASSUME_EQ(a, b) MCTEST_ASSUME_BINOP(a, b, ==)
#define ASSUME_NE(a, b) MCTEST_ASSUME_BINOP(a, b, !=)
#define ASSUME_LT(a, b) MCTEST_ASSUME_BINOP(a, b, <)
#define ASSUME_LE(a, b) MCTEST_ASSUME_BINOP(a, b, <=)
#define ASSUME_GT(a, b) MCTEST_ASSUME_BINOP(a, b, >)
#define ASSUME_GE(a, b) MCTEST_ASSUME_BINOP(a, b, >=)
#endif // SRC_INCLUDE_MCTEST_MCUNIT_HPP_

View File

@ -1,38 +0,0 @@
/*
* 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 SRC_INCLUDE_MCTEST_QUANTIFIED_HPP_
#define SRC_INCLUDE_MCTEST_QUANTIFIED_HPP_
#include <mctest/McTest.hpp>
#include <functional>
namespace mctest {
template <typename... Args>
inline static void ForAll(void (*func)(Args...)) {
func(Symbolic<Args>()...);
}
template <typename... Args, typename Closure>
inline static void ForAll(Closure func) {
func(Symbolic<Args>()...);
}
} // namespace mctest
#endif // SRC_INCLUDE_MCTEST_QUANTIFIED_HPP_

View File

@ -1,79 +0,0 @@
/*
* 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 SRC_INCLUDE_MCTEST_STREAM_H_
#define SRC_INCLUDE_MCTEST_STREAM_H_
#include <stdarg.h>
#include <stdint.h>
#include <mctest/Compiler.h>
#include <mctest/Log.h>
MCTEST_BEGIN_EXTERN_C
/* Flush the contents of the stream to a log. */
extern void McTest_LogStream(enum McTest_LogLevel level);
/* Stream a C string into the stream's message. */
extern void McTest_StreamCStr(enum McTest_LogLevel level, const char *begin);
/* TODO(pag): Implement `McTest_StreamWCStr` with `wchar_t`. */
/* Stream a some data in the inclusive range `[begin, end]` into the
* stream's message. */
/*extern void McTest_StreamData(enum McTest_LogLevel level, const void *begin,
const void *end);*/
/* Stream some formatted input */
extern void McTest_StreamFormat(enum McTest_LogLevel level, const char *format,
...);
/* Stream some formatted input */
extern void McTest_StreamVFormat(enum McTest_LogLevel level, const char *format,
va_list args);
#define MCTEST_DECLARE_STREAMER(Type, type) \
extern void McTest_Stream ## Type(enum McTest_LogLevel level, type val);
MCTEST_DECLARE_STREAMER(Double, double);
MCTEST_DECLARE_STREAMER(Pointer, void *);
MCTEST_DECLARE_STREAMER(UInt64, uint64_t)
MCTEST_DECLARE_STREAMER(Int64, int64_t)
MCTEST_DECLARE_STREAMER(UInt32, uint32_t)
MCTEST_DECLARE_STREAMER(Int32, int32_t)
MCTEST_DECLARE_STREAMER(UInt16, uint16_t)
MCTEST_DECLARE_STREAMER(Int16, int16_t)
MCTEST_DECLARE_STREAMER(UInt8, uint8_t)
MCTEST_DECLARE_STREAMER(Int8, int8_t)
#undef MCTEST_DECLARE_STREAMER
MCTEST_INLINE static void McTest_StreamFloat(enum McTest_LogLevel level,
float val) {
McTest_StreamDouble(level, (double) val);
}
/* Reset the formatting in a stream. */
extern void McTest_StreamResetFormatting(enum McTest_LogLevel level);
MCTEST_END_EXTERN_C
#endif /* SRC_INCLUDE_MCTEST_STREAM_H_ */

View File

@ -1,102 +0,0 @@
/*
* 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 SRC_INCLUDE_MCTEST_STREAM_HPP_
#define SRC_INCLUDE_MCTEST_STREAM_HPP_
#include <mctest/McTest.h>
#include <mctest/Stream.h>
#include <cstddef>
#include <string>
namespace mctest {
/* Conditionally stream output to a log using the streaming APIs. */
class Stream {
public:
MCTEST_INLINE Stream(McTest_LogLevel level_, bool do_log_,
const char *file, unsigned line)
: level(level_),
do_log(McTest_IsTrue(do_log_)) {
McTest_LogStream(level);
if (do_log) {
McTest_StreamFormat(level, "%s(%u): ", file, line);
}
}
MCTEST_INLINE ~Stream(void) {
if (do_log) {
McTest_LogStream(level);
}
}
#define MCTEST_DEFINE_STREAMER(Type, type, expr) \
MCTEST_INLINE const Stream &operator<<(type val) const { \
if (do_log) { \
McTest_Stream ## Type(level, expr); \
} \
return *this; \
}
MCTEST_DEFINE_STREAMER(UInt64, uint64_t, val)
MCTEST_DEFINE_STREAMER(Int64, int64_t, val)
MCTEST_DEFINE_STREAMER(UInt32, uint32_t, val)
MCTEST_DEFINE_STREAMER(Int32, int32_t, val)
MCTEST_DEFINE_STREAMER(UInt16, uint16_t, val)
MCTEST_DEFINE_STREAMER(Int16, int16_t, val)
MCTEST_DEFINE_STREAMER(UInt8, uint8_t, val)
MCTEST_DEFINE_STREAMER(Int8, int8_t, val)
MCTEST_DEFINE_STREAMER(Float, float, val)
MCTEST_DEFINE_STREAMER(Double, double, val)
MCTEST_DEFINE_STREAMER(CStr, const char *, val)
MCTEST_DEFINE_STREAMER(CStr, char *, const_cast<const char *>(val))
MCTEST_DEFINE_STREAMER(Pointer, nullptr_t, nullptr)
template <typename T>
MCTEST_DEFINE_STREAMER(Pointer, T *, val);
template <typename T>
MCTEST_DEFINE_STREAMER(Pointer, const T *, const_cast<T *>(val));
#undef MCTEST_DEFINE_INT_STREAMER
MCTEST_INLINE const Stream &operator<<(const std::string &str) const {
if (do_log && !str.empty()) {
McTest_StreamCStr(level, str.c_str());
}
return *this;
}
// TODO(pag): Implement a `std::wstring` streamer.
private:
Stream(void) = delete;
Stream(const Stream &) = delete;
Stream &operator=(const Stream &) = delete;
const McTest_LogLevel level;
const int do_log;
};
} // namespace mctest
#endif // SRC_INCLUDE_MCTEST_STREAM_HPP_

273
src/lib/DeepState.c Normal file
View File

@ -0,0 +1,273 @@
/*
* 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.
*/
#include "deepstate/DeepState.h"
#include <assert.h>
#include <limits.h>
#include <setjmp.h>
#include <stdio.h>
DEEPSTATE_BEGIN_EXTERN_C
/* Pointer to the last registers DeepState_TestInfo data structure */
struct DeepState_TestInfo *DeepState_LastTestInfo = NULL;
enum {
DeepState_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 DeepState_Input[DeepState_InputLength];
/* Index into the `DeepState_Input` array that tracks how many input bytes have
* been consumed. */
static uint32_t DeepState_InputIndex = 0;
/* Jump buffer for returning to `DeepState_Run`. */
jmp_buf DeepState_ReturnToRun = {};
static const char *DeepState_TestAbandoned = NULL;
static int DeepState_TestFailed = 0;
/* Abandon this test. We've hit some kind of internal problem. */
DEEPSTATE_NORETURN
void DeepState_Abandon(const char *reason) {
DeepState_Log(DeepState_LogFatal, reason);
DeepState_TestAbandoned = reason;
longjmp(DeepState_ReturnToRun, 1);
}
/* Mark this test as failing. */
DEEPSTATE_NORETURN
void DeepState_Fail(void) {
DeepState_TestFailed = 1;
longjmp(DeepState_ReturnToRun, 1);
}
/* Mark this test as passing. */
DEEPSTATE_NORETURN
void DeepState_Pass(void) {
longjmp(DeepState_ReturnToRun, 0);
}
void DeepState_SoftFail(void) {
DeepState_TestFailed = 1;
}
/* Symbolize the data in the exclusive range `[begin, end)`. */
void DeepState_SymbolizeData(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] = DeepState_Input[DeepState_InputIndex++];
}
}
}
/* 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) {
if (SIZE_MAX == len) {
DeepState_Abandon("Can't create an SIZE_MAX-length string.");
}
char *str = (char *) malloc(sizeof(char) * (len + 1));
if (len) {
DeepState_SymbolizeData(str, &(str[len - 1]));
}
str[len] = '\0';
return str;
}
/* Concretize a C string */
const char *DeepState_ConcretizeCStr(const char *begin) {
return begin;
}
/* Allocate and return a pointer to `num_bytes` symbolic bytes. */
void *DeepState_Malloc(size_t num_bytes) {
void *data = malloc(num_bytes);
uintptr_t data_end = ((uintptr_t) data) + num_bytes;
DeepState_SymbolizeData(data, (void *) data_end);
return data;
}
DEEPSTATE_NOINLINE int DeepState_One(void) {
return 1;
}
DEEPSTATE_NOINLINE int DeepState_Zero(void) {
return 0;
}
/* 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. */
int DeepState_IsTrue(int expr) {
if (expr == DeepState_Zero()) {
return DeepState_Zero();
} else {
return DeepState_One();
}
}
/* Return a symbolic value of a given type. */
int DeepState_Bool(void) {
return DeepState_Input[DeepState_InputIndex++] & 1;
}
#define MAKE_SYMBOL_FUNC(Type, type) \
type DeepState_ ## Type(void) { \
type val = 0; \
_Pragma("unroll") \
for (size_t i = 0; i < sizeof(type); ++i) { \
val = (val << 8) | ((type) DeepState_Input[DeepState_InputIndex++]); \
} \
return val; \
}
MAKE_SYMBOL_FUNC(Size, size_t)
MAKE_SYMBOL_FUNC(UInt64, uint64_t)
int64_t DeepState_Int64(void) {
return (int64_t) DeepState_UInt64();
}
MAKE_SYMBOL_FUNC(UInt, uint32_t)
int32_t DeepState_Int(void) {
return (int32_t) DeepState_UInt();
}
MAKE_SYMBOL_FUNC(UShort, uint16_t)
int16_t DeepState_Short(void) {
return (int16_t) DeepState_UShort();
}
MAKE_SYMBOL_FUNC(UChar, uint8_t)
int8_t DeepState_Char(void) {
return (int8_t) DeepState_UChar();
}
#undef MAKE_SYMBOL_FUNC
void _DeepState_Assume(int expr) {
assert(expr);
}
int DeepState_IsSymbolicUInt(uint32_t x) {
(void) x;
return 0;
}
/* Defined in Stream.c */
extern void _DeepState_StreamInt(enum DeepState_LogLevel level, const char *format,
const char *unpack, uint64_t *val);
extern void _DeepState_StreamFloat(enum DeepState_LogLevel level, const char *format,
const char *unpack, double *val);
extern void _DeepState_StreamString(enum DeepState_LogLevel level, const char *format,
const char *str);
/* A DeepState-specific symbol that is needed for hooking. */
struct DeepState_IndexEntry {
const char * const name;
void * const address;
};
/* An index of symbols that the symbolic executors will hook or
* need access to. */
const struct DeepState_IndexEntry DeepState_API[] = {
/* Control-flow during the test. */
{"Pass", (void *) DeepState_Pass},
{"Fail", (void *) DeepState_Fail},
{"SoftFail", (void *) DeepState_SoftFail},
{"Abandon", (void *) DeepState_Abandon},
/* Locating the tests. */
{"LastTestInfo", (void *) &DeepState_LastTestInfo},
/* Source of symbolic bytes. */
{"InputBegin", (void *) &(DeepState_Input[0])},
{"InputEnd", (void *) &(DeepState_Input[DeepState_InputLength])},
{"InputIndex", (void *) &DeepState_InputIndex},
/* Solver APIs. */
{"Assume", (void *) _DeepState_Assume},
{"IsSymbolicUInt", (void *) DeepState_IsSymbolicUInt},
{"ConcretizeData", (void *) DeepState_ConcretizeData},
{"ConcretizeCStr", (void *) DeepState_ConcretizeCStr},
/* Logging API. */
{"Log", (void *) DeepState_Log},
/* Streaming API for deferred logging. */
{"LogStream", (void *) DeepState_LogStream},
{"StreamInt", (void *) _DeepState_StreamInt},
{"StreamFloat", (void *) _DeepState_StreamFloat},
{"StreamString", (void *) _DeepState_StreamString},
{NULL, NULL},
};
/* Set up DeepState. */
void DeepState_Setup(void) {
/* TODO(pag): Sort the test cases by file name and line number. */
}
/* Tear down DeepState. */
void DeepState_Teardown(void) {
}
/* Notify that we're about to begin a test. */
void DeepState_Begin(struct DeepState_TestInfo *info) {
DeepState_TestFailed = 0;
DeepState_TestAbandoned = NULL;
DeepState_LogFormat(DeepState_LogInfo, "Running: %s from %s(%u)",
info->test_name, info->file_name, info->line_number);
}
/* Return the first test case to run. */
struct DeepState_TestInfo *DeepState_FirstTest(void) {
return DeepState_LastTestInfo;
}
/* Returns 1 if a failure was caught, otherwise 0. */
int DeepState_CatchFail(void) {
return DeepState_TestFailed;
}
/* Returns 1 if this test case was abandoned. */
int DeepState_CatchAbandoned(void) {
return DeepState_TestAbandoned != NULL;
}
DEEPSTATE_END_EXTERN_C

View File

@ -18,22 +18,22 @@
#include <stdio.h>
#include <string.h>
#include <mctest/McTest.h>
#include "deepstate/DeepState.h"
MCTEST_BEGIN_EXTERN_C
DEEPSTATE_BEGIN_EXTERN_C
/* Returns a printable string version of the log level. */
static const char *McTest_LogLevelStr(enum McTest_LogLevel level) {
static const char *DeepState_LogLevelStr(enum DeepState_LogLevel level) {
switch (level) {
case McTest_LogDebug:
case DeepState_LogDebug:
return "DEBUG";
case McTest_LogInfo:
case DeepState_LogInfo:
return "INFO";
case McTest_LogWarning:
case DeepState_LogWarning:
return "WARNING";
case McTest_LogError:
case DeepState_LogError:
return "ERROR";
case McTest_LogFatal:
case DeepState_LogFatal:
return "FATAL";
default:
return "UNKNOWN";
@ -41,69 +41,69 @@ static const char *McTest_LogLevelStr(enum McTest_LogLevel level) {
}
enum {
McTest_LogBufSize = 4096
DeepState_LogBufSize = 4096
};
char McTest_LogBuf[McTest_LogBufSize + 1] = {};
char DeepState_LogBuf[DeepState_LogBufSize + 1] = {};
/* Log a C string. */
void McTest_Log(enum McTest_LogLevel level, const char *str) {
memset(McTest_LogBuf, 0, McTest_LogBufSize);
snprintf(McTest_LogBuf, McTest_LogBufSize, "%s: %s",
McTest_LogLevelStr(level), str);
fputs(McTest_LogBuf, stderr);
void DeepState_Log(enum DeepState_LogLevel level, const char *str) {
memset(DeepState_LogBuf, 0, DeepState_LogBufSize);
snprintf(DeepState_LogBuf, DeepState_LogBufSize, "%s: %s",
DeepState_LogLevelStr(level), str);
fputs(DeepState_LogBuf, stderr);
if (McTest_LogError == level) {
McTest_SoftFail();
} else if (McTest_LogFatal == level) {
McTest_Fail();
if (DeepState_LogError == level) {
DeepState_SoftFail();
} else if (DeepState_LogFatal == level) {
DeepState_Fail();
}
}
/* Log some formatted output. */
void McTest_LogFormat(enum McTest_LogLevel level, const char *format, ...) {
McTest_LogStream(level);
void DeepState_LogFormat(enum DeepState_LogLevel level, const char *format, ...) {
DeepState_LogStream(level);
va_list args;
va_start(args, format);
McTest_StreamVFormat(level, format, args);
DeepState_StreamVFormat(level, format, args);
va_end(args);
McTest_LogStream(level);
DeepState_LogStream(level);
}
/* Log some formatted output. */
void McTest_LogVFormat(enum McTest_LogLevel level,
void DeepState_LogVFormat(enum DeepState_LogLevel level,
const char *format, va_list args) {
McTest_LogStream(level);
McTest_StreamVFormat(level, format, args);
McTest_LogStream(level);
DeepState_LogStream(level);
DeepState_StreamVFormat(level, format, args);
DeepState_LogStream(level);
}
/* Override libc! */
int printf(const char *format, ...) {
McTest_LogStream(McTest_LogInfo);
DeepState_LogStream(DeepState_LogInfo);
va_list args;
va_start(args, format);
McTest_StreamVFormat(McTest_LogInfo, format, args);
DeepState_StreamVFormat(DeepState_LogInfo, format, args);
va_end(args);
McTest_LogStream(McTest_LogInfo);
DeepState_LogStream(DeepState_LogInfo);
return 0;
}
int fprintf(FILE *file, const char *format, ...) {
enum McTest_LogLevel level = McTest_LogInfo;
enum DeepState_LogLevel level = DeepState_LogInfo;
if (stderr == file) {
level = McTest_LogDebug;
level = DeepState_LogDebug;
} else if (stdout != file) {
return 0; /* TODO(pag): This is probably evil. */
}
McTest_LogStream(level);
DeepState_LogStream(level);
va_list args;
va_start(args, format);
McTest_StreamVFormat(level, format, args);
DeepState_StreamVFormat(level, format, args);
va_end(args);
McTest_LogStream(level);
DeepState_LogStream(level);
return 0;
}
MCTEST_END_EXTERN_C
DEEPSTATE_END_EXTERN_C

View File

@ -1,251 +0,0 @@
/*
* 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.
*/
#include <mctest/Compiler.h>
#include <mctest/McTest.h>
#include <assert.h>
#include <setjmp.h>
#include <stdio.h>
MCTEST_BEGIN_EXTERN_C
/* Pointer to the last registers McTest_TestInfo data structure */
struct McTest_TestInfo *McTest_LastTestInfo = NULL;
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_Run`. */
jmp_buf McTest_ReturnToRun = {};
static const char *McTest_TestAbandoned = NULL;
static int McTest_TestFailed = 0;
/* Abandon this test. We've hit some kind of internal problem. */
MCTEST_NORETURN
void McTest_Abandon(const char *reason) {
McTest_Log(McTest_LogFatal, reason);
McTest_TestAbandoned = reason;
longjmp(McTest_ReturnToRun, 1);
}
/* Mark this test as failing. */
MCTEST_NORETURN
void McTest_Fail(void) {
McTest_TestFailed = 1;
longjmp(McTest_ReturnToRun, 1);
}
/* Mark this test as passing. */
MCTEST_NORETURN
void McTest_Pass(void) {
longjmp(McTest_ReturnToRun, 0);
}
void McTest_SoftFail(void) {
McTest_TestFailed = 1;
}
void McTest_SymbolizeData(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++];
}
}
}
/* Concretize some data i nthe range `[begin, end)`. */
void McTest_ConcretizeData(void *begin, void *end) {
(void) begin;
}
/* Concretize a C string */
void McTest_ConcretizeCStr(const char *begin) {
(void) begin;
}
MCTEST_NOINLINE int McTest_One(void) {
return 1;
}
MCTEST_NOINLINE int McTest_Zero(void) {
return 0;
}
/* 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. */
int McTest_IsTrue(int expr) {
if (expr == McTest_Zero()) {
return McTest_Zero();
} else {
return McTest_One();
}
}
/* Return a symbolic value of a given type. */
int McTest_Bool(void) {
return McTest_Input[McTest_InputIndex++] & 1;
}
#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(Size, size_t)
MAKE_SYMBOL_FUNC(UInt64, uint64_t)
int64_t McTest_Int64(void) {
return (int64_t) McTest_UInt64();
}
MAKE_SYMBOL_FUNC(UInt, uint32_t)
int32_t McTest_Int(void) {
return (int32_t) McTest_UInt();
}
MAKE_SYMBOL_FUNC(UShort, uint16_t)
int16_t McTest_Short(void) {
return (int16_t) McTest_UShort();
}
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);
}
int McTest_IsSymbolicUInt(uint32_t x) {
(void) x;
return 0;
}
/* Defined in Stream.c */
extern void _McTest_StreamInt(enum McTest_LogLevel level, const char *format,
const char *unpack, uint64_t *val);
extern void _McTest_StreamFloat(enum McTest_LogLevel level, const char *format,
const char *unpack, double *val);
extern void _McTest_StreamString(enum McTest_LogLevel level, const char *format,
const char *str);
/* 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[] = {
/* Control-flow during the test. */
{"Pass", (void *) McTest_Pass},
{"Fail", (void *) McTest_Fail},
{"SoftFail", (void *) McTest_SoftFail},
{"Abandon", (void *) McTest_Abandon},
/* Locating the tests. */
{"LastTestInfo", (void *) &McTest_LastTestInfo},
/* Source of symbolic bytes. */
{"InputBegin", (void *) &(McTest_Input[0])},
{"InputEnd", (void *) &(McTest_Input[McTest_InputLength])},
{"InputIndex", (void *) &McTest_InputIndex},
/* Solver APIs. */
{"Assume", (void *) _McTest_Assume},
{"IsSymbolicUInt", (void *) McTest_IsSymbolicUInt},
{"ConcretizeData", (void *) McTest_ConcretizeData},
{"ConcretizeCStr", (void *) McTest_ConcretizeCStr},
/* Logging API. */
{"Log", (void *) McTest_Log},
/* Streaming API for deferred logging. */
{"LogStream", (void *) McTest_LogStream},
{"StreamInt", (void *) _McTest_StreamInt},
{"StreamFloat", (void *) _McTest_StreamFloat},
{"StreamString", (void *) _McTest_StreamString},
{NULL, NULL},
};
/* Set up McTest. */
void McTest_Setup(void) {
/* TODO(pag): Sort the test cases by file name and line number. */
}
/* Tear down McTest. */
void McTest_Teardown(void) {
}
/* Notify that we're about to begin a test. */
void McTest_Begin(struct McTest_TestInfo *info) {
McTest_TestFailed = 0;
McTest_TestAbandoned = NULL;
McTest_LogFormat(McTest_LogInfo, "Running: %s from %s(%u)",
info->test_name, info->file_name, info->line_number);
}
/* Return the first test case to run. */
struct McTest_TestInfo *McTest_FirstTest(void) {
return McTest_LastTestInfo;
}
/* Returns 1 if a failure was caught, otherwise 0. */
int McTest_CatchFail(void) {
return McTest_TestFailed;
}
/* Returns 1 if this test case was abandoned. */
int McTest_CatchAbandoned(void) {
return McTest_TestAbandoned != NULL;
}
MCTEST_END_EXTERN_C

View File

@ -22,17 +22,16 @@
#include <string.h>
#include <inttypes.h>
#include <mctest/Compiler.h>
#include <mctest/McTest.h>
#include "deepstate/DeepState.h"
MCTEST_BEGIN_EXTERN_C
DEEPSTATE_BEGIN_EXTERN_C
enum {
McTest_StreamSize = 4096
DeepState_StreamSize = 4096
};
/* Formatting options availale to the streaming API. */
struct McTest_StreamFormatOptions {
struct DeepState_StreamFormatOptions {
/* int radix; */
int hex;
int oct;
@ -47,10 +46,10 @@ struct McTest_StreamFormatOptions {
* mirrors C++ I/O streams, not because I/O streams are good, but instead
* because the ability to stream in data to things like the C++-backed
* `ASSERT` and `CHECK` macros is really nice. */
struct McTest_Stream {
struct DeepState_Stream {
int size;
struct McTest_StreamFormatOptions options;
char message[McTest_StreamSize + 2];
struct DeepState_StreamFormatOptions options;
char message[DeepState_StreamSize + 2];
char staging[32];
char format[32];
char unpack[32];
@ -61,28 +60,35 @@ struct McTest_Stream {
};
/* Hard-coded streams for each log level. */
static struct McTest_Stream McTest_Streams[McTest_LogFatal + 1] = {};
static struct DeepState_Stream DeepState_Streams[DeepState_LogFatal + 1] = {};
static char McTest_EndianSpecifier = '=';
/* Endian specifier for Python's `struct.pack` and `struct.unpack`.
* = Native endian
* < Little endian
* > Big endian
*/
static char DeepState_EndianSpecifier = '=';
/* Figure out what the Python `struct` endianness specifier should be. */
MCTEST_INITIALIZER(DetectEndianness) {
DEEPSTATE_INITIALIZER(DetectEndianness) {
static const int one = 1;
if ((const char *) &one) {
McTest_EndianSpecifier = '<'; /* Little endian. */
DeepState_EndianSpecifier = '<'; /* Little endian. */
} else {
McTest_EndianSpecifier = '>'; /* Big endian. */
DeepState_EndianSpecifier = '>'; /* Big endian. */
}
}
static void McTest_StreamUnpack(struct McTest_Stream *stream, char type) {
stream->unpack[0] = McTest_EndianSpecifier;
/* Fills the `stream->unpack` character buffer with a Python `struct.unpack`-
* compatible format specifier. */
static void DeepState_StreamUnpack(struct DeepState_Stream *stream, char type) {
stream->unpack[0] = DeepState_EndianSpecifier;
stream->unpack[1] = type;
stream->unpack[2] = '\0';
}
/* Fill in the format for when we want to stream an integer. */
static void McTest_StreamIntFormat(struct McTest_Stream *stream,
static void DeepState_StreamIntFormat(struct DeepState_Stream *stream,
size_t val_size, int is_unsigned) {
char *format = stream->format;
int i = 0;
@ -135,11 +141,13 @@ static void McTest_StreamIntFormat(struct McTest_Stream *stream,
format[i++] = '\0';
}
static void CheckCapacity(struct McTest_Stream *stream, int num_chars_to_add) {
/* Make sure that we don't exceed our formatting capacity when running. */
static void CheckCapacity(struct DeepState_Stream *stream,
int num_chars_to_add) {
if (0 > num_chars_to_add) {
McTest_Abandon("Can't add a negative number of characters to a stream.");
} else if ((stream->size + num_chars_to_add) >= McTest_StreamSize) {
McTest_Abandon("Exceeded capacity of stream buffer.");
DeepState_Abandon("Can't add a negative number of characters to a stream.");
} else if ((stream->size + num_chars_to_add) >= DeepState_StreamSize) {
DeepState_Abandon("Exceeded capacity of stream buffer.");
}
}
@ -147,12 +155,12 @@ static void CheckCapacity(struct McTest_Stream *stream, int num_chars_to_add) {
* be hooked by the symbolic executor, so that it can easily pull out the
* relevant data from `*val`, which may be symbolic, and defer the actual
* formatting until later. */
MCTEST_NOINLINE
void _McTest_StreamInt(enum McTest_LogLevel level, const char *format,
DEEPSTATE_NOINLINE
void _DeepState_StreamInt(enum DeepState_LogLevel level, const char *format,
const char *unpack, uint64_t *val) {
struct McTest_Stream *stream = &(McTest_Streams[level]);
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
int size = 0;
int remaining_size = McTest_StreamSize - stream->size;
int remaining_size = DeepState_StreamSize - stream->size;
if (unpack[1] == 'Q' || unpack[1] == 'q') {
size = snprintf(&(stream->message[stream->size]),
remaining_size, format, *val);
@ -164,22 +172,24 @@ void _McTest_StreamInt(enum McTest_LogLevel level, const char *format,
stream->size += size;
}
MCTEST_NOINLINE
void _McTest_StreamFloat(enum McTest_LogLevel level, const char *format,
/* Format a streamed-in float. This gets hooked. */
DEEPSTATE_NOINLINE
void _DeepState_StreamFloat(enum DeepState_LogLevel level, const char *format,
const char *unpack, double *val) {
struct McTest_Stream *stream = &(McTest_Streams[level]);
int remaining_size = McTest_StreamSize - stream->size;
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
int remaining_size = DeepState_StreamSize - stream->size;
int size = snprintf(&(stream->message[stream->size]),
remaining_size, format, *val);
CheckCapacity(stream, size);
stream->size += size;
}
MCTEST_NOINLINE
void _McTest_StreamString(enum McTest_LogLevel level, const char *format,
/* Format a streamed-in NUL-terminated string. This gets hooked. */
DEEPSTATE_NOINLINE
void _DeepState_StreamString(enum DeepState_LogLevel level, const char *format,
const char *str) {
struct McTest_Stream *stream = &(McTest_Streams[level]);
int remaining_size = McTest_StreamSize - stream->size;
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
int remaining_size = DeepState_StreamSize - stream->size;
int size = snprintf(&(stream->message[stream->size]),
remaining_size, format, str);
CheckCapacity(stream, size);
@ -187,13 +197,13 @@ void _McTest_StreamString(enum McTest_LogLevel level, const char *format,
}
#define MAKE_INT_STREAMER(Type, type, is_unsigned, pack_kind) \
void McTest_Stream ## Type(enum McTest_LogLevel level, type val) { \
struct McTest_Stream *stream = &(McTest_Streams[level]); \
McTest_StreamIntFormat(stream, sizeof(val), is_unsigned); \
McTest_StreamUnpack(stream, pack_kind); \
void DeepState_Stream ## Type(enum DeepState_LogLevel level, type val) { \
struct DeepState_Stream *stream = &(DeepState_Streams[level]); \
DeepState_StreamIntFormat(stream, sizeof(val), is_unsigned); \
DeepState_StreamUnpack(stream, pack_kind); \
stream->value.as_uint64 = (uint64_t) val; \
_McTest_StreamInt(level, stream->format, stream->unpack, \
&(stream->value.as_uint64)); \
_DeepState_StreamInt(level, stream->format, stream->unpack, \
&(stream->value.as_uint64)); \
}
MAKE_INT_STREAMER(Pointer, void *, 1, (sizeof(void *) == 8 ? 'Q' : 'I'))
@ -213,16 +223,16 @@ MAKE_INT_STREAMER(Int8, int8_t, 0, 'c')
#undef MAKE_INT_STREAMER
/* Stream a C string into the stream's message. */
void McTest_StreamCStr(enum McTest_LogLevel level, const char *begin) {
_McTest_StreamString(level, "%s", begin);
void DeepState_StreamCStr(enum DeepState_LogLevel level, const char *begin) {
_DeepState_StreamString(level, "%s", begin);
}
/* Stream a some data in the inclusive range `[begin, end]` into the
* stream's message. */
/*void McTest_StreamData(enum McTest_LogLevel level, const void *begin,
/*void DeepState_StreamData(enum DeepState_LogLevel level, const void *begin,
const void *end) {
struct McTest_Stream *stream = &(McTest_Streams[level]);
int remaining_size = McTest_StreamSize - stream->size;
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
int remaining_size = DeepState_StreamSize - stream->size;
int input_size = (int) ((uintptr_t) end - (uintptr_t) begin) + 1;
CheckCapacity(stream, input_size);
memcpy(&(stream->message[stream->size]), begin, (size_t) input_size);
@ -233,38 +243,45 @@ void McTest_StreamCStr(enum McTest_LogLevel level, const char *begin) {
* be hooked by the symbolic executor, so that it can easily pull out the
* relevant data from `*val`, which may be symbolic, and defer the actual
* formatting until later. */
void McTest_StreamDouble(enum McTest_LogLevel level, double val) {
struct McTest_Stream *stream = &(McTest_Streams[level]);
void DeepState_StreamDouble(enum DeepState_LogLevel level, double val) {
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
const char *format = "%f"; /* TODO(pag): Support more? */
stream->value.as_fp64 = val;
McTest_StreamUnpack(stream, 'd');
_McTest_StreamFloat(level, format, stream->unpack, &(stream->value.as_fp64));
DeepState_StreamUnpack(stream, 'd');
_DeepState_StreamFloat(level, format, stream->unpack, &(stream->value.as_fp64));
}
/* Flush the contents of the stream to a log. */
void McTest_LogStream(enum McTest_LogLevel level) {
struct McTest_Stream *stream = &(McTest_Streams[level]);
void DeepState_LogStream(enum DeepState_LogLevel level) {
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
if (stream->size) {
stream->message[stream->size] = '\n';
stream->message[stream->size + 1] = '\0';
stream->message[McTest_StreamSize] = '\0';
McTest_Log(level, stream->message);
memset(stream->message, 0, McTest_StreamSize);
stream->message[DeepState_StreamSize] = '\0';
DeepState_Log(level, stream->message);
memset(stream->message, 0, DeepState_StreamSize);
stream->size = 0;
}
}
/* Reset the formatting in a stream. */
void McTest_StreamResetFormatting(enum McTest_LogLevel level) {
struct McTest_Stream *stream = &(McTest_Streams[level]);
void DeepState_StreamResetFormatting(enum DeepState_LogLevel level) {
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
memset(&(stream->options), 0, sizeof(stream->options));
}
static int McTest_NumLsInt64BitFormat = 2;
/* `PRId64` will be "ld" or "lld" */
DEEPSTATE_INITIALIZER(McTest_NumLsFor64BitFormat) {
McTest_NumLsInt64BitFormat = (PRId64)[1] == 'd' ? 1 : 2;
}
/* Approximately do string format parsing and convert it into calls into our
* streaming API. */
static int McTest_StreamFormatValue(enum McTest_LogLevel level,
const char *format, va_list args) {
struct McTest_Stream *stream = &(McTest_Streams[level]);
static int DeepState_StreamFormatValue(enum DeepState_LogLevel level,
const char *format, va_list args) {
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
char format_buf[32] = {'\0'};
int i = 0;
int k = 0;
@ -274,6 +291,7 @@ static int McTest_StreamFormatValue(enum McTest_LogLevel level,
int is_unsigned = 0;
int is_float = 0;
int long_double = 0;
int num_ls = 0;
char extract = '\0';
#define READ_FORMAT_CHAR \
@ -284,7 +302,7 @@ static int McTest_StreamFormatValue(enum McTest_LogLevel level,
READ_FORMAT_CHAR; /* Read the '%' */
if ('%' != ch) {
McTest_Abandon("Invalid format.");
DeepState_Abandon("Invalid format.");
return 0;
}
@ -293,7 +311,7 @@ get_flag_char:
READ_FORMAT_CHAR;
switch (ch) {
case '\0':
McTest_Abandon("Incomplete format (flags).");
DeepState_Abandon("Incomplete format (flags).");
return 0;
case '-':
case '+':
@ -309,10 +327,10 @@ get_flag_char:
get_width_char:
switch (ch) {
case '\0':
McTest_Abandon("Incomplete format (width).");
DeepState_Abandon("Incomplete format (width).");
return 0;
case '*':
McTest_Abandon("Variable width printing not supported.");
DeepState_Abandon("Variable width printing not supported.");
return 0;
case '0':
case '1':
@ -336,10 +354,10 @@ get_width_char:
READ_FORMAT_CHAR;
switch (ch) {
case '\0':
McTest_Abandon("Incomplete format (precision).");
DeepState_Abandon("Incomplete format (precision).");
return 0;
case '*':
McTest_Abandon("Variable precision printing not supported.");
DeepState_Abandon("Variable precision printing not supported.");
break;
case '0':
case '1':
@ -361,7 +379,7 @@ get_width_char:
get_length_char:
switch (ch) {
case '\0':
McTest_Abandon("Incomplete format (length).");
DeepState_Abandon("Incomplete format (length).");
return 0;
case 'L':
long_double = 1;
@ -373,7 +391,7 @@ get_length_char:
READ_FORMAT_CHAR;
goto get_length_char;
case 'l':
length *= 2;
num_ls += 1;
READ_FORMAT_CHAR;
goto get_length_char;
case 'j':
@ -394,7 +412,7 @@ get_length_char:
if (!length) {
length = 1;
} else if (8 < length) {
} else if (num_ls >= McTest_NumLsInt64BitFormat) {
length = 8;
}
@ -403,7 +421,7 @@ get_length_char:
/* Specifier */
switch(ch) {
case '\0':
McTest_Abandon("Incomplete format (specifier).");
DeepState_Abandon("Incomplete format (specifier).");
return 0;
case 'n':
@ -431,7 +449,7 @@ get_length_char:
stream->value.as_uint64 = (uint64_t) va_arg(args, int64_t);
extract = 'q';
} else {
McTest_Abandon("Unsupported integer length.");
DeepState_Abandon("Unsupported integer length.");
}
goto common_stream_int;
@ -459,12 +477,12 @@ get_length_char:
stream->value.as_uint64 = (uint64_t) va_arg(args, uint64_t);
extract = 'Q';
} else {
McTest_Abandon("Unsupported integer length.");
DeepState_Abandon("Unsupported integer length.");
}
common_stream_int:
McTest_StreamUnpack(stream, extract);
_McTest_StreamInt(level, format_buf, stream->unpack,
DeepState_StreamUnpack(stream, extract);
_DeepState_StreamInt(level, format_buf, stream->unpack,
&(stream->value.as_uint64));
break;
@ -482,37 +500,42 @@ get_length_char:
} else {
stream->value.as_fp64 = va_arg(args, double);
}
McTest_StreamUnpack(stream, 'd');
DeepState_StreamUnpack(stream, 'd');
break;
case 's':
_McTest_StreamString(level, format_buf, va_arg(args, const char *));
_DeepState_StreamString(level, format_buf, va_arg(args, const char *));
break;
default:
McTest_Abandon("Unsupported format specifier.");
DeepState_Abandon("Unsupported format specifier.");
return 0;
}
return i;
}
static char McTest_Format[McTest_StreamSize + 1];
/* Holding buffer for a format string. If we have something like `foo%dbar`
* then we want to be able to pull out the `%d`, and so having the format
* string in a mutable buffer lets us conveniently NUL-out the `b` of `bar`
* following the `%d`. */
static char DeepState_Format[DeepState_StreamSize + 1];
/* Stream some formatted input */
void McTest_StreamVFormat(enum McTest_LogLevel level, const char *format_,
va_list args) {
/* Stream some formatted input. This converts a `printf`-style format string
* into a */
void DeepState_StreamVFormat(enum DeepState_LogLevel level, const char *format_,
va_list args) {
char *begin = NULL;
char *end = NULL;
char *format = McTest_Format;
char *format = DeepState_Format;
int i = 0;
char ch = '\0';
char next_ch = '\0';
strncpy(format, format_, McTest_StreamSize);
format[McTest_StreamSize] = '\0';
strncpy(format, format_, DeepState_StreamSize);
format[DeepState_StreamSize] = '\0';
McTest_ConcretizeCStr(format);
DeepState_ConcretizeCStr(format);
for (i = 0; '\0' != (ch = format[i]); ) {
if (!begin) {
@ -524,7 +547,7 @@ void McTest_StreamVFormat(enum McTest_LogLevel level, const char *format_,
end = &(format[i]);
next_ch = end[1];
end[1] = '\0';
McTest_StreamCStr(level, begin);
DeepState_StreamCStr(level, begin);
end[1] = next_ch;
begin = NULL;
end = NULL;
@ -534,12 +557,12 @@ void McTest_StreamVFormat(enum McTest_LogLevel level, const char *format_,
if (end) {
next_ch = end[1];
end[1] = '\0';
McTest_StreamCStr(level, begin);
DeepState_StreamCStr(level, begin);
end[1] = next_ch;
}
begin = NULL;
end = NULL;
i += McTest_StreamFormatValue(level, &(format[i]), args);
i += DeepState_StreamFormatValue(level, &(format[i]), args);
}
} else {
end = &(format[i]);
@ -548,16 +571,16 @@ void McTest_StreamVFormat(enum McTest_LogLevel level, const char *format_,
}
if (begin && begin[0]) {
McTest_StreamCStr(level, begin);
DeepState_StreamCStr(level, begin);
}
}
/* Stream some formatted input */
void McTest_StreamFormat(enum McTest_LogLevel level, const char *format, ...) {
void DeepState_StreamFormat(enum DeepState_LogLevel level, const char *format, ...) {
va_list args;
va_start(args, format);
McTest_StreamVFormat(level, format, args);
DeepState_StreamVFormat(level, format, args);
va_end(args);
}
MCTEST_END_EXTERN_C
DEEPSTATE_END_EXTERN_C