From d2bc82fc3532d873a69f3714e1b32aebc876fe7a Mon Sep 17 00:00:00 2001 From: Peter Goodman Date: Wed, 1 Nov 2017 13:38:32 -0400 Subject: [PATCH] Renaming from McTest to DeepState. --- CMakeLists.txt | 8 +- bin/{mctest => deepstate}/__init__.py | 0 bin/{mctest => deepstate}/common.py | 28 +- bin/{mctest => deepstate}/main_angr.py | 38 +-- .../main_manticore.py} | 38 +-- bin/setup.py.in | 14 +- examples/CMakeLists.txt | 12 +- ...icProperties.cpp => IntegerArithmetic.cpp} | 9 +- ...OutOfBoundsInt.cpp => IntegerOverflow.cpp} | 22 +- examples/Lists.cpp | 7 +- examples/StreamingAndFormatting.cpp | 4 +- src/include/{mctest => deepstate}/Compiler.h | 46 +-- src/include/deepstate/DeepState.h | 312 +++++++++++++++++ src/include/deepstate/DeepState.hpp | 280 +++++++++++++++ src/include/{mctest => deepstate}/Log.h | 36 +- src/include/deepstate/Stream.h | 79 +++++ src/include/deepstate/Stream.hpp | 102 ++++++ src/include/mctest/McTest.h | 318 ------------------ src/include/mctest/McTest.hpp | 194 ----------- src/include/mctest/McUnit.hpp | 99 ------ src/include/mctest/Quantified.hpp | 38 --- src/include/mctest/Stream.h | 79 ----- src/include/mctest/Stream.hpp | 102 ------ src/lib/DeepState.c | 273 +++++++++++++++ src/lib/Log.c | 72 ++-- src/lib/McTest.c | 251 -------------- src/lib/Stream.c | 201 ++++++----- 27 files changed, 1325 insertions(+), 1337 deletions(-) rename bin/{mctest => deepstate}/__init__.py (100%) rename bin/{mctest => deepstate}/common.py (92%) rename bin/{mctest => deepstate}/main_angr.py (88%) rename bin/{mctest/__main__.py => deepstate/main_manticore.py} (87%) rename examples/{ArithmeticProperties.cpp => IntegerArithmetic.cpp} (89%) rename examples/{OutOfBoundsInt.cpp => IntegerOverflow.cpp} (68%) rename src/include/{mctest => deepstate}/Compiler.h (62%) create mode 100644 src/include/deepstate/DeepState.h create mode 100644 src/include/deepstate/DeepState.hpp rename src/include/{mctest => deepstate}/Log.h (55%) create mode 100644 src/include/deepstate/Stream.h create mode 100644 src/include/deepstate/Stream.hpp delete mode 100644 src/include/mctest/McTest.h delete mode 100644 src/include/mctest/McTest.hpp delete mode 100644 src/include/mctest/McUnit.hpp delete mode 100644 src/include/mctest/Quantified.hpp delete mode 100644 src/include/mctest/Stream.h delete mode 100644 src/include/mctest/Stream.hpp create mode 100644 src/lib/DeepState.c delete mode 100644 src/lib/McTest.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 2026ac7..8d31fa9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/bin/mctest/__init__.py b/bin/deepstate/__init__.py similarity index 100% rename from bin/mctest/__init__.py rename to bin/deepstate/__init__.py diff --git a/bin/mctest/common.py b/bin/deepstate/common.py similarity index 92% rename from bin/mctest/common.py rename to bin/deepstate/common.py index df807cd..dba1898 100644 --- a/bin/mctest/common.py +++ b/bin/deepstate/common.py @@ -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 diff --git a/bin/mctest/main_angr.py b/bin/deepstate/main_angr.py similarity index 88% rename from bin/mctest/main_angr.py rename to bin/deepstate/main_angr.py index 9c19dd8..def913a 100644 --- a/bin/mctest/main_angr.py +++ b/bin/deepstate/main_angr.py @@ -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) diff --git a/bin/mctest/__main__.py b/bin/deepstate/main_manticore.py similarity index 87% rename from bin/mctest/__main__.py rename to bin/deepstate/main_manticore.py index 70eb293..dfaad68 100644 --- a/bin/mctest/__main__.py +++ b/bin/deepstate/main_manticore.py @@ -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)) diff --git a/bin/setup.py.in b/bin/setup.py.in index 81a7368..6639b37 100644 --- a/bin/setup.py.in +++ b/bin/setup.py.in @@ -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' ] }) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 09ce33c..7d2669d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -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) diff --git a/examples/ArithmeticProperties.cpp b/examples/IntegerArithmetic.cpp similarity index 89% rename from examples/ArithmeticProperties.cpp rename to examples/IntegerArithmetic.cpp index e65902c..6bd4f74 100644 --- a/examples/ArithmeticProperties.cpp +++ b/examples/IntegerArithmetic.cpp @@ -14,12 +14,11 @@ * limitations under the License. */ -#include -#include +#include -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(); } diff --git a/examples/OutOfBoundsInt.cpp b/examples/IntegerOverflow.cpp similarity index 68% rename from examples/OutOfBoundsInt.cpp rename to examples/IntegerOverflow.cpp index 3bd35db..362b639 100644 --- a/examples/OutOfBoundsInt.cpp +++ b/examples/IntegerOverflow.cpp @@ -14,30 +14,32 @@ * limitations under the License. */ -#include +#include -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 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 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(); } diff --git a/examples/Lists.cpp b/examples/Lists.cpp index 2c6d625..1723b63 100644 --- a/examples/Lists.cpp +++ b/examples/Lists.cpp @@ -14,13 +14,12 @@ * limitations under the License. */ -#include -#include +#include #include #include -using namespace mctest; +using namespace deepstate; TEST(Vector, DoubleReversal) { ForAll>([] (const std::vector &vec1) { @@ -33,5 +32,5 @@ TEST(Vector, DoubleReversal) { } int main(void) { - McTest_Run(); + DeepState_Run(); } diff --git a/examples/StreamingAndFormatting.cpp b/examples/StreamingAndFormatting.cpp index 3698ea0..960aa70 100644 --- a/examples/StreamingAndFormatting.cpp +++ b/examples/StreamingAndFormatting.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include +#include 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(); } diff --git a/src/include/mctest/Compiler.h b/src/include/deepstate/Compiler.h similarity index 62% rename from src/include/mctest/Compiler.h rename to src/include/deepstate/Compiler.h index 210cab1..038f0c1 100644 --- a/src/include/mctest/Compiler.h +++ b/src/include/deepstate/Compiler.h @@ -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 #include /* 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 -# 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_ */ diff --git a/src/include/deepstate/DeepState.h b/src/include/deepstate/DeepState.h new file mode 100644 index 0000000..8520747 --- /dev/null +++ b/src/include/deepstate/DeepState.h @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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_ */ diff --git a/src/include/deepstate/DeepState.hpp b/src/include/deepstate/DeepState.hpp new file mode 100644 index 0000000..25cdd76 --- /dev/null +++ b/src/include/deepstate/DeepState.hpp @@ -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 +#include + +#include +#include +#include + +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(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 +class Symbolic { + public: + template + DEEPSTATE_INLINE Symbolic(Args&& ...args) + : value(std::forward(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 +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 : public SymbolicLinearContainer { + using SymbolicLinearContainer::SymbolicLinearContainer; +}; + +template <> +class Symbolic : public SymbolicLinearContainer { + using SymbolicLinearContainer::SymbolicLinearContainer; +}; + +template +class Symbolic> : + public SymbolicLinearContainer> {}; + +#define MAKE_SYMBOL_SPECIALIZATION(Tname, tname) \ + template <> \ + class Symbolic { \ + 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 +inline static void ForAll(void (*func)(Args...)) { + func(Symbolic()...); +} + +template +inline static void ForAll(Closure func) { + func(Symbolic()...); +} + +} // 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_ diff --git a/src/include/mctest/Log.h b/src/include/deepstate/Log.h similarity index 55% rename from src/include/mctest/Log.h rename to src/include/deepstate/Log.h index 43b8888..1102318 100644 --- a/src/include/mctest/Log.h +++ b/src/include/deepstate/Log.h @@ -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 -#include +#include -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_ */ diff --git a/src/include/deepstate/Stream.h b/src/include/deepstate/Stream.h new file mode 100644 index 0000000..0dc8527 --- /dev/null +++ b/src/include/deepstate/Stream.h @@ -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 +#include + +#include +#include + +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_ */ diff --git a/src/include/deepstate/Stream.hpp b/src/include/deepstate/Stream.hpp new file mode 100644 index 0000000..176b8a5 --- /dev/null +++ b/src/include/deepstate/Stream.hpp @@ -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 +#include + +#include +#include + +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(val)) + + DEEPSTATE_DEFINE_STREAMER(Pointer, nullptr_t, nullptr) + + template + DEEPSTATE_DEFINE_STREAMER(Pointer, T *, val); + + template + DEEPSTATE_DEFINE_STREAMER(Pointer, const T *, const_cast(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_ diff --git a/src/include/mctest/McTest.h b/src/include/mctest/McTest.h deleted file mode 100644 index 06db23f..0000000 --- a/src/include/mctest/McTest.h +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#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_ */ diff --git a/src/include/mctest/McTest.hpp b/src/include/mctest/McTest.hpp deleted file mode 100644 index ab23539..0000000 --- a/src/include/mctest/McTest.hpp +++ /dev/null @@ -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 - -#include -#include -#include - -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(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 -class Symbolic { - public: - template - MCTEST_INLINE Symbolic(Args&& ...args) - : value(std::forward(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 -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 : public SymbolicLinearContainer { - using SymbolicLinearContainer::SymbolicLinearContainer; -}; - -template <> -class Symbolic : public SymbolicLinearContainer { - using SymbolicLinearContainer::SymbolicLinearContainer; -}; - -template -class Symbolic> : - public SymbolicLinearContainer> {}; - -#define MAKE_SYMBOL_SPECIALIZATION(Tname, tname) \ - template <> \ - class Symbolic { \ - 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_ diff --git a/src/include/mctest/McUnit.hpp b/src/include/mctest/McUnit.hpp deleted file mode 100644 index 8a605d5..0000000 --- a/src/include/mctest/McUnit.hpp +++ /dev/null @@ -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 -#include - -#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_ diff --git a/src/include/mctest/Quantified.hpp b/src/include/mctest/Quantified.hpp deleted file mode 100644 index bc5d372..0000000 --- a/src/include/mctest/Quantified.hpp +++ /dev/null @@ -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 - -#include - -namespace mctest { - -template -inline static void ForAll(void (*func)(Args...)) { - func(Symbolic()...); -} - -template -inline static void ForAll(Closure func) { - func(Symbolic()...); -} - -} // namespace mctest - -#endif // SRC_INCLUDE_MCTEST_QUANTIFIED_HPP_ diff --git a/src/include/mctest/Stream.h b/src/include/mctest/Stream.h deleted file mode 100644 index 52560ac..0000000 --- a/src/include/mctest/Stream.h +++ /dev/null @@ -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 -#include - -#include -#include - -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_ */ diff --git a/src/include/mctest/Stream.hpp b/src/include/mctest/Stream.hpp deleted file mode 100644 index 54213c6..0000000 --- a/src/include/mctest/Stream.hpp +++ /dev/null @@ -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 -#include - -#include -#include - -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(val)) - - MCTEST_DEFINE_STREAMER(Pointer, nullptr_t, nullptr) - - template - MCTEST_DEFINE_STREAMER(Pointer, T *, val); - - template - MCTEST_DEFINE_STREAMER(Pointer, const T *, const_cast(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_ diff --git a/src/lib/DeepState.c b/src/lib/DeepState.c new file mode 100644 index 0000000..2d9bb72 --- /dev/null +++ b/src/lib/DeepState.c @@ -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 +#include +#include +#include + +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 diff --git a/src/lib/Log.c b/src/lib/Log.c index aa739ae..70d9820 100644 --- a/src/lib/Log.c +++ b/src/lib/Log.c @@ -18,22 +18,22 @@ #include #include -#include +#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 diff --git a/src/lib/McTest.c b/src/lib/McTest.c deleted file mode 100644 index d25343e..0000000 --- a/src/lib/McTest.c +++ /dev/null @@ -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 -#include - -#include -#include -#include - -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 diff --git a/src/lib/Stream.c b/src/lib/Stream.c index 787d3ad..d379fd0 100644 --- a/src/lib/Stream.c +++ b/src/lib/Stream.c @@ -22,17 +22,16 @@ #include #include -#include -#include +#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