merge + fixes in travis
This commit is contained in:
commit
8ba19cb121
22
.travis.yml
22
.travis.yml
@ -1,20 +1,18 @@
|
||||
language: python
|
||||
dist: trusty
|
||||
language: bash
|
||||
dist: xenial
|
||||
sudo: true
|
||||
python:
|
||||
- "2.7"
|
||||
#python:
|
||||
#- "2.7"
|
||||
install:
|
||||
- pip install pyflakes
|
||||
- sudo apt-get -y update
|
||||
- sudo apt-get -y install build-essential gcc-multilib cmake python python-pip python-setuptools libffi-dev
|
||||
- sudo pip install -U pip ; sudo pip install manticore
|
||||
- sudo pip uninstall -y Manticore || echo "Manticore not cached"
|
||||
- sudo pip install https://github.com/trailofbits/manticore/archive/last_python2.zip
|
||||
- sudo apt-get -y install build-essential gcc-multilib cmake python3 python3-pip python3-setuptools libffi-dev python3-nose
|
||||
- pip3 install pyflakes --user
|
||||
- pip3 install angr --user
|
||||
- pip3 install manticore --user
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
- make
|
||||
- python setup.py install
|
||||
- sudo make install
|
||||
- cd ..
|
||||
env:
|
||||
- TASK=ARITHMETIC
|
||||
@ -25,6 +23,7 @@ env:
|
||||
- TASK=ONEOF
|
||||
- TASK=OVERFLOW
|
||||
- TASK=PRIMES
|
||||
- TASK=RUNLEN
|
||||
- TASK=STREAMINGANDFORMATTING
|
||||
- TASK=TAKEOVER
|
||||
script:
|
||||
@ -36,6 +35,7 @@ script:
|
||||
- if [ $TASK = KLEE ]; then nosetests tests/test_klee.py ; fi
|
||||
- if [ $TASK = LISTS ]; then nosetests tests/test_lists.py ; fi
|
||||
- if [ $TASK = ONEOF ]; then nosetests tests/test_oneof.py ; fi
|
||||
- if [ $TASK = RUNLEN ]; then nosetests tests/test_runlen.py ; fi
|
||||
- if [ $TASK = OVERFLOW ]; then nosetests tests/test_overflow.py ; fi
|
||||
- if [ $TASK = PRIMES ]; then nosetests tests/test_primes.py ; fi
|
||||
- if [ $TASK = STREAMINGANDFORMATTING ]; then nosetests tests/test_streamingandformatting.py ; fi
|
||||
|
||||
@ -15,15 +15,31 @@
|
||||
project(deepstate)
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
if (NOT DEFINED BUILD_LIBFUZZER AND DEFINED ENV{BUILD_LIBFUZZER})
|
||||
set(BUILD_LIBFUZZER "$ENV{BUILD_LIBFUZZER}")
|
||||
endif()
|
||||
|
||||
if (BUILD_LIBFUZZER)
|
||||
if(NOT DEFINED CMAKE_C_COMPILER)
|
||||
set(CMAKE_C_COMPILER clang)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_CXX_COMPILER)
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
endif()
|
||||
|
||||
if (NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" AND NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
|
||||
message(FATAL_ERROR "DeepState's libFuzzer mode requires the Clang C compiler.")
|
||||
endif()
|
||||
|
||||
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
|
||||
message(FATAL_ERROR "DeepState's libFuzzer mode requires the Clang C++ compiler.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
enable_language(C)
|
||||
enable_language(CXX)
|
||||
|
||||
set(BUILD_LIBFUZZER "$ENV{BUILD_LIBFUZZER}")
|
||||
if (BUILD_LIBFUZZER)
|
||||
SET(CMAKE_C_COMPILER clang)
|
||||
SET(CMAKE_CXX_COMPILER clang++)
|
||||
endif()
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
@ -65,9 +81,10 @@ target_compile_options(${PROJECT_NAME} PUBLIC -mno-avx)
|
||||
|
||||
target_compile_options(${PROJECT_NAME}32 PUBLIC -m32 -g3 -mno-avx)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} -static "-Wl,--allow-multiple-definition,--no-export-dynamic")
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}32 -static "-Wl,--allow-multiple-definition,--no-export-dynamic")
|
||||
if (NOT APPLE)
|
||||
target_link_libraries(${PROJECT_NAME} -static "-Wl,--allow-multiple-definition,--no-export-dynamic")
|
||||
target_link_libraries(${PROJECT_NAME}32 -static "-Wl,--allow-multiple-definition,--no-export-dynamic")
|
||||
endif()
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PUBLIC SYSTEM "${CMAKE_SOURCE_DIR}/src/include"
|
||||
@ -98,7 +115,7 @@ if (BUILD_LIBFUZZER)
|
||||
src/lib/Stream.c
|
||||
)
|
||||
|
||||
target_compile_options(${PROJECT_NAME}_LF PUBLIC -mno-avx -fsanitize=fuzzer-no-link)
|
||||
target_compile_options(${PROJECT_NAME}_LF PUBLIC -DLIBFUZZER -mno-avx -fsanitize=fuzzer-no-link,undefined)
|
||||
|
||||
target_include_directories(${PROJECT_NAME}_LF
|
||||
PUBLIC SYSTEM "${CMAKE_SOURCE_DIR}/src/include"
|
||||
|
||||
282
README.md
282
README.md
@ -6,7 +6,7 @@
|
||||
|
||||
DeepState is a framework that provides C and C++ developers with a common interface to various symbolic execution and fuzzing engines. Users can write one test harness using a Google Test-like API, then execute it using multiple backends without having to learn the complexities of the underlying engines. It supports writing unit tests and API sequence tests, as well as automatic test generation. Read more about the goals and design of DeepState in our [paper](https://agroce.github.io/bar18.pdf).
|
||||
|
||||
The [2018 IEEE Cybersecurity Development Conference](https://secdev.ieee.org/2018/home) will include a full tutorial on effective use of DeepState.
|
||||
The [2018 IEEE Cybersecurity Development Conference](https://secdev.ieee.org/2018/home) included a [full tutorial](https://github.com/trailofbits/publications/tree/master/workshops/DeepState:%20Bringing%20vulnerability%20detection%20tools%20into%20the%20development%20lifecycle%20-%20SecDev%202018) on effective use of DeepState.
|
||||
|
||||
## Overview of Features
|
||||
|
||||
@ -18,6 +18,7 @@ The [2018 IEEE Cybersecurity Development Conference](https://secdev.ieee.org/201
|
||||
* Find out which tool works best for your code under test
|
||||
* Different tools find different bugs/vulnerabilities
|
||||
* Fair way to benchmark/bakeoff tools
|
||||
* Provides test replay for regression plus effective automatic test case reduction to aid debugging
|
||||
* Supports API-sequence generation with extensions to Google Test interface
|
||||
* Concise readable way (OneOf) to say "run one of these blocks of code"
|
||||
* Same construct supports fixed value set non-determinism
|
||||
@ -28,7 +29,9 @@ The [2018 IEEE Cybersecurity Development Conference](https://secdev.ieee.org/201
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
DeepState currently targets Linux, with macOS support in progress.
|
||||
DeepState currently targets Linux, with macOS support in progress
|
||||
(the fuzzers work fine, but symbolic execution is not well-supported
|
||||
yet, without a painful cross-compilation process).
|
||||
|
||||
## Dependencies
|
||||
|
||||
@ -85,19 +88,215 @@ deepstate-angr --num_workers 4 --output_test_dir out $DEEPSTATE/build/examples/I
|
||||
└── f1d3ff8443297732862df21dc4e57262.pass
|
||||
```
|
||||
|
||||
To run these tests, you can just use the native executable, e.g.:
|
||||
|
||||
```shell
|
||||
$DEEPSTATE/build/examples/IntegerOverflow --input_test_dir out
|
||||
```
|
||||
|
||||
to run all the generated tests, or
|
||||
|
||||
```shell
|
||||
$DEEPSTATE/build/examples/IntegerOverflow --input_test_files_dir out/IntegerOverflow.cpp/SignedInteger_AdditionOverflow --input_which_test SignedInteger_AdditionOverflow
|
||||
```
|
||||
|
||||
to run the tests in one directory (in this case, you want to specify
|
||||
which test to run, also). You can also run a single test, e.g.:
|
||||
|
||||
```shell
|
||||
$DEEPSTATE/build/examples/IntegerOverflow --input_test_file out/IntegerOverflow.cpp/SignedInteger_AdditionOverflow/a512f8ffb2c1bb775a9779ec60b699cb.fail--input_which_test SignedInteger_AdditionOverflow
|
||||
```
|
||||
|
||||
In the absence of an `--input_which_test` argument, DeepState defaults
|
||||
to the last-defined test. Run the native executable with the `--help`
|
||||
argument to see all DeepState options.
|
||||
|
||||
## Usage
|
||||
|
||||
DeepState consists of a static library, used to write test harnesses, and command-line _executors_ written in Python. At this time, the best documentation is in the [examples](/examples) and in our [paper](https://agroce.github.io/bar18.pdf).
|
||||
DeepState consists of a static library, used to write test harnesses, and command-line _executors_ written in Python. At this time, the best documentation is in the [examples](/examples) and in our [paper](https://agroce.github.io/bar18.pdf). A more extensive example, using DeepState and libFuzzer to test a user-mode file system, is available [here](https://github.com/agroce/testfs); in particular the [Tests.cpp](https://github.com/agroce/testfs/blob/master/Tests.cpp) file and CMakeLists.txt show DeepState usage.
|
||||
|
||||
## Example Code
|
||||
|
||||
```
|
||||
#include <deepstate/DeepState.hpp>
|
||||
|
||||
using namespace deepstate;
|
||||
|
||||
/* Simple, buggy, run-length encoding that creates "human readable"
|
||||
* encodings by adding 'A'-1 to the count, and splitting at 26.
|
||||
* e.g., encode("aaabbbbbc") = "aCbEcA" since C=3 and E=5 */
|
||||
|
||||
char* encode(const char* input) {
|
||||
unsigned int len = strlen(input);
|
||||
char* encoded = (char*)malloc((len*2)+1);
|
||||
int pos = 0;
|
||||
if (len > 0) {
|
||||
unsigned char last = input[0];
|
||||
int count = 1;
|
||||
for (int i = 1; i < len; i++) {
|
||||
if (((unsigned char)input[i] == last) && (count < 26))
|
||||
count++;
|
||||
else {
|
||||
encoded[pos++] = last;
|
||||
encoded[pos++] = 64 + count;
|
||||
last = (unsigned char)input[i];
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
encoded[pos++] = last;
|
||||
encoded[pos++] = 65; // Should be 64 + count
|
||||
}
|
||||
encoded[pos] = '\0';
|
||||
return encoded;
|
||||
}
|
||||
|
||||
char* decode(const char* output) {
|
||||
unsigned int len = strlen(output);
|
||||
char* decoded = (char*)malloc((len/2)*26);
|
||||
int pos = 0;
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
for (int j = 0; j < (output[i+1] - 64); j++) {
|
||||
decoded[pos++] = output[i];
|
||||
}
|
||||
}
|
||||
decoded[pos] = '\0';
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// Can be (much) higher (e.g., > 1024) if we're using fuzzing, not symbolic execution
|
||||
#define MAX_STR_LEN 6
|
||||
|
||||
TEST(Runlength, BoringUnitTest) {
|
||||
ASSERT_EQ(strcmp(encode(""), ""), 0);
|
||||
ASSERT_EQ(strcmp(encode("a"), "aA"), 0);
|
||||
ASSERT_EQ(strcmp(encode("aaabbbbbc"), "aCbEcA"), 0);
|
||||
}
|
||||
|
||||
TEST(Runlength, EncodeDecode) {
|
||||
char* original = DeepState_CStrUpToLen(MAX_STR_LEN, "abcdef0123456789");
|
||||
char* encoded = encode(original);
|
||||
ASSERT_LE(strlen(encoded), strlen(original)*2) << "Encoding is > length*2!";
|
||||
char* roundtrip = decode(encoded);
|
||||
ASSERT_EQ(strncmp(roundtrip, original, MAX_STR_LEN), 0) <<
|
||||
"ORIGINAL: '" << original << "', ENCODED: '" << encoded <<
|
||||
"', ROUNDTRIP: '" << roundtrip << "'";
|
||||
}
|
||||
```
|
||||
|
||||
The code above (which can be found
|
||||
[here](https://github.com/trailofbits/deepstate/blob/master/examples/Runlen.cpp))
|
||||
shows an example of a DeepState test harness. Most of the code is
|
||||
just the functions to be tested. Using DeepState to test them requires:
|
||||
|
||||
- Including the DeepState C++ header and using the DeepState namespace
|
||||
|
||||
- Defining at least one TEST, with names
|
||||
|
||||
- Calling some DeepState APIs that produce data
|
||||
- In this example, we see the `DeepState_CStrUpToLen` call tells
|
||||
DeepState to produce a string that has up the `MAX_STR_LEN`
|
||||
characters, chosen from those present in hex strings.
|
||||
|
||||
- Optionally making some assertions about the correctness of the
|
||||
results
|
||||
- In `Runlen.cpp` this is the `ASSERT_LE` and `ASSERT_EQ` checks.
|
||||
- In the absence of any properties to check, DeepState can still
|
||||
look for memory safety violations, crashes, and other general
|
||||
categories of undesirable behavior, like any fuzzer.
|
||||
|
||||
DeepState will also run the "BoringUnitTest," but it (like a
|
||||
traditional hand-written unit test) is simply a test of fixed inputs
|
||||
devised by a programmer. These inputs do not expose the bug in
|
||||
`encode`. Nor do the default values for the DeepState test:
|
||||
|
||||
```
|
||||
~/deepstate/build/examples$ ./Runlen
|
||||
TRACE: Running: Runlength_EncodeDecode from /Users/alex/deepstate/examples/Runlen.cpp(55)
|
||||
TRACE: Passed: Runlength_EncodeDecode
|
||||
TRACE: Running: Runlength_BoringUnitTest from /Users/alex/deepstate/examples/Runlen.cpp(49)
|
||||
TRACE: Passed: Runlength_BoringUnitTest
|
||||
```
|
||||
|
||||
Using DeepState, however, it is easy to find the bug. Just
|
||||
go into the `$DEEPSTATE/build/examples` directory and try:
|
||||
|
||||
```shell
|
||||
deepstate-angr ./Runlen
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```shell
|
||||
./Runlen --fuzz --abort_on_fail
|
||||
```
|
||||
|
||||
The fuzzer will output something like:
|
||||
|
||||
```
|
||||
INFO: Starting fuzzing
|
||||
WARNING: No seed provided; using 1546631311
|
||||
WARNING: No test specified, defaulting to last test defined (Runlength_EncodeDecode)
|
||||
CRITICAL: /Users/alex/deepstate/examples/Runlen.cpp(60): ORIGINAL: '91c499', ENCODED: '9A1AcA4A9A', ROUNDTRIP: '91c49'
|
||||
ERROR: Failed: Runlength_EncodeDecode
|
||||
```
|
||||
|
||||
## Log Levels
|
||||
|
||||
By default, DeepState is not very verbose about testing activity,
|
||||
other than failing tests. The `--log_level` argument lowers the
|
||||
threshold for output, with 0 = `DEBUG`, 1 = `TRACE` (output from the
|
||||
tests, including from `printf`), 2 = INFO (DeepState messages, the default), 3 = `WARNING`,
|
||||
4 = `ERROR`, 5 = `EXTERNAL` (output from other programs such as
|
||||
libFuzzer), and 6 = `CRITICAL` messages. Lowering the `log_level` can be very
|
||||
useful for understanding what a DeepState harness is actually doing;
|
||||
often, setting `--log_level 1` in either fuzzing or symbolic
|
||||
execution will give sufficient information to debug your test harness.
|
||||
|
||||
## A Note on Mac OS and Forking
|
||||
|
||||
Normally, when running a test for replay or fuzzing, DeepState forks
|
||||
in order to cleanly handle crashes of a test. Unfortunately, `fork()`
|
||||
on mac OS is extremely slow. When using the built-in fuzzer or
|
||||
replaying tests, it is highly recommended to add the `--no_fork`
|
||||
option on mac OS, unless you need the added crash handling (that is,
|
||||
things aren't working without that option).
|
||||
|
||||
## Built-In Fuzzer
|
||||
|
||||
Every DeepState executable provides a simple built-in fuzzer that
|
||||
generates tests using completely random data. Using this fuzzer is as
|
||||
simple as calling the native executable with the `--fuzz` argument.
|
||||
The fuzzer also takes a `seed` and `timeout` (default of two minutes)
|
||||
to control the fuzzing. By default fuzzing saves
|
||||
only failing and crashing tests, and these only when given an output
|
||||
directory. If you want to actually save the test cases
|
||||
generated, you need to add a `--output_test_dir` argument to tell
|
||||
DeepState where to put the generated tests, and if you want the
|
||||
(totally random and unlikely to be high-quality) passing tests, you
|
||||
need to add `--fuzz_save_passing`.
|
||||
|
||||
Note that while symbolic execution only works on Linux, without a
|
||||
fairly complex cross-compilation process, the brute force fuzzer works
|
||||
on macOS or (as far as we know) any Unix-like system.
|
||||
|
||||
## A Note on Mac OS and Forking
|
||||
|
||||
Normally, when running a test for replay or fuzzing, DeepState forks
|
||||
in order to cleanly handle crashes of a test. Unfortunately, `fork()`
|
||||
on mac OS is _extremely_ slow. When using the built-in fuzzer or
|
||||
replaying more than a few tests, it is highly recommended to add the `--no_fork`
|
||||
option on mac OS, unless you need the added crash handling (that is,
|
||||
only when things aren't working without that option).
|
||||
|
||||
## Fuzzing with libFuzzer
|
||||
|
||||
If you install clang 6.0 or later, and run `cmake` when you install
|
||||
with the `BUILD_LIBFUZZER` environment variable defined, you can
|
||||
generate tests using LlibFuzzer. Because both DeepState and libFuzzer
|
||||
generate tests using libFuzzer. Because both DeepState and libFuzzer
|
||||
want to be `main`, this requires building a different executable for
|
||||
libFuzzer. The `examples` directory shows how this can be done. The
|
||||
libFuzzer executable works like any other libFuzzer executable, and
|
||||
the tests produced can be run using the normal DeepState executable.
|
||||
the tests produced can be replayed using the normal DeepState executable.
|
||||
For example, generating some tests of the `OneOf` example (up to 5,000
|
||||
runs), then running those tests to examine the results, would look
|
||||
like:
|
||||
@ -119,7 +318,74 @@ corpus, but fuzzing will work even without an initial corpus, unlike AFL.
|
||||
One hint when using libFuzzer is to avoid dynamically allocating
|
||||
memory during a test, if that memory would not be freed on a test
|
||||
failure. This will leak memory and libFuzzer will run out of memory
|
||||
very quickly in each fuzzing session.
|
||||
very quickly in each fuzzing session. Using libFuzzer on macOS
|
||||
requires compiling DeepState and your program with a clang that
|
||||
supports libFuzzer (which the Apple built-in probably won't); this can be as simple as doing:
|
||||
|
||||
```shell
|
||||
brew install llvm@6
|
||||
CC=/usr/local/opt/llvm\@6/bin/clang CXX=/usr/local/opt/llvm\@6/bin/clang++ BUILD_LIBFUZZER=TRUE cmake ..
|
||||
make install
|
||||
```
|
||||
|
||||
On mac OS, libFuzzer's normal output is not visible. On any platform,
|
||||
you can see more about what DeepState under libFuzzer is doing by
|
||||
setting the `LIBFUZZER_LOUD` environment variable.
|
||||
|
||||
Other ways of getting an appropriate LLVM may also work.
|
||||
|
||||
## Test case reduction
|
||||
|
||||
While tests generated by symbolic execution are likely to be highly
|
||||
concise already, fuzzer-generated tests may be much larger than they
|
||||
need to be.
|
||||
|
||||
DeepState provides a test case reducer to shrink tests intelligently,
|
||||
aware of the structure of a DeepState test. For example, if your
|
||||
executable is named `TestFileSystem` and the test you want to reduce
|
||||
is named `rmdirfail.test` you would use it like this:
|
||||
|
||||
```shell
|
||||
deepstate-reduce ./TestFileSystem rmdirfail.test minrmdirfail.test
|
||||
```
|
||||
|
||||
In many cases, this will result in finding a different failure or
|
||||
crash that allows smaller test cases, so you can also provide a string
|
||||
that controls the criteria for which test outputs are considered valid
|
||||
reductions (by default, the reducer looks for any test that fails or
|
||||
crashes). Only outputs containing the `--criteria` are considered to
|
||||
be valid reductions:
|
||||
|
||||
```shell
|
||||
deepstate-reduce ./TestFileSystem rmdirfail.test minrmdirfail.test --criteria "FATAL: /root/testfs/super.c(252)"
|
||||
```
|
||||
|
||||
The output will look something like:
|
||||
|
||||
```
|
||||
ORIGINAL TEST HAS 119 BYTES
|
||||
ONEOF REMOVAL REDUCED TEST TO 103 BYTES
|
||||
ONEOF REMOVAL REDUCED TEST TO 87 BYTES
|
||||
ONEOF REMOVAL REDUCED TEST TO 67 BYTES
|
||||
ONEOF REMOVAL REDUCED TEST TO 51 BYTES
|
||||
BYTE RANGE REMOVAL REDUCED TEST TO 50 BYTES
|
||||
BYTE RANGE REMOVAL REDUCED TEST TO 49 BYTES
|
||||
BYTE REDUCTION: BYTE 3 FROM 4 TO 0
|
||||
BYTE REDUCTION: BYTE 43 FROM 4 TO 0
|
||||
ONEOF REMOVAL REDUCED TEST TO 33 BYTES
|
||||
ONEOF REMOVAL REDUCED TEST TO 17 BYTES
|
||||
BYTE REDUCTION: BYTE 7 FROM 2 TO 1
|
||||
BYTE REDUCTION: BYTE 15 FROM 2 TO 1
|
||||
NO REDUCTIONS FOUND
|
||||
PADDING TEST WITH 3 ZEROS
|
||||
|
||||
WRITING REDUCED TEST WITH 20 BYTES TO minrmdirfail.test
|
||||
```
|
||||
|
||||
You can use `--which_test <testname>` to specify which test to
|
||||
run, as with the `--input_which_test` options to test replay.
|
||||
|
||||
Test case reduction should work on any OS.
|
||||
|
||||
## Fuzzing with AFL
|
||||
|
||||
@ -174,6 +440,10 @@ deferred instrumentation. You'll need code like:
|
||||
just before the call to `DeepState_Run()` (which reads the entire
|
||||
input file) in your `main`.
|
||||
|
||||
Because AFL and other file-based fuzzers only rely on the DeepState
|
||||
native test executable, they should (like DeepState's built-in simple
|
||||
fuzzer) work fine on macOS and other Unix-like OSes.
|
||||
|
||||
## Contributing
|
||||
|
||||
All accepted PRs are awarded bounties by Trail of Bits. Join the #deepstate channel on the [Empire Hacking Slack](https://empireslacking.herokuapp.com/) to discuss ongoing development and claim bounties. Check the [good first issue](https://github.com/trailofbits/deepstate/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label for suggested contributions.
|
||||
|
||||
@ -14,10 +14,12 @@
|
||||
|
||||
import logging
|
||||
logging.basicConfig()
|
||||
logging.addLevelName(15, "TRACE")
|
||||
|
||||
import argparse
|
||||
#import md5
|
||||
import hashlib
|
||||
import functools
|
||||
import os
|
||||
import struct
|
||||
|
||||
@ -33,19 +35,23 @@ class TestInfo(object):
|
||||
|
||||
|
||||
LOG_LEVEL_DEBUG = 0
|
||||
LOG_LEVEL_INFO = 1
|
||||
LOG_LEVEL_WARNING = 2
|
||||
LOG_LEVEL_ERROR = 3
|
||||
LOG_LEVEL_EXTERNAL = 4
|
||||
LOG_LEVEL_FATAL = 5
|
||||
LOG_LEVEL_TRACE = 1
|
||||
LOG_LEVEL_INFO = 2
|
||||
LOG_LEVEL_WARNING = 3
|
||||
LOG_LEVEL_ERROR = 4
|
||||
LOG_LEVEL_EXTERNAL = 5
|
||||
LOG_LEVEL_FATAL = 6
|
||||
|
||||
|
||||
LOGGER = logging.getLogger("deepstate")
|
||||
LOGGER.setLevel(logging.DEBUG)
|
||||
|
||||
LOGGER.trace = functools.partial(LOGGER.log, 15)
|
||||
logging.TRACE = 15
|
||||
|
||||
LOG_LEVEL_TO_LOGGER = {
|
||||
LOG_LEVEL_DEBUG: LOGGER.debug,
|
||||
LOG_LEVEL_TRACE: LOGGER.trace,
|
||||
LOG_LEVEL_INFO: LOGGER.info,
|
||||
LOG_LEVEL_WARNING: LOGGER.warning,
|
||||
LOG_LEVEL_ERROR: LOGGER.error,
|
||||
@ -88,6 +94,9 @@ class DeepState(object):
|
||||
def write_uint8_t(self, ea, val):
|
||||
raise NotImplementedError("Must be implemented by engine.")
|
||||
|
||||
def write_uint32_t(self, ea, val):
|
||||
raise NotImplementedError("Must be implemented by engine.")
|
||||
|
||||
def concretize(self, val, constrain=False):
|
||||
raise NotImplementedError("Must be implemented by engine.")
|
||||
|
||||
@ -132,8 +141,12 @@ class DeepState(object):
|
||||
|
||||
parser.add_argument(
|
||||
"--verbosity", default=1, type=int,
|
||||
help="Verbosity level.")
|
||||
help="Verbosity level for symbolic execution tool.")
|
||||
|
||||
parser.add_argument(
|
||||
"--log_level", default=2, type=int,
|
||||
help="Log level (DeepState logging).")
|
||||
|
||||
parser.add_argument(
|
||||
"binary", type=str, help="Path to the test binary to run.")
|
||||
|
||||
@ -235,7 +248,7 @@ class DeepState(object):
|
||||
self.context['stream_{}'.format(level)] = []
|
||||
|
||||
self.context['info'] = info
|
||||
self.log_message(LOG_LEVEL_INFO, "Running {} from {}({})".format(
|
||||
self.log_message(LOG_LEVEL_TRACE, "Running {} from {}({})".format(
|
||||
info.name, info.file_name, info.line_number))
|
||||
|
||||
apis = self.context['apis']
|
||||
@ -251,6 +264,17 @@ class DeepState(object):
|
||||
|
||||
# Create the output directory for this test case.
|
||||
args = self.parse_args()
|
||||
|
||||
logging_levels = {
|
||||
LOG_LEVEL_DEBUG: logging.DEBUG,
|
||||
LOG_LEVEL_TRACE: logging.TRACE,
|
||||
LOG_LEVEL_INFO: logging.INFO,
|
||||
LOG_LEVEL_WARNING: logging.WARNING,
|
||||
LOG_LEVEL_ERROR: logging.ERROR,
|
||||
LOG_LEVEL_FATAL: logging.CRITICAL
|
||||
}
|
||||
LOGGER.setLevel(logging_levels[args.log_level])
|
||||
|
||||
if args.output_test_dir is not None:
|
||||
test_dir = os.path.join(args.output_test_dir,
|
||||
os.path.basename(info.file_name),
|
||||
@ -338,21 +362,27 @@ class DeepState(object):
|
||||
md5.update(input_bytes)
|
||||
test_name = md5.hexdigest()
|
||||
|
||||
passing = False
|
||||
if self.context['failed']:
|
||||
test_name += ".fail"
|
||||
elif self.context['crashed']:
|
||||
test_name += ".crash"
|
||||
else:
|
||||
test_name += ".pass"
|
||||
passing = True
|
||||
|
||||
test_file = os.path.join(test_dir, test_name)
|
||||
LOGGER.info("Saving input to {}".format(test_file))
|
||||
try:
|
||||
with open(test_file, "wb") as f:
|
||||
f.write(input_bytes)
|
||||
except:
|
||||
LOGGER.critical("Error saving input to {}".format(test_file))
|
||||
|
||||
if not passing:
|
||||
LOGGER.info("Saved test case in file {}".format(test_file))
|
||||
else:
|
||||
LOGGER.trace("Saved test case in file {}".format(test_file))
|
||||
|
||||
def report(self):
|
||||
"""Report on the pass/fail status of a test case, and dump its log."""
|
||||
info = self.context['info']
|
||||
@ -382,7 +412,7 @@ class DeepState(object):
|
||||
# Print out the first few input bytes to be helpful.
|
||||
lots_of_bytes = len(input_bytes) > 20 and " ..." or ""
|
||||
bytes_to_show = min(20, len(input_bytes))
|
||||
LOGGER.info("Input: {}{}".format(
|
||||
LOGGER.trace("Input: {}{}".format(
|
||||
" ".join("{:02x}".format(b) for b in input_bytes[:bytes_to_show]),
|
||||
lots_of_bytes))
|
||||
|
||||
@ -491,7 +521,7 @@ class DeepState(object):
|
||||
self.api_fail()
|
||||
else:
|
||||
info = self.context['info']
|
||||
self.log_message(LOG_LEVEL_INFO, "Passed: {}".format(info.name))
|
||||
self.log_message(LOG_LEVEL_TRACE, "Passed: {}".format(info.name))
|
||||
self.pass_test()
|
||||
|
||||
def api_crash(self):
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3.6
|
||||
# Copyright (c) 2017 Trail of Bits, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -41,12 +41,12 @@ class DeepAngr(DeepState):
|
||||
return self.state.globals
|
||||
|
||||
def is_symbolic(self, val):
|
||||
if isinstance(val, (int, long)):
|
||||
if isinstance(val, (int, int)):
|
||||
return False
|
||||
return self.state.se.symbolic(val)
|
||||
return self.state.solver.symbolic(val)
|
||||
|
||||
def create_symbol(self, name, size_in_bits):
|
||||
return self.state.se.Unconstrained('name', size_in_bits)
|
||||
return self.state.solver.Unconstrained('name', size_in_bits)
|
||||
|
||||
def read_uintptr_t(self, ea, concretize=True, constrain=False):
|
||||
addr_size_bytes = self.state.arch.bits // 8
|
||||
@ -84,8 +84,12 @@ class DeepAngr(DeepState):
|
||||
self.state.memory.store(ea, val, size=1)
|
||||
return ea + 1
|
||||
|
||||
def write_uint32_t(self, ea, val):
|
||||
self.state.memory.store(ea, val, size=4)
|
||||
return ea + 4
|
||||
|
||||
def concretize(self, val, constrain=False):
|
||||
if isinstance(val, (int, long)):
|
||||
if isinstance(val, (int, int)):
|
||||
return val
|
||||
elif isinstance(val, str):
|
||||
assert len(val) == 1
|
||||
@ -98,7 +102,7 @@ class DeepAngr(DeepState):
|
||||
return concrete_val
|
||||
|
||||
def concretize_min(self, val, constrain=False):
|
||||
if isinstance(val, (int, long)):
|
||||
if isinstance(val, (int, int)):
|
||||
return val
|
||||
concrete_val = self.state.solver.min(val)
|
||||
if constrain:
|
||||
@ -106,7 +110,7 @@ class DeepAngr(DeepState):
|
||||
return concrete_val
|
||||
|
||||
def concretize_max(self, val, constrain=False):
|
||||
if isinstance(val, (int, long)):
|
||||
if isinstance(val, (int, int)):
|
||||
return val
|
||||
concrete_val = self.state.solver.max(val)
|
||||
if constrain:
|
||||
@ -115,7 +119,7 @@ class DeepAngr(DeepState):
|
||||
|
||||
def concretize_many(self, val, max_num):
|
||||
assert 0 < max_num
|
||||
if isinstance(val, (int, long)):
|
||||
if isinstance(val, (int, int)):
|
||||
return [val]
|
||||
return self.state.solver.eval_upto(val, max_num, cast_to=int)
|
||||
|
||||
@ -277,6 +281,10 @@ def do_run_test(project, test, apis, run_state, should_call_state):
|
||||
test_state = run_state
|
||||
|
||||
mc = DeepAngr(state=test_state)
|
||||
|
||||
# Tell the system that we're using symbolic execution.
|
||||
mc.write_uint32_t(apis["UsingSymExec"], 8589934591)
|
||||
|
||||
mc.begin_test(test)
|
||||
del mc
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3.6
|
||||
# Copyright (c) 2017 Trail of Bits, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -88,6 +88,10 @@ class DeepManticore(DeepState):
|
||||
self.state.cpu.write_int(ea, val, size=8)
|
||||
return ea + 1
|
||||
|
||||
def write_uint32_t(self, ea, val):
|
||||
self.state.cpu.write_int(ea, val, size=32)
|
||||
return ea + 4
|
||||
|
||||
def concretize(self, val, constrain=False):
|
||||
if isinstance(val, (int)):
|
||||
return val
|
||||
@ -336,6 +340,10 @@ def do_run_test(state, apis, test, hook_test=False):
|
||||
|
||||
state = m.initial_state
|
||||
mc = DeepManticore(state)
|
||||
|
||||
# Tell the system that we're using symbolic execution.
|
||||
mc.write_uint32_t(apis["UsingSymExec"], 8589934591)
|
||||
|
||||
mc.begin_test(test)
|
||||
del mc
|
||||
|
||||
@ -423,6 +431,7 @@ def main_takeover(m, args, takeover_symbol):
|
||||
|
||||
base = get_base(m)
|
||||
apis = mc.read_api_table(ea_of_api_table, base)
|
||||
|
||||
del mc
|
||||
|
||||
fake_test = TestInfo(takeover_ea, '_takeover_test', '_takeover_file', 0)
|
||||
|
||||
234
bin/deepstate/reducer.py
Normal file
234
bin/deepstate/reducer.py
Normal file
@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2018 Trail of Bits, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
import argparse
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Intelligently reduce test case")
|
||||
|
||||
parser.add_argument(
|
||||
"binary", type=str, help="Path to the test binary to run.")
|
||||
|
||||
parser.add_argument(
|
||||
"input_test", type=str, help="Path to test to reduce.")
|
||||
|
||||
parser.add_argument(
|
||||
"output_test", type=str, help="Path for reduced test.")
|
||||
|
||||
parser.add_argument(
|
||||
"--which_test", type=str, help="Which test to run (equivalent to --input_which_test).", default=None)
|
||||
|
||||
parser.add_argument(
|
||||
"--criteria", type=str, help="String to search for in valid reduction outputs.",
|
||||
default=None)
|
||||
|
||||
parser.add_argument(
|
||||
"--search", action="store_true", help="Allow initial test to not satisfy criteria (search for test).",
|
||||
default=None)
|
||||
|
||||
parser.add_argument(
|
||||
"--timeout", type=int, help="After this amount of time (in seconds), give up on reduction.",
|
||||
default=1200)
|
||||
|
||||
class TimeoutException(Exception):
|
||||
pass
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
deepstate = args.binary
|
||||
test = args.input_test
|
||||
out = args.output_test
|
||||
checkString = args.criteria
|
||||
whichTest = args.which_test
|
||||
|
||||
start = time.time()
|
||||
|
||||
def runCandidate(candidate):
|
||||
if (time.time() - start) > args.timeout:
|
||||
raise TimeoutException
|
||||
with open(".reducer.out", 'w') as outf:
|
||||
cmd = [deepstate + " --input_test_file " +
|
||||
candidate + " --verbose_reads"]
|
||||
if whichTest is not None:
|
||||
cmd += ["--input_which_test", whichTest]
|
||||
subprocess.call(cmd, shell=True, stdout=outf, stderr=outf)
|
||||
result = []
|
||||
with open(".reducer.out", 'r') as inf:
|
||||
for line in inf:
|
||||
result.append(line)
|
||||
return result
|
||||
|
||||
def checks(result):
|
||||
for line in result:
|
||||
if checkString:
|
||||
if checkString in line:
|
||||
return True
|
||||
else:
|
||||
if "ERROR: Failed:" in line:
|
||||
return True
|
||||
if "ERROR: Crashed" in line:
|
||||
return True
|
||||
return False
|
||||
|
||||
def writeAndRunCandidate(test):
|
||||
with open(".candidate.test", 'wb') as outf:
|
||||
outf.write(test)
|
||||
r = runCandidate(".candidate.test")
|
||||
return r
|
||||
|
||||
def structure(result):
|
||||
OneOfs = []
|
||||
currentOneOf = []
|
||||
for line in result:
|
||||
if "STARTING OneOf CALL" in line:
|
||||
currentOneOf.append(-1)
|
||||
elif "Reading byte at" in line:
|
||||
lastRead = int(line.split()[-1])
|
||||
if len(currentOneOf) > 0:
|
||||
if currentOneOf[-1] == -1:
|
||||
currentOneOf[-1] = lastRead
|
||||
elif "FINISHED OneOf CALL" in line:
|
||||
OneOfs.append((currentOneOf[-1], lastRead))
|
||||
currentOneOf = currentOneOf[:-1]
|
||||
return (OneOfs, lastRead)
|
||||
|
||||
initial = runCandidate(test)
|
||||
if (not args.search) and (not checks(initial)):
|
||||
print("STARTING TEST DOES NOT SATISFY REDUCTION CRITERIA")
|
||||
return 1
|
||||
|
||||
with open(test, 'rb') as test:
|
||||
currentTest = bytearray(test.read())
|
||||
|
||||
print("ORIGINAL TEST HAS", len(currentTest), "BYTES")
|
||||
|
||||
s = structure(initial)
|
||||
if (s[1]+1) < len(currentTest):
|
||||
print("LAST BYTE READ IS", s[1])
|
||||
print("SHRINKING TO IGNORE UNREAD BYTES")
|
||||
currentTest = currentTest[:s[1]+1]
|
||||
|
||||
changed = True
|
||||
try:
|
||||
while changed:
|
||||
changed = False
|
||||
|
||||
cuts = s[0]
|
||||
for c in cuts:
|
||||
newTest = currentTest[:c[0]] + currentTest[c[1]+1:]
|
||||
if len(newTest) == len(currentTest):
|
||||
continue # Ignore non-shrinking reductions
|
||||
r = writeAndRunCandidate(newTest)
|
||||
if checks(r):
|
||||
print("ONEOF REMOVAL REDUCED TEST TO", len(newTest), "BYTES")
|
||||
changed = True
|
||||
break
|
||||
|
||||
if not changed:
|
||||
for b in range(0, len(currentTest)):
|
||||
for v in range(b+1, len(currentTest)):
|
||||
newTest = currentTest[:b] + currentTest[v:]
|
||||
r = writeAndRunCandidate(newTest)
|
||||
if checks(r):
|
||||
print("BYTE RANGE REMOVAL REDUCED TEST TO", len(newTest), "BYTES")
|
||||
changed = True
|
||||
break
|
||||
if changed:
|
||||
break
|
||||
|
||||
if not changed:
|
||||
for b in range(0, len(currentTest)):
|
||||
for v in range(0, currentTest[b]):
|
||||
newTest = bytearray(currentTest)
|
||||
newTest[b] = v
|
||||
r = writeAndRunCandidate(newTest)
|
||||
if checks(r):
|
||||
print("BYTE REDUCTION: BYTE", b, "FROM", currentTest[b], "TO", v)
|
||||
changed = True
|
||||
break
|
||||
if changed:
|
||||
break
|
||||
|
||||
if not changed:
|
||||
for b in range(0, len(currentTest)):
|
||||
if currentTest[b] == 0:
|
||||
continue
|
||||
newTest = bytearray(currentTest)
|
||||
newTest[b] = currentTest[b]-1
|
||||
newTest = newTest[:b+1] + newTest[b+2:]
|
||||
r = writeAndRunCandidate(newTest)
|
||||
if checks(r):
|
||||
print("BYTE REDUCE AND DELETE AT BYTE", b)
|
||||
changed = True
|
||||
break
|
||||
|
||||
if not changed:
|
||||
for b1 in range(0, len(currentTest)-4):
|
||||
for b2 in range(b1+2, len(currentTest)-4):
|
||||
v1 = (currentTest[b1], currentTest[b1+1])
|
||||
v2 = (currentTest[b2], currentTest[b2+1])
|
||||
if (v1 == v2):
|
||||
ba = bytearray(v1)
|
||||
part1 = currentTest[:b1]
|
||||
part2 = currentTest[b1+2:b2]
|
||||
part3 = currentTest[b2+2:]
|
||||
banews = []
|
||||
banews.append(ba[0:1])
|
||||
banews.append(ba[1:2])
|
||||
if ba[0] > 0:
|
||||
for v in range(0, ba[0]):
|
||||
banews.append(bytearray([v, ba[1]]))
|
||||
banews.append(bytearray([ba[0]-1]))
|
||||
if ba[1] > 0:
|
||||
for v in range(0, ba[1]):
|
||||
banews.append(bytearray([ba[0], v]))
|
||||
for banew in banews:
|
||||
newTest = part1 + banew + part2 + banew + part3
|
||||
r = writeAndRunCandidate(newTest)
|
||||
if checks(r):
|
||||
print("BYTE PATTERN", tuple(ba), "AT", b1, "AND", b2, "CHANGED TO", tuple(banew))
|
||||
changed = True
|
||||
break
|
||||
if changed:
|
||||
break
|
||||
if changed:
|
||||
break
|
||||
|
||||
if changed:
|
||||
currentTest = newTest
|
||||
s = structure(r)
|
||||
else:
|
||||
print("NO (MORE) REDUCTIONS FOUND")
|
||||
except TimeoutException:
|
||||
print("REDUCTION TIMED OUT AFTER", args.timeout, "SECONDS")
|
||||
|
||||
if (s[1] + 1) > len(currentTest):
|
||||
print("PADDING TEST WITH", (s[1] + 1) - len(currentTest), "ZEROS")
|
||||
padding = bytearray('\x00' * ((s[1] + 1) - len(currentTest)))
|
||||
currentTest = currentTest + padding
|
||||
|
||||
print()
|
||||
print("WRITING REDUCED TEST WITH", len(currentTest), "BYTES TO", out)
|
||||
|
||||
with open(out, 'wb') as outf:
|
||||
outf.write(currentTest)
|
||||
|
||||
return 0
|
||||
|
||||
if "__main__" == __name__:
|
||||
exit(main())
|
||||
@ -34,7 +34,8 @@ setuptools.setup(
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'deepstate = deepstate.main_manticore:main',
|
||||
#'deepstate-angr = deepstate.main_angr:main',
|
||||
'deepstate-angr = deepstate.main_angr:main',
|
||||
'deepstate-manticore = deepstate.main_manticore:main',
|
||||
'deepstate-reduce = deepstate.reducer:main',
|
||||
]
|
||||
})
|
||||
|
||||
@ -12,14 +12,13 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
add_executable(Crash Crash.cpp)
|
||||
target_link_libraries(Crash deepstate)
|
||||
|
||||
if (BUILD_LIBFUZZER)
|
||||
add_executable(Crash_LF Crash.cpp)
|
||||
target_link_libraries(Crash_LF deepstate_LF)
|
||||
target_link_libraries (Crash_LF "-fsanitize=fuzzer")
|
||||
target_link_libraries (Crash_LF "-fsanitize=fuzzer,undefined")
|
||||
set_target_properties(Crash_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER")
|
||||
endif()
|
||||
|
||||
@ -29,7 +28,7 @@ target_link_libraries(OneOf deepstate)
|
||||
if (BUILD_LIBFUZZER)
|
||||
add_executable(OneOf_LF OneOf.cpp)
|
||||
target_link_libraries(OneOf_LF deepstate_LF)
|
||||
target_link_libraries (OneOf_LF "-fsanitize=fuzzer")
|
||||
target_link_libraries (OneOf_LF "-fsanitize=fuzzer,undefined")
|
||||
set_target_properties(OneOf_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER")
|
||||
endif()
|
||||
|
||||
@ -39,7 +38,7 @@ target_link_libraries(Fixture deepstate)
|
||||
if (BUILD_LIBFUZZER)
|
||||
add_executable(Fixture_LF Fixture.cpp)
|
||||
target_link_libraries(Fixture_LF deepstate_LF)
|
||||
target_link_libraries (Fixture_LF "-fsanitize=fuzzer")
|
||||
target_link_libraries (Fixture_LF "-fsanitize=fuzzer,undefined")
|
||||
set_target_properties(Fixture_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER")
|
||||
endif()
|
||||
|
||||
@ -49,7 +48,7 @@ target_link_libraries(Primes deepstate)
|
||||
if (BUILD_LIBFUZZER)
|
||||
add_executable(Primes_LF Primes.cpp)
|
||||
target_link_libraries(Primes_LF deepstate_LF)
|
||||
target_link_libraries (Primes_LF "-fsanitize=fuzzer")
|
||||
target_link_libraries (Primes_LF "-fsanitize=fuzzer,undefined")
|
||||
set_target_properties(Primes_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER")
|
||||
endif()
|
||||
|
||||
@ -59,7 +58,7 @@ target_link_libraries(Euler deepstate)
|
||||
if (BUILD_LIBFUZZER)
|
||||
add_executable(Euler_LF Euler.cpp)
|
||||
target_link_libraries(Euler_LF deepstate_LF)
|
||||
target_link_libraries (Euler_LF "-fsanitize=fuzzer")
|
||||
target_link_libraries (Euler_LF "-fsanitize=fuzzer,undefined")
|
||||
set_target_properties(Euler_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER")
|
||||
endif()
|
||||
|
||||
@ -69,7 +68,7 @@ target_link_libraries(IntegerOverflow deepstate)
|
||||
if (BUILD_LIBFUZZER)
|
||||
add_executable(IntegerOverflow_LF IntegerOverflow.cpp)
|
||||
target_link_libraries(IntegerOverflow_LF deepstate_LF)
|
||||
target_link_libraries (IntegerOverflow_LF "-fsanitize=fuzzer")
|
||||
target_link_libraries (IntegerOverflow_LF "-fsanitize=fuzzer,undefined")
|
||||
set_target_properties(IntegerOverflow_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER")
|
||||
endif()
|
||||
|
||||
@ -79,7 +78,7 @@ target_link_libraries(IntegerArithmetic deepstate)
|
||||
if (BUILD_LIBFUZZER)
|
||||
add_executable(IntegerArithmetic_LF IntegerArithmetic.cpp)
|
||||
target_link_libraries(IntegerArithmetic_LF deepstate_LF)
|
||||
target_link_libraries (IntegerArithmetic_LF "-fsanitize=fuzzer")
|
||||
target_link_libraries (IntegerArithmetic_LF "-fsanitize=fuzzer,undefined")
|
||||
set_target_properties(IntegerArithmetic_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER")
|
||||
endif()
|
||||
|
||||
@ -89,7 +88,7 @@ target_link_libraries(Lists deepstate)
|
||||
if (BUILD_LIBFUZZER)
|
||||
add_executable(Lists_LF Lists.cpp)
|
||||
target_link_libraries(Lists_LF deepstate_LF)
|
||||
target_link_libraries (Lists_LF "-fsanitize=fuzzer")
|
||||
target_link_libraries (Lists_LF "-fsanitize=fuzzer,undefined")
|
||||
set_target_properties(Lists_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER")
|
||||
endif()
|
||||
|
||||
@ -99,13 +98,25 @@ target_link_libraries(StreamingAndFormatting deepstate)
|
||||
if (BUILD_LIBFUZZER)
|
||||
add_executable(StreamingAndFormatting_LF StreamingAndFormatting.cpp)
|
||||
target_link_libraries(StreamingAndFormatting_LF deepstate_LF)
|
||||
target_link_libraries (StreamingAndFormatting_LF "-fsanitize=fuzzer")
|
||||
target_link_libraries (StreamingAndFormatting_LF "-fsanitize=fuzzer,undefined")
|
||||
set_target_properties(StreamingAndFormatting_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER")
|
||||
endif()
|
||||
|
||||
add_executable(Squares Squares.c)
|
||||
target_link_libraries(Squares deepstate)
|
||||
set_target_properties(Squares PROPERTIES COMPILE_DEFINITIONS "DEEPSTATE_TEST")
|
||||
add_executable(Runlen Runlen.cpp)
|
||||
target_link_libraries(Runlen deepstate)
|
||||
|
||||
if (BUILD_LIBFUZZER)
|
||||
add_executable(Runlen_LF Runlen.cpp)
|
||||
target_link_libraries(Runlen_LF deepstate_LF)
|
||||
target_link_libraries (Runlen_LF "-fsanitize=fuzzer,undefined")
|
||||
set_target_properties(Runlen_LF PROPERTIES COMPILE_DEFINITIONS "LIBFUZZER")
|
||||
endif()
|
||||
|
||||
if (NOT APPLE)
|
||||
add_executable(Squares Squares.c)
|
||||
target_link_libraries(Squares deepstate)
|
||||
set_target_properties(Squares PROPERTIES COMPILE_DEFINITIONS "DEEPSTATE_TEST")
|
||||
endif()
|
||||
|
||||
add_executable(TakeOver TakeOver.cpp)
|
||||
target_link_libraries(TakeOver deepstate)
|
||||
|
||||
@ -22,7 +22,7 @@ using namespace deepstate;
|
||||
DEEPSTATE_NOINLINE static unsigned segfault(unsigned x) {
|
||||
if (x == 0x1234) { // Magic number for engine to discover
|
||||
unsigned *p = NULL;
|
||||
*p = 0xdeadbeef; // Trigger segfault here
|
||||
*(p+1) = 0xdeadbeef; // Trigger segfault here
|
||||
}
|
||||
|
||||
return x;
|
||||
@ -35,10 +35,3 @@ TEST(Crash, SegFault) {
|
||||
|
||||
ASSERT_EQ(x, x);
|
||||
}
|
||||
|
||||
#ifndef LIBFUZZER
|
||||
int main(int argc, char *argv[]) {
|
||||
DeepState_InitOptions(argc, argv);
|
||||
return DeepState_Run();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -39,9 +39,3 @@ TEST(Euler, SumsOfLikePowers) {
|
||||
<< "^5 + " << d << "^5 = " << e << "^5";
|
||||
}
|
||||
|
||||
#ifndef LIBFUZZER
|
||||
int main(int argc, char *argv[]) {
|
||||
DeepState_InitOptions(argc, argv);
|
||||
return DeepState_Run();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -20,12 +20,12 @@ class MyTest : public deepstate::Test {
|
||||
public:
|
||||
|
||||
void SetUp(void) {
|
||||
LOG(INFO)
|
||||
LOG(TRACE)
|
||||
<< "Setting up!";
|
||||
}
|
||||
|
||||
void TearDown(void) {
|
||||
LOG(INFO)
|
||||
LOG(TRACE)
|
||||
<< "Tearing down!";
|
||||
}
|
||||
|
||||
@ -35,10 +35,3 @@ class MyTest : public deepstate::Test {
|
||||
TEST_F(MyTest, Something) {
|
||||
ASSUME_NE(x, 0);
|
||||
}
|
||||
|
||||
#ifndef LIBFUZZER
|
||||
int main(int argc, char *argv[]) {
|
||||
DeepState_InitOptions(argc, argv);
|
||||
return DeepState_Run();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -44,10 +44,3 @@ TEST(Arithmetic, InvertibleMultiplication_CanFail) {
|
||||
<< x << " != (" << x << " / " << y << ") * " << y;
|
||||
});
|
||||
}
|
||||
|
||||
#ifndef LIBFUZZER
|
||||
int main(int argc, char *argv[]) {
|
||||
DeepState_InitOptions(argc, argv);
|
||||
return DeepState_Run();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <deepstate/DeepState.hpp>
|
||||
|
||||
#include <climits>
|
||||
using namespace deepstate;
|
||||
|
||||
DEEPSTATE_NOINLINE int ident1(int x) {
|
||||
@ -40,9 +40,3 @@ TEST(SignedInteger, MultiplicationOverflow) {
|
||||
<< x << " squared overflowed.";
|
||||
}
|
||||
|
||||
#ifndef LIBFUZZER
|
||||
int main(int argc, char *argv[]) {
|
||||
DeepState_InitOptions(argc, argv);
|
||||
return DeepState_Run();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -30,10 +30,3 @@ TEST(Vector, DoubleReversal) {
|
||||
<< "Double reverse of vectors must be equal.";
|
||||
});
|
||||
}
|
||||
|
||||
#ifndef LIBFUZZER
|
||||
int main(int argc, char *argv[]) {
|
||||
DeepState_InitOptions(argc, argv);
|
||||
DeepState_Run();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -63,10 +63,3 @@ TEST(OneOfExample, ProduceSixtyOrHigher) {
|
||||
<< x << " is >= 60: " << " did " << choices << " from " << start;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef LIBFUZZER
|
||||
int main(int argc, char *argv[]) {
|
||||
DeepState_InitOptions(argc, argv);
|
||||
return DeepState_Run();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -52,10 +52,3 @@ TEST(PrimePolynomial, OnlyGeneratesPrimes_NoStreaming) {
|
||||
DeepState_Assert(poly != (y * z));
|
||||
DeepState_Assert(IsPrime(Pump(poly)));
|
||||
}
|
||||
|
||||
#ifndef LIBFUZZER
|
||||
int main(int argc, char *argv[]) {
|
||||
DeepState_InitOptions(argc, argv);
|
||||
return DeepState_Run();
|
||||
}
|
||||
#endif
|
||||
|
||||
63
examples/Runlen.cpp
Normal file
63
examples/Runlen.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#include <deepstate/DeepState.hpp>
|
||||
|
||||
using namespace deepstate;
|
||||
|
||||
/* Simple, buggy, run-length encoding that creates "human readable"
|
||||
* encodings by adding 'A'-1 to the count, and splitting at 26.
|
||||
* e.g., encode("aaabbbbbc") = "aCbEcA" since C=3 and E=5 */
|
||||
|
||||
char* encode(const char* input) {
|
||||
unsigned int len = strlen(input);
|
||||
char* encoded = (char*)malloc((len*2)+1);
|
||||
int pos = 0;
|
||||
if (len > 0) {
|
||||
unsigned char last = input[0];
|
||||
int count = 1;
|
||||
for (int i = 1; i < len; i++) {
|
||||
if (((unsigned char)input[i] == last) && (count < 26))
|
||||
count++;
|
||||
else {
|
||||
encoded[pos++] = last;
|
||||
encoded[pos++] = 64 + count;
|
||||
last = (unsigned char)input[i];
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
encoded[pos++] = last;
|
||||
encoded[pos++] = 65; // Should be 64 + count
|
||||
}
|
||||
encoded[pos] = '\0';
|
||||
return encoded;
|
||||
}
|
||||
|
||||
char* decode(const char* output) {
|
||||
unsigned int len = strlen(output);
|
||||
char* decoded = (char*)malloc((len/2)*26);
|
||||
int pos = 0;
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
for (int j = 0; j < (output[i+1] - 64); j++) {
|
||||
decoded[pos++] = output[i];
|
||||
}
|
||||
}
|
||||
decoded[pos] = '\0';
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// Can be (much) higher (e.g., > 1024) if we're using fuzzing, not symbolic execution
|
||||
#define MAX_STR_LEN 6
|
||||
|
||||
TEST(Runlength, BoringUnitTest) {
|
||||
ASSERT_EQ(strcmp(encode(""), ""), 0);
|
||||
ASSERT_EQ(strcmp(encode("a"), "aA"), 0);
|
||||
ASSERT_EQ(strcmp(encode("aaabbbbbc"), "aCbEcA"), 0);
|
||||
}
|
||||
|
||||
TEST(Runlength, EncodeDecode) {
|
||||
char* original = DeepState_CStrUpToLen(MAX_STR_LEN, "abcdef0123456789");
|
||||
char* encoded = encode(original);
|
||||
ASSERT_LE(strlen(encoded), strlen(original)*2) << "Encoding is > length*2!";
|
||||
char* roundtrip = decode(encoded);
|
||||
ASSERT_EQ(strncmp(roundtrip, original, MAX_STR_LEN), 0) <<
|
||||
"ORIGINAL: '" << original << "', ENCODED: '" << encoded <<
|
||||
"', ROUNDTRIP: '" << roundtrip << "'";
|
||||
}
|
||||
@ -10,7 +10,7 @@ int square(int x) {
|
||||
DeepState_EntryPoint(test_main) {
|
||||
const char *new_args[2];
|
||||
new_args[0] = "deepstate";
|
||||
new_args[1] = DeepState_CStr(8);
|
||||
new_args[1] = DeepState_CStr_C(8, 0);
|
||||
|
||||
DeepState_Assert(0 == old_main(2, new_args));
|
||||
}
|
||||
|
||||
@ -18,19 +18,20 @@
|
||||
|
||||
TEST(Streaming, BasicLevels) {
|
||||
LOG(DEBUG) << "This is a debug message";
|
||||
LOG(INFO) << "This is an info message";
|
||||
LOG(TRACE) << "This is a trace message";
|
||||
LOG(INFO) << "This is an info message";
|
||||
LOG(WARNING) << "This is a warning message";
|
||||
LOG(ERROR) << "This is a error message";
|
||||
LOG(INFO) << "This is a info message again";
|
||||
LOG(TRACE) << "This is a trace message again";
|
||||
ASSERT(true) << "This should not be printed.";
|
||||
}
|
||||
|
||||
TEST(Streaming, BasicTypes) {
|
||||
LOG(INFO) << 'a';
|
||||
LOG(INFO) << 1;
|
||||
LOG(INFO) << 1.0;
|
||||
LOG(INFO) << "string";
|
||||
LOG(INFO) << nullptr;
|
||||
LOG(TRACE) << 'a';
|
||||
LOG(TRACE) << 1;
|
||||
LOG(TRACE) << 1.0;
|
||||
LOG(TRACE) << "string";
|
||||
LOG(TRACE) << nullptr;
|
||||
}
|
||||
|
||||
TEST(Formatting, OverridePrintf) {
|
||||
@ -40,9 +41,3 @@ TEST(Formatting, OverridePrintf) {
|
||||
printf("hello again!");
|
||||
}
|
||||
|
||||
#ifndef LIBFUZZER
|
||||
int main(int argc, char *argv[]) {
|
||||
DeepState_InitOptions(argc, argv);
|
||||
return DeepState_Run();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <deepstate/Log.h>
|
||||
@ -47,6 +48,11 @@
|
||||
#define assume DeepState_Assume
|
||||
#define check DeepState_Check
|
||||
|
||||
#ifdef DEEPSTATE_TAKEOVER_RAND
|
||||
#define rand DeepState_RandInt
|
||||
#define srand DeepState_Warn_srand
|
||||
#endif
|
||||
|
||||
#define MAYBE(...) \
|
||||
if (DeepState_Bool()) { \
|
||||
__VA_ARGS__ ; \
|
||||
@ -62,6 +68,14 @@ DECLARE_string(output_test_dir);
|
||||
|
||||
DECLARE_bool(take_over);
|
||||
DECLARE_bool(abort_on_fail);
|
||||
DECLARE_bool(verbose_reads);
|
||||
DECLARE_bool(fuzz);
|
||||
DECLARE_bool(fuzz_save_passing);
|
||||
DECLARE_bool(fork);
|
||||
|
||||
DECLARE_int(log_level);
|
||||
DECLARE_int(seed);
|
||||
DECLARE_int(timeout);
|
||||
|
||||
enum {
|
||||
DeepState_InputSize = 8192
|
||||
@ -82,6 +96,7 @@ 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 int32_t DeepState_RandInt(void);
|
||||
extern uint16_t DeepState_UShort(void);
|
||||
extern int16_t DeepState_Short(void);
|
||||
extern uint8_t DeepState_UChar(void);
|
||||
@ -127,9 +142,12 @@ DEEPSTATE_INLINE static int8_t DeepState_MaxChar(int8_t v) {
|
||||
return (int8_t) DeepState_MaxInt(v);
|
||||
}
|
||||
|
||||
/* Function to clean up generated strings, and any other DeepState-managed data. */
|
||||
extern void DeepState_CleanUp();
|
||||
|
||||
/* 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. */
|
||||
* value with a concrete value. */
|
||||
extern int DeepState_IsTrue(int expr);
|
||||
|
||||
/* Always returns `1`. */
|
||||
@ -144,15 +162,22 @@ extern int DeepState_ZeroSink(int);
|
||||
/* Symbolize the data in the exclusive range `[begin, end)`. */
|
||||
extern void DeepState_SymbolizeData(void *begin, void *end);
|
||||
|
||||
/* Symbolize the data in the exclusive range `[begin, end)` with no nulls. */
|
||||
extern void DeepState_SymbolizeDataNoNull(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);
|
||||
/* Assign a symbolic C string of _strlen_ `len` -- with only chars in allowed,
|
||||
* if `allowed` is non-null; needs space for null + len bytes */
|
||||
extern void DeepState_AssignCStr_C(char* str, size_t len, const char* allowed);
|
||||
|
||||
/* Return a symbolic C string of strlen `len`. */
|
||||
extern char *DeepState_CStr_C(size_t len, const char* allowed);
|
||||
|
||||
/* Symbolize a C string */
|
||||
void DeepState_SymbolizeCStr(char *begin);
|
||||
void DeepState_SymbolizeCStr_C(char *begin, const char* allowed);
|
||||
|
||||
/* Concretize a C string. Returns a pointer to the beginning of the
|
||||
* concretized C string. */
|
||||
@ -230,11 +255,42 @@ DEEPSTATE_INLINE static void DeepState_Check(int expr) {
|
||||
}
|
||||
|
||||
/* Return a symbolic value in a the range `[low_inc, high_inc]`. */
|
||||
/* Saturating version here is an alternative, but worse for fuzzing:
|
||||
#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); \
|
||||
if (low > high) { \
|
||||
return DeepState_ ## Tname ## InRange(high, low); \
|
||||
} \
|
||||
const tname x = DeepState_ ## Tname(); \
|
||||
if (DeepState_UsingSymExec) { \
|
||||
(void) DeepState_Assume(low <= x && x <= high); \
|
||||
return x; \
|
||||
} \
|
||||
if (x < low) { \
|
||||
return low; \
|
||||
} else if (x > high) { \
|
||||
return high; \
|
||||
} else { \
|
||||
return x; \
|
||||
} \
|
||||
}
|
||||
*/
|
||||
#define DEEPSTATE_MAKE_SYMBOLIC_RANGE(Tname, tname) \
|
||||
DEEPSTATE_INLINE static tname DeepState_ ## Tname ## InRange( \
|
||||
tname low, tname high) { \
|
||||
if (low > high) { \
|
||||
return DeepState_ ## Tname ## InRange(high, low); \
|
||||
} \
|
||||
const tname x = DeepState_ ## Tname(); \
|
||||
if (DeepState_UsingSymExec) { \
|
||||
(void) DeepState_Assume(low <= x && x <= high); \
|
||||
return x; \
|
||||
} \
|
||||
if ((x < low) || (x > high)) { \
|
||||
const tname size = (high - low) + 1; \
|
||||
return low + ((x % size + size) % size); \
|
||||
} \
|
||||
return x; \
|
||||
}
|
||||
|
||||
@ -405,6 +461,8 @@ static bool DeepState_IsTestCaseFile(const char *name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
extern void DeepState_Warn_srand(unsigned int seed);
|
||||
|
||||
/* Resets the global `DeepState_Input` buffer, then fills it with the
|
||||
* data found in the file `path`. */
|
||||
static void DeepState_InitInputFromFile(const char *path) {
|
||||
@ -441,7 +499,7 @@ static void DeepState_InitInputFromFile(const char *path) {
|
||||
DeepState_Abandon("Error reading file");
|
||||
}
|
||||
|
||||
DeepState_LogFormat(DeepState_LogInfo,
|
||||
DeepState_LogFormat(DeepState_LogTrace,
|
||||
"Initialized test input buffer with data from `%s`",
|
||||
path);
|
||||
}
|
||||
@ -483,16 +541,18 @@ static void DeepState_RunTest(struct DeepState_TestInfo *test) {
|
||||
|
||||
/* The test passed. */
|
||||
} else {
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Passed: %s", test->test_name);
|
||||
DeepState_LogFormat(DeepState_LogTrace, "Passed: %s", test->test_name);
|
||||
if (HAS_FLAG_output_test_dir) {
|
||||
DeepState_SavePassingTest();
|
||||
if (!FLAGS_fuzz || FLAGS_fuzz_save_passing) {
|
||||
DeepState_SavePassingTest();
|
||||
}
|
||||
}
|
||||
exit(DeepState_TestRunPass);
|
||||
}
|
||||
}
|
||||
|
||||
/* Run a test case, but in libFuzzer, so not inside a fork. */
|
||||
static int DeepState_RunTestLLVM(struct DeepState_TestInfo *test) {
|
||||
static int DeepState_RunTestNoFork(struct DeepState_TestInfo *test) {
|
||||
/* Run the test. */
|
||||
if (!setjmp(DeepState_ReturnToRun)) {
|
||||
/* Convert uncaught C++ exceptions into a test failure. */
|
||||
@ -525,7 +585,7 @@ static int DeepState_RunTestLLVM(struct DeepState_TestInfo *test) {
|
||||
|
||||
/* The test passed. */
|
||||
} else {
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Passed: %s", test->test_name);
|
||||
DeepState_LogFormat(DeepState_LogTrace, "Passed: %s", test->test_name);
|
||||
if (HAS_FLAG_output_test_dir) {
|
||||
DeepState_SavePassingTest();
|
||||
}
|
||||
@ -536,13 +596,22 @@ static int DeepState_RunTestLLVM(struct DeepState_TestInfo *test) {
|
||||
/* Fork and run `test`. */
|
||||
static enum DeepState_TestRunResult
|
||||
DeepState_ForkAndRunTest(struct DeepState_TestInfo *test) {
|
||||
pid_t test_pid = fork();
|
||||
if (!test_pid) {
|
||||
DeepState_RunTest(test);
|
||||
pid_t test_pid;
|
||||
if (FLAGS_fork) {
|
||||
test_pid = fork();
|
||||
if (!test_pid) {
|
||||
DeepState_RunTest(test);
|
||||
/* No need to clean up in a fork; exit() is the ultimate garbage collector */
|
||||
}
|
||||
}
|
||||
int wstatus;
|
||||
waitpid(test_pid, &wstatus, 0);
|
||||
|
||||
int wstatus = 0;
|
||||
if (FLAGS_fork) {
|
||||
waitpid(test_pid, &wstatus, 0);
|
||||
} else {
|
||||
wstatus = DeepState_RunTestNoFork(test);
|
||||
DeepState_CleanUp();
|
||||
}
|
||||
|
||||
/* If we exited normally, the status code tells us if the test passed. */
|
||||
if (WIFEXITED(wstatus)) {
|
||||
uint8_t status = WEXITSTATUS(wstatus);
|
||||
@ -554,6 +623,8 @@ DeepState_ForkAndRunTest(struct DeepState_TestInfo *test) {
|
||||
return DeepState_TestRunCrash;
|
||||
}
|
||||
|
||||
extern enum DeepState_TestRunResult DeepState_FuzzOneTestCase(struct DeepState_TestInfo *test);
|
||||
|
||||
/* Run a single saved test case with input initialized from the file
|
||||
* `name` in directory `dir`. */
|
||||
static enum DeepState_TestRunResult
|
||||
@ -618,9 +689,12 @@ static int DeepState_RunSavedCasesForTest(struct DeepState_TestInfo *test) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int i = 0;
|
||||
|
||||
/* Read generated test cases and run a test for each file found. */
|
||||
while ((dp = readdir(dir_fd)) != NULL) {
|
||||
if (DeepState_IsTestCaseFile(dp->d_name)) {
|
||||
i++;
|
||||
enum DeepState_TestRunResult result =
|
||||
DeepState_RunSavedTestCase(test, test_case_dir, dp->d_name);
|
||||
|
||||
@ -632,6 +706,9 @@ static int DeepState_RunSavedCasesForTest(struct DeepState_TestInfo *test) {
|
||||
closedir(dir_fd);
|
||||
free(test_case_dir);
|
||||
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Ran %u tests for %s; %d tests failed",
|
||||
i, test->test_name, num_failed_tests);
|
||||
|
||||
return num_failed_tests;
|
||||
}
|
||||
|
||||
@ -649,8 +726,9 @@ static int DeepState_RunSingleSavedTestCase(void) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
DeepState_LogFormat(DeepState_LogInfo,
|
||||
"No test specified, defaulting to first test");
|
||||
DeepState_LogFormat(DeepState_LogWarning,
|
||||
"No test specified, defaulting to last test defined (%s)",
|
||||
test->test_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -667,7 +745,7 @@ static int DeepState_RunSingleSavedTestCase(void) {
|
||||
|
||||
if ((result == DeepState_TestRunFail) || (result == DeepState_TestRunCrash)) {
|
||||
if (FLAGS_abort_on_fail) {
|
||||
abort();
|
||||
assert(0); // Terminate in a way AFL/etc. can see as a crash
|
||||
}
|
||||
num_failed_tests++;
|
||||
}
|
||||
@ -677,12 +755,18 @@ static int DeepState_RunSingleSavedTestCase(void) {
|
||||
return num_failed_tests;
|
||||
}
|
||||
|
||||
extern int DeepState_Fuzz(void);
|
||||
|
||||
/* Run tests from `FLAGS_input_test_files_dir`, under `FLAGS_input_which_test`
|
||||
* or first test, if not defined. */
|
||||
static int DeepState_RunSingleSavedTestDir(void) {
|
||||
int num_failed_tests = 0;
|
||||
struct DeepState_TestInfo *test = NULL;
|
||||
|
||||
if (!HAS_FLAG_log_level) {
|
||||
FLAGS_log_level = 2;
|
||||
}
|
||||
|
||||
DeepState_Setup();
|
||||
|
||||
for (test = DeepState_FirstTest(); test != NULL; test = test->prev) {
|
||||
@ -691,8 +775,10 @@ static int DeepState_RunSingleSavedTestDir(void) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
DeepState_LogFormat(DeepState_LogInfo,
|
||||
"No test specified, defaulting to last test defined");
|
||||
DeepState_LogFormat(DeepState_LogWarning,
|
||||
"No test specified, defaulting to last test defined (%s)",
|
||||
test->test_name);
|
||||
;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -712,10 +798,12 @@ static int DeepState_RunSingleSavedTestDir(void) {
|
||||
dir_fd = opendir(FLAGS_input_test_files_dir);
|
||||
if (dir_fd == NULL) {
|
||||
DeepState_LogFormat(DeepState_LogInfo,
|
||||
"No tests to run.");
|
||||
"No tests to run");
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int i = 0;
|
||||
|
||||
/* Read generated test cases and run a test for each file found. */
|
||||
while ((dp = readdir(dir_fd)) != NULL) {
|
||||
size_t path_len = 2 + sizeof(char) * (strlen(FLAGS_input_test_files_dir) + strlen(dp->d_name));
|
||||
@ -724,12 +812,13 @@ static int DeepState_RunSingleSavedTestDir(void) {
|
||||
stat(path, &path_stat);
|
||||
|
||||
if (S_ISREG(path_stat.st_mode)) {
|
||||
i++;
|
||||
enum DeepState_TestRunResult result =
|
||||
DeepState_RunSavedTestCase(test, FLAGS_input_test_files_dir, dp->d_name);
|
||||
|
||||
if ((result == DeepState_TestRunFail) || (result == DeepState_TestRunCrash)) {
|
||||
if (FLAGS_abort_on_fail) {
|
||||
abort();
|
||||
assert(0); // Terminate in a way AFL/etc. can see as a crash
|
||||
}
|
||||
|
||||
num_failed_tests++;
|
||||
@ -738,6 +827,9 @@ static int DeepState_RunSingleSavedTestDir(void) {
|
||||
}
|
||||
closedir(dir_fd);
|
||||
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Ran %u tests; %d tests failed",
|
||||
i, num_failed_tests);
|
||||
|
||||
return num_failed_tests;
|
||||
}
|
||||
|
||||
@ -750,6 +842,10 @@ static int DeepState_RunSavedTestCases(void) {
|
||||
int num_failed_tests = 0;
|
||||
struct DeepState_TestInfo *test = NULL;
|
||||
|
||||
if (!HAS_FLAG_log_level) {
|
||||
FLAGS_log_level = 2;
|
||||
}
|
||||
|
||||
DeepState_Setup();
|
||||
|
||||
for (test = DeepState_FirstTest(); test != NULL; test = test->prev) {
|
||||
@ -764,20 +860,24 @@ static int DeepState_RunSavedTestCases(void) {
|
||||
/* Start DeepState and run the tests. Returns the number of failed tests. */
|
||||
static int DeepState_Run(void) {
|
||||
if (!DeepState_OptionsAreInitialized) {
|
||||
DeepState_Abandon("Please call DeepState_InitOptions(argc, argv) in main.");
|
||||
}
|
||||
|
||||
if (HAS_FLAG_input_test_dir) {
|
||||
return DeepState_RunSavedTestCases();
|
||||
DeepState_Abandon("Please call DeepState_InitOptions(argc, argv) in main");
|
||||
}
|
||||
|
||||
if (HAS_FLAG_input_test_file) {
|
||||
return DeepState_RunSingleSavedTestCase();
|
||||
}
|
||||
|
||||
if (HAS_FLAG_input_test_dir) {
|
||||
return DeepState_RunSavedTestCases();
|
||||
}
|
||||
|
||||
if (HAS_FLAG_input_test_files_dir) {
|
||||
return DeepState_RunSingleSavedTestDir();
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAGS_fuzz) {
|
||||
return DeepState_Fuzz();
|
||||
}
|
||||
|
||||
int num_failed_tests = 0;
|
||||
int use_drfuzz = getenv("DYNAMORIO_EXE_PATH") != NULL;
|
||||
@ -795,8 +895,9 @@ static int DeepState_Run(void) {
|
||||
} else {
|
||||
DeepState_Begin(test);
|
||||
}
|
||||
|
||||
num_failed_tests += DeepState_ForkAndRunTest(test);
|
||||
if (DeepState_ForkAndRunTest(test) != 0) {
|
||||
num_failed_tests++;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_drfuzz) {
|
||||
|
||||
@ -301,7 +301,7 @@ static T Pump(T val, unsigned max=10) {
|
||||
return val;
|
||||
}
|
||||
if (!max) {
|
||||
DeepState_Abandon("Must have a positive maximum number of values to pump.");
|
||||
DeepState_Abandon("Must have a positive maximum number of values to Pump");
|
||||
}
|
||||
for (auto i = 0U; i < max - 1; ++i) {
|
||||
T min_val = Minimize(val);
|
||||
@ -314,7 +314,7 @@ static T Pump(T val, unsigned max=10) {
|
||||
}
|
||||
return Minimize(val);
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
inline static void ForAll(void (*func)(Args...)) {
|
||||
func(Symbolic<Args>()...);
|
||||
@ -327,32 +327,38 @@ inline static void ForAll(Closure func) {
|
||||
|
||||
template <typename... FuncTys>
|
||||
inline static void OneOf(FuncTys&&... funcs) {
|
||||
if (FLAGS_verbose_reads) {
|
||||
printf("STARTING OneOf CALL\n");
|
||||
}
|
||||
std::function<void(void)> func_arr[sizeof...(FuncTys)] = {funcs...};
|
||||
unsigned index = DeepState_UIntInRange(
|
||||
0U, static_cast<unsigned>(sizeof...(funcs))-1);
|
||||
func_arr[Pump(index, sizeof...(funcs))]();
|
||||
if (FLAGS_verbose_reads) {
|
||||
printf("FINISHED OneOf CALL\n");
|
||||
}
|
||||
}
|
||||
|
||||
inline static char OneOf(const char *str) {
|
||||
if (!str || !str[0]) {
|
||||
DeepState_Abandon("NULL or empty string passed to OneOf.");
|
||||
DeepState_Abandon("NULL or empty string passed to OneOf");
|
||||
}
|
||||
return str[DeepState_IntInRange(0, strlen(str) - 1)];
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline static const T &OneOf(const std::vector<T> &arr) {
|
||||
if (arr.empty()) {
|
||||
DeepState_Abandon("Empty vector passed to OneOf.");
|
||||
DeepState_Abandon("Empty vector passed to OneOf");
|
||||
}
|
||||
return arr[DeepState_IntInRange(0, arr.size - 1)];
|
||||
return arr[DeepState_IntInRange(0, arr.size() - 1)];
|
||||
}
|
||||
|
||||
|
||||
template <typename T, int len>
|
||||
inline static const T &OneOf(T (&arr)[len]) {
|
||||
if (!len) {
|
||||
DeepState_Abandon("Empty array passed to OneOf.");
|
||||
DeepState_Abandon("Empty array passed to OneOf");
|
||||
}
|
||||
return arr[DeepState_IntInRange(0, len - 1)];
|
||||
}
|
||||
@ -465,6 +471,35 @@ struct Comparer {
|
||||
}
|
||||
};
|
||||
|
||||
/* Like DeepState_AssignCStr_C, but fills in a null `allowed` value. */
|
||||
inline static void DeepState_AssignCStr(char* str, size_t len,
|
||||
const char* allowed = 0) {
|
||||
DeepState_AssignCStr_C(str, len, allowed);
|
||||
}
|
||||
|
||||
/* Like DeepState_AssignCStr, but Pumps through possible string sizes. */
|
||||
inline static void DeepState_AssignCStrUpToLen(char* str, size_t max_len,
|
||||
const char* allowed = 0) {
|
||||
uint32_t len = DeepState_UIntInRange(0, max_len);
|
||||
DeepState_AssignCStr_C(str, Pump(len, max_len+1), allowed);
|
||||
}
|
||||
|
||||
/* Like DeepState_CStr_C, but fills in a null `allowed` value. */
|
||||
inline static char* DeepState_CStr(size_t len, const char* allowed = 0) {
|
||||
return DeepState_CStr_C(len, allowed);
|
||||
}
|
||||
|
||||
/* Like DeepState_CStr, but Pumps through possible string sizes. */
|
||||
inline static char* DeepState_CStrUpToLen(size_t max_len, const char* allowed = 0) {
|
||||
uint32_t len = DeepState_UIntInRange(0, max_len);
|
||||
return DeepState_CStr_C(Pump(len, max_len+1), allowed);
|
||||
}
|
||||
|
||||
/* Like DeepState_Symbolize_CStr, but fills in null `allowed` value. */
|
||||
inline static void DeepState_SymbolizeCStr(char *begin, const char* allowed = 0) {
|
||||
DeepState_SymbolizeCStr_C(begin, allowed);
|
||||
}
|
||||
|
||||
} // namespace deepstate
|
||||
|
||||
#define ONE_OF ::deepstate::OneOf
|
||||
@ -515,6 +550,9 @@ struct Comparer {
|
||||
#define LOG_DEBUG(cond) \
|
||||
::deepstate::Stream(DeepState_LogDebug, (cond), __FILE__, __LINE__)
|
||||
|
||||
#define LOG_TRACE(cond) \
|
||||
::deepstate::Stream(DeepState_LogTrace, (cond), __FILE__, __LINE__)
|
||||
|
||||
#define LOG_INFO(cond) \
|
||||
::deepstate::Stream(DeepState_LogInfo, (cond), __FILE__, __LINE__)
|
||||
|
||||
@ -575,11 +613,11 @@ struct Comparer {
|
||||
|
||||
#define ASSUME(expr) \
|
||||
DeepState_Assume(expr), ::deepstate::Stream( \
|
||||
DeepState_LogInfo, true, __FILE__, __LINE__)
|
||||
DeepState_LogTrace, true, __FILE__, __LINE__)
|
||||
|
||||
#define DEEPSTATE_ASSUME_BINOP(a, b, op) \
|
||||
DeepState_Assume((a op b)), ::deepstate::Stream( \
|
||||
DeepState_LogInfo, true, __FILE__, __LINE__)
|
||||
DeepState_LogTrace, true, __FILE__, __LINE__)
|
||||
|
||||
#define ASSUME_EQ(a, b) DEEPSTATE_ASSUME_BINOP(a, b, ==)
|
||||
#define ASSUME_NE(a, b) DEEPSTATE_ASSUME_BINOP(a, b, !=)
|
||||
|
||||
@ -97,7 +97,12 @@ static void klee_posix_prefer_cex(void *object, uintptr_t condition) {
|
||||
/* static KLEE_GET_VALUE(d, double); */
|
||||
|
||||
static KLEE_GET_VALUE(l, long) {
|
||||
DeepState_MinInt(val);
|
||||
if (sizeof(long) == sizeof(int)) {
|
||||
return DeepState_MinInt((int) val);
|
||||
} else {
|
||||
// TODO: We need a MinInt64 function.
|
||||
return DeepState_MinInt(val);
|
||||
}
|
||||
}
|
||||
|
||||
/* Unsupported. */
|
||||
@ -105,7 +110,7 @@ static KLEE_GET_VALUE(l, long) {
|
||||
|
||||
/* TODO(joe): Implement */
|
||||
static KLEE_GET_VALUE(_i32, int32_t) {
|
||||
DeepState_MinInt(val);
|
||||
return DeepState_MinInt(val);
|
||||
}
|
||||
|
||||
/* TODO(joe): Implement */
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
DEEPSTATE_BEGIN_EXTERN_C
|
||||
|
||||
extern int DeepState_UsingLibFuzzer;
|
||||
extern int DeepState_UsingSymExec;
|
||||
|
||||
struct DeepState_Stream;
|
||||
|
||||
@ -33,12 +34,13 @@ struct DeepState_VarArgs {
|
||||
|
||||
enum DeepState_LogLevel {
|
||||
DeepState_LogDebug = 0,
|
||||
DeepState_LogInfo = 1,
|
||||
DeepState_LogWarning = 2,
|
||||
DeepState_LogTrace = 1,
|
||||
DeepState_LogInfo = 2,
|
||||
DeepState_LogWarning = 3,
|
||||
DeepState_LogWarn = DeepState_LogWarning,
|
||||
DeepState_LogError = 3,
|
||||
DeepState_LogExternal = 4,
|
||||
DeepState_LogFatal = 5,
|
||||
DeepState_LogError = 4,
|
||||
DeepState_LogExternal = 5,
|
||||
DeepState_LogFatal = 6,
|
||||
DeepState_LogCritical = DeepState_LogFatal,
|
||||
};
|
||||
|
||||
|
||||
@ -40,11 +40,10 @@ class Stream {
|
||||
|
||||
DEEPSTATE_INLINE ~Stream(void) {
|
||||
if (do_log) {
|
||||
if (has_something_to_log) {
|
||||
DeepState_LogStream(level);
|
||||
} else {
|
||||
DeepState_ClearStream(level);
|
||||
if (!has_something_to_log) {
|
||||
DeepState_StreamCStr(level, "Checked condition");
|
||||
}
|
||||
DeepState_LogStream(level);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,11 @@
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEEPSTATE_TAKEOVER_RAND
|
||||
#undef rand
|
||||
#undef srand
|
||||
#endif
|
||||
|
||||
DEEPSTATE_BEGIN_EXTERN_C
|
||||
|
||||
DEFINE_uint(num_workers, 1,
|
||||
@ -36,6 +41,28 @@ DEFINE_string(output_test_dir, "", "Directory where tests will be saved.");
|
||||
|
||||
DEFINE_bool(take_over, false, "Replay test cases in take-over mode.");
|
||||
DEFINE_bool(abort_on_fail, false, "Abort on file replay failure (useful in file fuzzing).");
|
||||
DEFINE_bool(verbose_reads, false, "Report on bytes being read during execution of test.");
|
||||
DEFINE_bool(fuzz, false, "Perform brute force unguided fuzzing.");
|
||||
DEFINE_bool(fuzz_save_passing, false, "Save passing tests during fuzzing.");
|
||||
DEFINE_bool(fork, true, "Fork when running a test.");
|
||||
|
||||
DEFINE_int(log_level, 0, "Minimum level of logging to output.");
|
||||
DEFINE_int(seed, 0, "Seed for brute force fuzzing (uses time if not set).");
|
||||
DEFINE_int(timeout, 120, "Timeout for brute force fuzzing.");
|
||||
|
||||
/* Set to 1 by Manticore/Angr/etc. when we're running symbolically. */
|
||||
int DeepState_UsingSymExec = 0;
|
||||
|
||||
/* Set to 1 when we're using libFuzzer. */
|
||||
int DeepState_UsingLibFuzzer = 0;
|
||||
|
||||
/* To make libFuzzer louder on mac OS. */
|
||||
int DeepState_LibFuzzerLoud = 0;
|
||||
|
||||
/* Array of DeepState generated strings. Impossible for there to
|
||||
* be more than there are input bytes. Index stores where we are. */
|
||||
char* DeepState_GeneratedStrings[DeepState_InputSize];
|
||||
uint32_t DeepState_GeneratedStringsIndex = 0;
|
||||
|
||||
/* Pointer to the last registers DeepState_TestInfo data structure */
|
||||
struct DeepState_TestInfo *DeepState_LastTestInfo = NULL;
|
||||
@ -73,7 +100,7 @@ void DeepState_AllocCurrentTestRun(void) {
|
||||
mem_vis, 0, 0);
|
||||
|
||||
if (shared_mem == MAP_FAILED) {
|
||||
DeepState_Log(DeepState_LogError, "Unable to map shared memory.");
|
||||
DeepState_Log(DeepState_LogError, "Unable to map shared memory");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -140,36 +167,107 @@ void DeepState_SymbolizeData(void *begin, void *end) {
|
||||
if (DeepState_InputIndex >= DeepState_InputSize) {
|
||||
DeepState_Abandon("Read too many symbols");
|
||||
}
|
||||
if (FLAGS_verbose_reads) {
|
||||
printf("Reading byte at %u\n", DeepState_InputIndex);
|
||||
}
|
||||
bytes[i] = DeepState_Input[DeepState_InputIndex++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Symbolize the data in the exclusive range `[begin, end)` without null
|
||||
* characters included. Primarily useful for C strings. */
|
||||
void DeepState_SymbolizeDataNoNull(void *begin, void *end) {
|
||||
uintptr_t begin_addr = (uintptr_t) begin;
|
||||
uintptr_t end_addr = (uintptr_t) end;
|
||||
|
||||
if (begin_addr > end_addr) {
|
||||
DeepState_Abandon("Invalid data bounds for DeepState_SymbolizeData");
|
||||
} 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) {
|
||||
if (DeepState_InputIndex >= DeepState_InputSize) {
|
||||
DeepState_Abandon("Read too many symbols");
|
||||
}
|
||||
if (FLAGS_verbose_reads) {
|
||||
printf("Reading byte at %u\n", DeepState_InputIndex);
|
||||
}
|
||||
bytes[i] = DeepState_Input[DeepState_InputIndex++];
|
||||
if (bytes[i] == 0) {
|
||||
bytes[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
/* Assign a symbolic C string of strlen length `len`. str should include
|
||||
* storage for both `len` characters AND the null terminator. Allowed
|
||||
* is a set of chars that are allowed (ignored if null). */
|
||||
void DeepState_AssignCStr_C(char* str, size_t len, const char* allowed) {
|
||||
if (SIZE_MAX == len) {
|
||||
DeepState_Abandon("Can't create an SIZE_MAX-length string.");
|
||||
}
|
||||
char *str = (char *) malloc(sizeof(char) * (len + 1));
|
||||
if (NULL == str) {
|
||||
DeepState_Abandon("Can't allocate memory.");
|
||||
DeepState_Abandon("Attempted to populate null pointer.");
|
||||
}
|
||||
if (len) {
|
||||
DeepState_SymbolizeData(str, &(str[len - 1]));
|
||||
if (!allowed) {
|
||||
DeepState_SymbolizeDataNoNull(str, &(str[len]));
|
||||
} else {
|
||||
uint32_t allowed_size = strlen(allowed);
|
||||
for (int i = 0; i < len; i++) {
|
||||
str[i] = allowed[DeepState_UIntInRange(0, allowed_size-1)];
|
||||
}
|
||||
}
|
||||
}
|
||||
str[len] = '\0';
|
||||
}
|
||||
|
||||
/* Return a symbolic C string of strlen `len`. */
|
||||
char *DeepState_CStr_C(size_t len, const char* allowed) {
|
||||
if (SIZE_MAX == len) {
|
||||
DeepState_Abandon("Can't create an SIZE_MAX-length string");
|
||||
}
|
||||
char *str = (char *) malloc(sizeof(char) * (len + 1));
|
||||
if (NULL == str) {
|
||||
DeepState_Abandon("Can't allocate memory");
|
||||
}
|
||||
DeepState_GeneratedStrings[DeepState_GeneratedStringsIndex++] = str;
|
||||
if (len) {
|
||||
if (!allowed) {
|
||||
DeepState_SymbolizeDataNoNull(str, &(str[len]));
|
||||
} else {
|
||||
uint32_t allowed_size = strlen(allowed);
|
||||
for (int i = 0; i < len; i++) {
|
||||
str[i] = allowed[DeepState_UIntInRange(0, allowed_size-1)];
|
||||
}
|
||||
}
|
||||
}
|
||||
str[len] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
/* Symbolize a C string */
|
||||
void DeepState_SymbolizeCStr(char *begin) {
|
||||
/* Symbolize a C string; keeps the null terminator where it was. */
|
||||
void DeepState_SymbolizeCStr_C(char *begin, const char* allowed) {
|
||||
if (begin && begin[0]) {
|
||||
DeepState_SymbolizeData(begin, begin + strlen(begin));
|
||||
if (!allowed) {
|
||||
DeepState_SymbolizeDataNoNull(begin, begin + strlen(begin));
|
||||
} else {
|
||||
uint32_t allowed_size = strlen(allowed);
|
||||
uint8_t *bytes = (uint8_t *) begin;
|
||||
uintptr_t begin_addr = (uintptr_t) begin;
|
||||
uintptr_t end_addr = (uintptr_t) (begin + strlen(begin));
|
||||
for (uintptr_t i = 0, max_i = (end_addr - begin_addr); i < max_i; ++i) {
|
||||
bytes[i] = allowed[DeepState_UIntInRange(0, allowed_size-1)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,6 +314,9 @@ int DeepState_Bool(void) {
|
||||
if (DeepState_InputIndex >= DeepState_InputSize) {
|
||||
DeepState_Abandon("Read too many symbols");
|
||||
}
|
||||
if (FLAGS_verbose_reads) {
|
||||
printf("Reading byte as boolean at %u\n", DeepState_InputIndex);
|
||||
}
|
||||
return DeepState_Input[DeepState_InputIndex++] & 1;
|
||||
}
|
||||
|
||||
@ -225,10 +326,19 @@ int DeepState_Bool(void) {
|
||||
DeepState_Abandon("Read too many symbols"); \
|
||||
} \
|
||||
type val = 0; \
|
||||
if (FLAGS_verbose_reads) { \
|
||||
printf("STARTING MULTI-BYTE READ\n"); \
|
||||
} \
|
||||
_Pragma("unroll") \
|
||||
for (size_t i = 0; i < sizeof(type); ++i) { \
|
||||
if (FLAGS_verbose_reads) { \
|
||||
printf("Reading byte at %u\n", DeepState_InputIndex); \
|
||||
} \
|
||||
val = (val << 8) | ((type) DeepState_Input[DeepState_InputIndex++]); \
|
||||
} \
|
||||
if (FLAGS_verbose_reads) { \
|
||||
printf("FINISHED MULTI-BYTE READ\n"); \
|
||||
} \
|
||||
return val; \
|
||||
}
|
||||
|
||||
@ -257,6 +367,10 @@ int8_t DeepState_Char(void) {
|
||||
|
||||
#undef MAKE_SYMBOL_FUNC
|
||||
|
||||
int32_t DeepState_RandInt() {
|
||||
return DeepState_IntInRange(0, RAND_MAX);
|
||||
}
|
||||
|
||||
/* Returns the minimum satisfiable value for a given symbolic value, given
|
||||
* the constraints present on that value. */
|
||||
uint32_t DeepState_MinUInt(uint32_t v) {
|
||||
@ -279,9 +393,20 @@ int32_t DeepState_MaxInt(int32_t v) {
|
||||
0x80000000U);
|
||||
}
|
||||
|
||||
/* Function to clean up generated strings, and any other DeepState-managed data. */
|
||||
extern void DeepState_CleanUp() {
|
||||
for (int i = 0; i < DeepState_GeneratedStringsIndex; i++) {
|
||||
free(DeepState_GeneratedStrings[i]);
|
||||
}
|
||||
DeepState_GeneratedStringsIndex = 0;
|
||||
}
|
||||
|
||||
void _DeepState_Assume(int expr, const char *expr_str, const char *file,
|
||||
unsigned line) {
|
||||
if (!expr) {
|
||||
DeepState_LogFormat(DeepState_LogError,
|
||||
"%s(%u): Assumption %s failed",
|
||||
file, line, expr_str);
|
||||
DeepState_Abandon("Assumption failed");
|
||||
}
|
||||
}
|
||||
@ -347,12 +472,20 @@ const struct DeepState_IndexEntry DeepState_API[] = {
|
||||
{"StreamFloat", (void *) _DeepState_StreamFloat},
|
||||
{"StreamString", (void *) _DeepState_StreamString},
|
||||
|
||||
{"UsingLibFuzzer", (void *) &DeepState_UsingLibFuzzer},
|
||||
{"UsingSymExec", (void *) &DeepState_UsingSymExec},
|
||||
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
/* Set up DeepState. */
|
||||
DEEPSTATE_NOINLINE
|
||||
void DeepState_Setup(void) {
|
||||
DeepState_AllocCurrentTestRun();
|
||||
static int was_setup = 0;
|
||||
if (!was_setup) {
|
||||
DeepState_AllocCurrentTestRun();
|
||||
was_setup = 1;
|
||||
}
|
||||
|
||||
/* TODO(pag): Sort the test cases by file name and line number. */
|
||||
}
|
||||
@ -365,7 +498,7 @@ void DeepState_Teardown(void) {
|
||||
/* Notify that we're about to begin a test. */
|
||||
void DeepState_Begin(struct DeepState_TestInfo *test) {
|
||||
DeepState_InitCurrentTestRun(test);
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Running: %s from %s(%u)",
|
||||
DeepState_LogFormat(DeepState_LogTrace, "Running: %s from %s(%u)",
|
||||
test->test_name, test->file_name, test->line_number);
|
||||
}
|
||||
|
||||
@ -376,7 +509,7 @@ void DrMemFuzzFunc(volatile uint8_t *buff, size_t size) {
|
||||
struct DeepState_TestInfo *test = DeepState_DrFuzzTest;
|
||||
DeepState_InputIndex = 0;
|
||||
DeepState_InitCurrentTestRun(test);
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Running: %s from %s(%u)",
|
||||
DeepState_LogFormat(DeepState_LogTrace, "Running: %s from %s(%u)",
|
||||
test->test_name, test->file_name, test->line_number);
|
||||
|
||||
if (!setjmp(DeepState_ReturnToRun)) {
|
||||
@ -386,6 +519,7 @@ void DrMemFuzzFunc(volatile uint8_t *buff, size_t size) {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
test->test_func();
|
||||
DeepState_CleanUp();
|
||||
DeepState_Pass();
|
||||
|
||||
#if defined(__cplusplus) && defined(__cpp_exceptions)
|
||||
@ -407,13 +541,18 @@ void DrMemFuzzFunc(volatile uint8_t *buff, size_t size) {
|
||||
|
||||
/* The test passed. */
|
||||
} else {
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Passed: %s", test->test_name);
|
||||
DeepState_LogFormat(DeepState_LogTrace, "Passed: %s", test->test_name);
|
||||
if (HAS_FLAG_output_test_dir) {
|
||||
DeepState_SavePassingTest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeepState_Warn_srand(unsigned int seed) {
|
||||
DeepState_LogFormat(DeepState_LogWarning,
|
||||
"srand under DeepState has no effect: rand is re-defined as DeepState_Int");
|
||||
}
|
||||
|
||||
void DeepState_RunSavedTakeOverCases(jmp_buf env,
|
||||
struct DeepState_TestInfo *test) {
|
||||
int num_failed_tests = 0;
|
||||
@ -458,7 +597,7 @@ void DeepState_RunSavedTakeOverCases(jmp_buf env,
|
||||
if (WIFEXITED(wstatus)) {
|
||||
switch (DeepState_CurrentTestRun->result) {
|
||||
case DeepState_TestRunPass:
|
||||
DeepState_LogFormat(DeepState_LogInfo,
|
||||
DeepState_LogFormat(DeepState_LogTrace,
|
||||
"Passed: TakeOver test with data from `%s`",
|
||||
dp->d_name);
|
||||
break;
|
||||
@ -520,14 +659,64 @@ void DeepState_BeginDrFuzz(struct DeepState_TestInfo *test) {
|
||||
DrMemFuzzFunc(DeepState_Input, DeepState_InputSize);
|
||||
}
|
||||
|
||||
/* Right now "fake" a hexdigest by just using random bytes. Not ideal. */
|
||||
void makeFilename(char *name, size_t size) {
|
||||
const char *entities = "0123456789abcdef";
|
||||
for (int i = 0; i < size; i++) {
|
||||
name[i] = entities[rand()%16];
|
||||
}
|
||||
}
|
||||
|
||||
void writeInputData(char* name, int important) {
|
||||
size_t path_len = 2 + sizeof(char) * (strlen(FLAGS_output_test_dir) + strlen(name));
|
||||
char *path = (char *) malloc(path_len);
|
||||
snprintf(path, path_len, "%s/%s", FLAGS_output_test_dir, name);
|
||||
FILE *fp = fopen(path, "wb");
|
||||
if (fp == NULL) {
|
||||
DeepState_LogFormat(DeepState_LogError, "Failed to create file `%s`", path);
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
size_t written = fwrite((void *)DeepState_Input, 1, DeepState_InputSize, fp);
|
||||
if (written != DeepState_InputSize) {
|
||||
DeepState_LogFormat(DeepState_LogError, "Failed to write to file `%s`", path);
|
||||
} else {
|
||||
if (important) {
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Saved test case in file `%s`", path);
|
||||
} else {
|
||||
DeepState_LogFormat(DeepState_LogTrace, "Saved test case in file `%s`", path);
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/* Save a passing test to the output test directory. */
|
||||
void DeepState_SavePassingTest(void) {}
|
||||
void DeepState_SavePassingTest(void) {
|
||||
char name[48];
|
||||
makeFilename(name, 40);
|
||||
name[40] = 0;
|
||||
strncat(name, ".pass", 48);
|
||||
writeInputData(name, 0);
|
||||
}
|
||||
|
||||
/* Save a failing test to the output test directory. */
|
||||
void DeepState_SaveFailingTest(void) {}
|
||||
void DeepState_SaveFailingTest(void) {
|
||||
char name[48];
|
||||
makeFilename(name, 40);
|
||||
name[40] = 0;
|
||||
strncat(name, ".fail", 48);
|
||||
writeInputData(name, 1);
|
||||
}
|
||||
|
||||
/* Save a crashing test to the output test directory. */
|
||||
void DeepState_SaveCrashingTest(void) {}
|
||||
void DeepState_SaveCrashingTest(void) {
|
||||
char name[48];
|
||||
makeFilename(name, 40);
|
||||
name[40] = 0;
|
||||
strncat(name, ".crash", 48);
|
||||
writeInputData(name, 1);
|
||||
}
|
||||
|
||||
/* Return the first test case to run. */
|
||||
struct DeepState_TestInfo *DeepState_FirstTest(void) {
|
||||
@ -544,12 +733,123 @@ bool DeepState_CatchAbandoned(void) {
|
||||
return DeepState_CurrentTestRun->result == DeepState_TestRunAbandon;
|
||||
}
|
||||
|
||||
/* Fuzz test `FLAGS_input_which_test` or first test, if not defined.
|
||||
Has to be defined here since we redefine rand in the header. */
|
||||
int DeepState_Fuzz(void){
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Starting fuzzing");
|
||||
|
||||
if (!HAS_FLAG_log_level) {
|
||||
FLAGS_log_level = 2;
|
||||
}
|
||||
|
||||
if (HAS_FLAG_seed) {
|
||||
srand(FLAGS_seed);
|
||||
} else {
|
||||
unsigned int seed = time(NULL);
|
||||
DeepState_LogFormat(DeepState_LogWarning, "No seed provided; using %u", seed);
|
||||
srand(seed);
|
||||
}
|
||||
|
||||
long start = (long)time(NULL);
|
||||
long current = (long)time(NULL);
|
||||
unsigned diff = 0;
|
||||
unsigned int i = 0;
|
||||
|
||||
int num_failed_tests = 0;
|
||||
|
||||
struct DeepState_TestInfo *test = NULL;
|
||||
|
||||
DeepState_Setup();
|
||||
|
||||
for (test = DeepState_FirstTest(); test != NULL; test = test->prev) {
|
||||
if (HAS_FLAG_input_which_test) {
|
||||
if (strncmp(FLAGS_input_which_test, test->test_name, strlen(FLAGS_input_which_test)) == 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
DeepState_LogFormat(DeepState_LogWarning,
|
||||
"No test specified, defaulting to last test defined (%s)",
|
||||
test->test_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (test == NULL) {
|
||||
DeepState_LogFormat(DeepState_LogInfo,
|
||||
"Could not find matching test for %s",
|
||||
FLAGS_input_which_test);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int last_status = 0;
|
||||
|
||||
while (diff < FLAGS_timeout) {
|
||||
i++;
|
||||
if ((diff != last_status) && ((diff % 30) == 0) ) {
|
||||
time_t t = time(NULL);
|
||||
struct tm tm = *localtime(&t);
|
||||
DeepState_LogFormat(DeepState_LogInfo, "%d-%02d-%02d %02d:%02d:%02d: %u tests/second / %d failed tests so far",
|
||||
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, i/diff, num_failed_tests);
|
||||
last_status = diff;
|
||||
}
|
||||
if (DeepState_FuzzOneTestCase(test) != 0) {
|
||||
num_failed_tests ++;
|
||||
}
|
||||
|
||||
current = (long)time(NULL);
|
||||
diff = current-start;
|
||||
}
|
||||
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Done fuzzing! Ran %u tests (%u tests/second) with %d failed tests",
|
||||
i, i/diff, num_failed_tests);
|
||||
|
||||
return num_failed_tests;
|
||||
}
|
||||
|
||||
|
||||
/* Run a test case with input initialized by fuzzing.
|
||||
Has to be defined here since we redefine rand in the header. */
|
||||
enum DeepState_TestRunResult DeepState_FuzzOneTestCase(struct DeepState_TestInfo *test) {
|
||||
DeepState_InputIndex = 0;
|
||||
|
||||
for (int i = 0; i < DeepState_InputSize; i++) {
|
||||
DeepState_Input[i] = (char)rand();
|
||||
}
|
||||
|
||||
DeepState_Begin(test);
|
||||
|
||||
enum DeepState_TestRunResult result = DeepState_ForkAndRunTest(test);
|
||||
|
||||
if (result == DeepState_TestRunCrash) {
|
||||
DeepState_LogFormat(DeepState_LogError, "Crashed: %s", test->test_name);
|
||||
|
||||
if (HAS_FLAG_output_test_dir) {
|
||||
DeepState_SaveCrashingTest();
|
||||
}
|
||||
|
||||
DeepState_Crash();
|
||||
}
|
||||
|
||||
if (FLAGS_abort_on_fail && ((result == DeepState_TestRunCrash) ||
|
||||
(result == DeepState_TestRunFail))) {
|
||||
assert(0); // Terminate the testing in a way AFL/etc. can see as a crash
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size > sizeof(DeepState_Input)) {
|
||||
return 0; // Just ignore any too-big inputs
|
||||
}
|
||||
|
||||
DeepState_UsingLibFuzzer = 1;
|
||||
|
||||
const char* loud = getenv("LIBFUZZER_LOUD");
|
||||
if (loud != NULL) {
|
||||
FLAGS_log_level = 0;
|
||||
DeepState_LibFuzzerLoud = 1;
|
||||
}
|
||||
|
||||
struct DeepState_TestInfo *test = NULL;
|
||||
|
||||
@ -575,7 +875,15 @@ extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
|
||||
DeepState_Begin(test);
|
||||
|
||||
enum DeepState_TestRunResult result = DeepState_RunTestLLVM(test);
|
||||
enum DeepState_TestRunResult result = DeepState_RunTestNoFork(test);
|
||||
DeepState_CleanUp();
|
||||
|
||||
const char* abort_check = getenv("LIBFUZZER_ABORT_ON_FAIL");
|
||||
if (abort_check != NULL) {
|
||||
if ((result == DeepState_TestRunFail) || (result == DeepState_TestRunCrash)) {
|
||||
assert(0); // Terminate the testing more permanently
|
||||
}
|
||||
}
|
||||
|
||||
DeepState_Teardown();
|
||||
DeepState_CurrentTestRun = NULL;
|
||||
@ -598,8 +906,20 @@ void __assert_fail(const char * assertion, const char * file,
|
||||
}
|
||||
|
||||
void __stack_chk_fail(void) {
|
||||
DeepState_Log(DeepState_LogFatal, "Stack smash detected.");
|
||||
DeepState_Log(DeepState_LogFatal, "Stack smash detected");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
#ifndef LIBFUZZER
|
||||
__attribute__((weak))
|
||||
int main(int argc, char *argv[]) {
|
||||
int ret = 0;
|
||||
DeepState_Setup();
|
||||
DeepState_InitOptions(argc, argv);
|
||||
ret = DeepState_Run();
|
||||
DeepState_Teardown();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
DEEPSTATE_END_EXTERN_C
|
||||
|
||||
@ -47,6 +47,8 @@ static const char *DeepState_LogLevelStr(enum DeepState_LogLevel level) {
|
||||
switch (level) {
|
||||
case DeepState_LogDebug:
|
||||
return "DEBUG";
|
||||
case DeepState_LogTrace:
|
||||
return "TRACE";
|
||||
case DeepState_LogInfo:
|
||||
return "INFO";
|
||||
case DeepState_LogWarning:
|
||||
@ -56,7 +58,7 @@ static const char *DeepState_LogLevelStr(enum DeepState_LogLevel level) {
|
||||
case DeepState_LogExternal:
|
||||
return "EXTERNAL";
|
||||
case DeepState_LogFatal:
|
||||
return "FATAL";
|
||||
return "CRITICAL";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@ -66,14 +68,16 @@ enum {
|
||||
DeepState_LogBufSize = 4096
|
||||
};
|
||||
|
||||
int DeepState_UsingLibFuzzer = 0;
|
||||
extern int DeepState_UsingLibFuzzer;
|
||||
extern int DeepState_LibFuzzerLoud;
|
||||
|
||||
char DeepState_LogBuf[DeepState_LogBufSize + 1] = {};
|
||||
|
||||
/* Log a C string. */
|
||||
DEEPSTATE_NOINLINE
|
||||
void DeepState_Log(enum DeepState_LogLevel level, const char *str) {
|
||||
if (DeepState_UsingLibFuzzer && (level < DeepState_LogExternal)) {
|
||||
if ((DeepState_UsingLibFuzzer && !DeepState_LibFuzzerLoud && (level < DeepState_LogExternal)) ||
|
||||
(level < FLAGS_log_level)) {
|
||||
return;
|
||||
}
|
||||
memset(DeepState_LogBuf, 0, DeepState_LogBufSize);
|
||||
@ -126,10 +130,16 @@ void DeepState_LogFormat(enum DeepState_LogLevel level,
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch"
|
||||
|
||||
/* Override libc! */
|
||||
DEEPSTATE_NOINLINE
|
||||
int puts(const char *str) {
|
||||
DeepState_Log(DeepState_LogInfo, str);
|
||||
DeepState_Log(DeepState_LogTrace, str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -137,7 +147,7 @@ DEEPSTATE_NOINLINE
|
||||
int printf(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
DeepState_LogVFormat(DeepState_LogInfo, format, args);
|
||||
DeepState_LogVFormat(DeepState_LogTrace, format, args);
|
||||
va_end(args);
|
||||
return 0;
|
||||
}
|
||||
@ -146,20 +156,20 @@ DEEPSTATE_NOINLINE
|
||||
int __printf_chk(int flag, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
DeepState_LogVFormat(DeepState_LogInfo, format, args);
|
||||
DeepState_LogVFormat(DeepState_LogTrace, format, args);
|
||||
va_end(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEEPSTATE_NOINLINE
|
||||
int vprintf(const char *format, va_list args) {
|
||||
DeepState_LogVFormat(DeepState_LogInfo, format, args);
|
||||
DeepState_LogVFormat(DeepState_LogTrace, format, args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEEPSTATE_NOINLINE
|
||||
int __vprintf_chk(int flag, const char *format, va_list args) {
|
||||
DeepState_LogVFormat(DeepState_LogInfo, format, args);
|
||||
DeepState_LogVFormat(DeepState_LogTrace, format, args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -168,7 +178,7 @@ int vfprintf(FILE *file, const char *format, va_list args) {
|
||||
if (stderr == file) {
|
||||
DeepState_LogVFormat(DeepState_LogDebug, format, args);
|
||||
} else if (stdout == file) {
|
||||
DeepState_LogVFormat(DeepState_LogInfo, format, args);
|
||||
DeepState_LogVFormat(DeepState_LogTrace, format, args);
|
||||
} else {
|
||||
DeepState_LogVFormat(DeepState_LogExternal, format, args);
|
||||
}
|
||||
@ -216,4 +226,7 @@ int __vfprintf_chk(int flag, FILE *file, const char *format, va_list args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
DEEPSTATE_END_EXTERN_C
|
||||
|
||||
@ -125,7 +125,8 @@ static void ProcessOptionString(void) {
|
||||
if (' ' == *ch || DeepState_FakeSpace == *ch) {
|
||||
*ch = '\0';
|
||||
state = kSeenSpace;
|
||||
|
||||
} else if ('-' == *ch) {
|
||||
state = kSeenDash;
|
||||
} else if (IsValidValueChar(*ch)) { /* E.g. `--tools bbcount`. */
|
||||
state = kInValue;
|
||||
DeepState_OptionValues[num_options - 1] = ch;
|
||||
|
||||
@ -9,7 +9,8 @@ def logrun(cmd, file, timeout):
|
||||
sys.stderr.write(" ".join(cmd) + "\n\n")
|
||||
sys.stderr.flush()
|
||||
with open(file, 'w') as outf:
|
||||
p = subprocess.Popen(cmd, stdout=outf, stderr=outf)
|
||||
# We need to set log_level so we see ALL messages, for testing
|
||||
p = subprocess.Popen(cmd + ["--log_level", "0"], stdout=outf, stderr=outf)
|
||||
start = time.time()
|
||||
oldContents = ""
|
||||
lastOutput = time.time()
|
||||
|
||||
@ -12,7 +12,7 @@ class CrashTest(deepstate_base.DeepStateTestCase):
|
||||
self.assertTrue("Passed: Crash_SegFault" in output)
|
||||
foundCrashSave = False
|
||||
for line in output.split("\n"):
|
||||
if ("Saving input to" in line) and (".crash" in line):
|
||||
if ("Saved test case" in line) and (".crash" in line):
|
||||
foundCrashSave = True
|
||||
self.assertTrue(foundCrashSave)
|
||||
|
||||
|
||||
20
tests/test_runlen.py
Normal file
20
tests/test_runlen.py
Normal file
@ -0,0 +1,20 @@
|
||||
from __future__ import print_function
|
||||
import deepstate_base
|
||||
import logrun
|
||||
|
||||
|
||||
class RunlenTest(deepstate_base.DeepStateTestCase):
|
||||
def run_deepstate(self, deepstate):
|
||||
if deepstate == "deepstate-manticore":
|
||||
return # Just skip for now, we know it's too slow
|
||||
(r, output) = logrun.logrun([deepstate, "build/examples/Runlen"],
|
||||
"deepstate.out", 2900)
|
||||
self.assertEqual(r, 0)
|
||||
|
||||
self.assertTrue("Passed: Runlength_EncodeDecode" in output)
|
||||
foundFailSave = False
|
||||
for line in output.split("\n"):
|
||||
if ("Saved test case" in line) and (".fail" in line):
|
||||
foundFailSave = True
|
||||
self.assertTrue(foundFailSave)
|
||||
|
||||
@ -11,10 +11,11 @@ class StreamingAndFormattingTest(deepstate_base.DeepStateTestCase):
|
||||
|
||||
self.assertTrue("Failed: Streaming_BasicLevels" in output)
|
||||
self.assertTrue("This is a debug message" in output)
|
||||
self.assertTrue("This is a trace message" in output)
|
||||
self.assertTrue("This is an info message" in output)
|
||||
self.assertTrue("This is a warning message" in output)
|
||||
self.assertTrue("This is a error message" in output)
|
||||
self.assertTrue("This is a info message again" in output)
|
||||
self.assertTrue("This is a trace message again" in output)
|
||||
self.assertTrue(": 97" in output)
|
||||
self.assertTrue(": 1" in output)
|
||||
self.assertTrue(": 1.000000" in output)
|
||||
|
||||
@ -15,6 +15,6 @@ class TakeOverTest(deepstate_base.DeepStateTestCase):
|
||||
|
||||
foundPassSave = False
|
||||
for line in output.split("\n"):
|
||||
if ("Saving input to" in line) and (".pass" in line):
|
||||
if ("Saved test case" in line) and (".pass" in line):
|
||||
foundPassSave = True
|
||||
self.assertTrue(foundPassSave)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user