🔀 merge branch 'release/2.0.10'
This commit is contained in:
commit
1c98ce869c
233 changed files with 10418 additions and 167 deletions
24
.doozer.json
Normal file
24
.doozer.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"targets": {
|
||||
"xenial-i386": {
|
||||
"buildenv": "xenial-i386",
|
||||
"builddeps": ["build-essential", "cmake"],
|
||||
"buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"]
|
||||
},
|
||||
"xenial-amd64": {
|
||||
"buildenv": "xenial-amd64",
|
||||
"builddeps": ["build-essential", "cmake"],
|
||||
"buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"]
|
||||
},
|
||||
"fedora24-x86_64": {
|
||||
"buildenv": "fedora24-x86_64",
|
||||
"builddeps": ["cmake", "make", "clang"],
|
||||
"buildcmd": ["mkdir cm", "cd cm", "CXX=clang++ cmake ..", "cmake --build .", "ctest --output-on-failure"]
|
||||
},
|
||||
"osx": {
|
||||
"buildenv": "osx",
|
||||
"builddeps": ["build-essential"],
|
||||
"buildcmd": ["make check"]
|
||||
}
|
||||
}
|
||||
}
|
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -20,3 +20,12 @@ cmake-build-debug
|
|||
|
||||
|
||||
test/test-*
|
||||
.svn
|
||||
|
||||
test/thirdparty/Fuzzer/libFuzzer.a
|
||||
|
||||
test/parse_afl_fuzzer
|
||||
|
||||
test/parse_cbor_fuzzer
|
||||
|
||||
test/parse_msgpack_fuzzer
|
||||
|
|
25
.travis.yml
25
.travis.yml
|
@ -42,14 +42,20 @@ matrix:
|
|||
- make check TEST_PREFIX="valgrind --error-exitcode=1 --leak-check=full " TEST_PATTERN=""
|
||||
|
||||
# cLang sanitizer
|
||||
|
||||
#- os: linux
|
||||
# env:
|
||||
# - LLVM_VERSION=3.8.1
|
||||
# - SPECIAL=sanitizer
|
||||
# compiler: clang
|
||||
# before_script:
|
||||
# - make clang_sanitize
|
||||
# note: sadly clang's libc++ has errors when running with sanitize,
|
||||
# so we use clang with gcc's libstdc++ which doesn't give those error.
|
||||
# that's why we need to install g++-6 to get the lastest version
|
||||
- os: linux
|
||||
env:
|
||||
- LLVM_VERSION=3.8.1
|
||||
- SPECIAL=sanitizer
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: g++-6
|
||||
compiler: clang
|
||||
before_script:
|
||||
- make clang_sanitize
|
||||
|
||||
# cppcheck
|
||||
|
||||
|
@ -98,9 +104,6 @@ matrix:
|
|||
compiler: clang
|
||||
before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['valgrind']
|
||||
coverity_scan:
|
||||
project:
|
||||
name: "nlohmann/json"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
# define the project
|
||||
project(nlohmann_json VERSION 2.0.9 LANGUAGES CXX)
|
||||
project(nlohmann_json VERSION 2.0.10 LANGUAGES CXX)
|
||||
|
||||
enable_testing()
|
||||
|
||||
|
|
20
ChangeLog.md
20
ChangeLog.md
|
@ -1,6 +1,26 @@
|
|||
# Change Log
|
||||
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [v2.0.10](https://github.com/nlohmann/json/releases/tag/v2.0.10) (2017-01-02)
|
||||
[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.9...v2.0.10)
|
||||
|
||||
- Heap-buffer-overflow \(OSS-Fuzz issue 367\) [\#412](https://github.com/nlohmann/json/issues/412)
|
||||
- Heap-buffer-overflow \(OSS-Fuzz issue 366\) [\#411](https://github.com/nlohmann/json/issues/411)
|
||||
- Use-of-uninitialized-value \(OSS-Fuzz issue 347\) [\#409](https://github.com/nlohmann/json/issues/409)
|
||||
- Heap-buffer-overflow \(OSS-Fuzz issue 344\) [\#408](https://github.com/nlohmann/json/issues/408)
|
||||
- Heap-buffer-overflow \(OSS-Fuzz issue 343\) [\#407](https://github.com/nlohmann/json/issues/407)
|
||||
- Heap-buffer-overflow \(OSS-Fuzz issue 342\) [\#405](https://github.com/nlohmann/json/issues/405)
|
||||
- strerror throwing error in compiler VS2015 [\#403](https://github.com/nlohmann/json/issues/403)
|
||||
- json::parse of std::string being underlined by Visual Studio [\#402](https://github.com/nlohmann/json/issues/402)
|
||||
- Explicitly getting string without .dump\(\) [\#401](https://github.com/nlohmann/json/issues/401)
|
||||
- Possible to speed up json::parse? [\#398](https://github.com/nlohmann/json/issues/398)
|
||||
- the alphabetic order in the code influence console\_output. [\#396](https://github.com/nlohmann/json/issues/396)
|
||||
- Execute tests with clang sanitizers [\#394](https://github.com/nlohmann/json/issues/394)
|
||||
- Check if library can be used with ETL [\#361](https://github.com/nlohmann/json/issues/361)
|
||||
|
||||
- Feature/clang sanitize [\#410](https://github.com/nlohmann/json/pull/410) ([Daniel599](https://github.com/Daniel599))
|
||||
- Add Doozer build badge [\#400](https://github.com/nlohmann/json/pull/400) ([andoma](https://github.com/andoma))
|
||||
|
||||
## [v2.0.9](https://github.com/nlohmann/json/releases/tag/v2.0.9) (2016-12-16)
|
||||
[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.8...v2.0.9)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
JSON for Modern C++ is licensed under the MIT License
|
||||
<http://opensource.org/licenses/MIT>:
|
||||
|
||||
Copyright (c) 2013-2016 Niels Lohmann
|
||||
Copyright (c) 2013-2017 Niels Lohmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
|
40
Makefile
40
Makefile
|
@ -10,7 +10,7 @@ all:
|
|||
|
||||
# clean up
|
||||
clean:
|
||||
rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM
|
||||
rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM test/*.dSYM
|
||||
rm -fr benchmarks/files/numbers/*.json
|
||||
$(MAKE) clean -Cdoc
|
||||
$(MAKE) clean -Ctest
|
||||
|
@ -49,15 +49,40 @@ doctest:
|
|||
fuzz_testing:
|
||||
rm -fr fuzz-testing
|
||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||
$(MAKE) fuzz CXX=afl-clang++
|
||||
mv fuzz fuzz-testing
|
||||
$(MAKE) parse_afl_fuzzer -C test CXX=afl-clang++
|
||||
mv test/parse_afl_fuzzer fuzz-testing/fuzzer
|
||||
find test/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases
|
||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzz"
|
||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
||||
|
||||
# the fuzzer binary
|
||||
fuzz: test/src/fuzz.cpp src/json.hpp
|
||||
$(CXX) -std=c++11 $(CXXFLAGS) $(FLAGS) $(CPPFLAGS) -I src $< $(LDFLAGS) -o $@
|
||||
fuzz_testing_cbor:
|
||||
rm -fr fuzz-testing
|
||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||
$(MAKE) parse_cbor_fuzzer -C test CXX=afl-clang++
|
||||
mv test/parse_cbor_fuzzer fuzz-testing/fuzzer
|
||||
find test/data -size -5k -name *.cbor | xargs -I{} cp "{}" fuzz-testing/testcases
|
||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
||||
|
||||
fuzz_testing_msgpack:
|
||||
rm -fr fuzz-testing
|
||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||
$(MAKE) parse_msgpack_fuzzer -C test CXX=afl-clang++
|
||||
mv test/parse_msgpack_fuzzer fuzz-testing/fuzzer
|
||||
find test/data -size -5k -name *.msgpack | xargs -I{} cp "{}" fuzz-testing/testcases
|
||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
||||
|
||||
fuzzing-start:
|
||||
afl-fuzz -S fuzzer1 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||
afl-fuzz -S fuzzer2 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||
afl-fuzz -S fuzzer3 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||
afl-fuzz -S fuzzer4 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||
afl-fuzz -S fuzzer5 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||
afl-fuzz -S fuzzer6 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||
afl-fuzz -S fuzzer7 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||
afl-fuzz -M fuzzer0 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer
|
||||
|
||||
fuzzing-stop:
|
||||
-killall fuzzer
|
||||
-killall afl-fuzz
|
||||
|
||||
##########################################################################
|
||||
# static analyzer
|
||||
|
@ -67,6 +92,7 @@ fuzz: test/src/fuzz.cpp src/json.hpp
|
|||
cppcheck:
|
||||
cppcheck --enable=warning --inconclusive --force --std=c++11 src/json.hpp --error-exitcode=1
|
||||
|
||||
# run clang sanitize (we are overrding the CXXFLAGS provided by travis in order to use gcc's libstdc++)
|
||||
clang_sanitize: clean
|
||||
CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE)
|
||||
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
[![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json)
|
||||
[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
|
||||
[![Build status](https://doozer.io/badge/nlohmann/json/buildstatus/develop)](https://doozer.io/user/nlohmann/json)
|
||||
[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
|
||||
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
|
||||
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/8soFCqS532vOyZcK)
|
||||
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/IoZNMHqubixQx2dN)
|
||||
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
|
||||
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
|
||||
[![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
|
||||
|
@ -585,6 +586,7 @@ I deeply appreciate the help of the following people.
|
|||
- [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning.
|
||||
- [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check.
|
||||
- [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one.
|
||||
- [Daniel599](https://github.com/Daniel599) helped to get Travis execute the tests with Clang's sanitizers.
|
||||
|
||||
Thanks a lot for helping out!
|
||||
|
||||
|
@ -608,7 +610,7 @@ To compile and run the tests, you need to execute
|
|||
$ make check
|
||||
|
||||
===============================================================================
|
||||
All tests passed (11201893 assertions in 43 test cases)
|
||||
All tests passed (11202040 assertions in 44 test cases)
|
||||
```
|
||||
|
||||
Alternatively, you can use [CMake](https://cmake.org) and run
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = "JSON for Modern C++"
|
||||
PROJECT_NUMBER = 2.0.9
|
||||
PROJECT_NUMBER = 2.0.10
|
||||
PROJECT_BRIEF =
|
||||
PROJECT_LOGO =
|
||||
OUTPUT_DIRECTORY = .
|
||||
|
|
|
@ -57,6 +57,7 @@ doxygen: create_output create_links
|
|||
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType >@@g' html/*.html
|
||||
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html
|
||||
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html
|
||||
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html
|
||||
|
||||
upload: clean doxygen check_output
|
||||
cd html ; ../scripts/git-update-ghpages nlohmann/json
|
||||
|
|
|
@ -1 +1 @@
|
|||
<a target="_blank" href="http://melpon.org/wandbox/permlink/8soFCqS532vOyZcK"><b>online</b></a>
|
||||
<a target="_blank" href="http://melpon.org/wandbox/permlink/IoZNMHqubixQx2dN"><b>online</b></a>
|
|
@ -272,9 +272,9 @@ The container functions known from STL have been extended to support the differe
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
@copyright Copyright © 2013-2016 Niels Lohmann. The code is licensed under the [MIT License](http://opensource.org/licenses/MIT).
|
||||
@copyright Copyright © 2013-2017 Niels Lohmann. The code is licensed under the [MIT License](http://opensource.org/licenses/MIT).
|
||||
|
||||
@author [Niels Lohmann](http://nlohmann.me)
|
||||
@see https://github.com/nlohmann/json to download the source code
|
||||
|
||||
@version 2.0.9
|
||||
@version 2.0.10
|
||||
|
|
BIN
doc/json.gif
BIN
doc/json.gif
Binary file not shown.
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
100
src/json.hpp
100
src/json.hpp
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -873,8 +873,17 @@ class basic_json
|
|||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if (t == value_t::null)
|
||||
{
|
||||
throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3793,7 +3802,7 @@ class basic_json
|
|||
container `c`, the expression `c.front()` is equivalent to `*c.begin()`.
|
||||
|
||||
@return In case of a structured type (array or object), a reference to the
|
||||
first element is returned. In cast of number, string, or boolean values, a
|
||||
first element is returned. In case of number, string, or boolean values, a
|
||||
reference to the value is returned.
|
||||
|
||||
@complexity Constant.
|
||||
|
@ -3836,7 +3845,7 @@ class basic_json
|
|||
@endcode
|
||||
|
||||
@return In case of a structured type (array or object), a reference to the
|
||||
last element is returned. In cast of number, string, or boolean values, a
|
||||
last element is returned. In case of number, string, or boolean values, a
|
||||
reference to the value is returned.
|
||||
|
||||
@complexity Constant.
|
||||
|
@ -4187,10 +4196,14 @@ class basic_json
|
|||
element is not found or the JSON value is not an object, end() is
|
||||
returned.
|
||||
|
||||
@note This method always returns @ref end() when executed on a JSON type
|
||||
that is not an object.
|
||||
|
||||
@param[in] key key value of the element to search for
|
||||
|
||||
@return Iterator to an element with key equivalent to @a key. If no such
|
||||
element is found, past-the-end (see end()) iterator is returned.
|
||||
element is found or the JSON value is not an object, past-the-end (see
|
||||
@ref end()) iterator is returned.
|
||||
|
||||
@complexity Logarithmic in the size of the JSON object.
|
||||
|
||||
|
@ -4233,6 +4246,9 @@ class basic_json
|
|||
default `std::map` type, the return value will always be `0` (@a key was
|
||||
not found) or `1` (@a key was found).
|
||||
|
||||
@note This method always returns `0` when executed on a JSON type that is
|
||||
not an object.
|
||||
|
||||
@param[in] key key value of the element to count
|
||||
|
||||
@return Number of elements with key @a key. If the JSON value is not an
|
||||
|
@ -4792,9 +4808,6 @@ class basic_json
|
|||
object | `{}`
|
||||
array | `[]`
|
||||
|
||||
@note Floating-point numbers are set to `0.0` which will be serialized to
|
||||
`0`. The vale type remains @ref number_float_t.
|
||||
|
||||
@complexity Linear in the size of the JSON value.
|
||||
|
||||
@liveexample{The example below shows the effect of `clear()` to different
|
||||
|
@ -6831,6 +6844,50 @@ class basic_json
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@brief checks if given lengths do not exceed the size of a given vector
|
||||
|
||||
To secure the access to the byte vector during CBOR/MessagePack
|
||||
deserialization, bytes are copied from the vector into buffers. This
|
||||
function checks if the number of bytes to copy (@a len) does not exceed the
|
||||
size @s size of the vector. Additionally, an @a offset is given from where
|
||||
to start reading the bytes.
|
||||
|
||||
This function checks whether reading the bytes is safe; that is, offset is a
|
||||
valid index in the vector, offset+len
|
||||
|
||||
@param[in] size size of the byte vector
|
||||
@param[in] len number of bytes to read
|
||||
@param[in] offset offset where to start reading
|
||||
|
||||
vec: x x x x x X X X X X
|
||||
^ ^ ^
|
||||
0 offset len
|
||||
|
||||
@throws out_of_range if `len > v.size()`
|
||||
*/
|
||||
static void check_length(const size_t size, const size_t len, const size_t offset)
|
||||
{
|
||||
// simple case: requested length is greater than the vector's length
|
||||
if (len > size or offset > size)
|
||||
{
|
||||
throw std::out_of_range("len out of range");
|
||||
}
|
||||
|
||||
// second case: adding offset would result in overflow
|
||||
if ((size > (std::numeric_limits<size_t>::max() - offset)))
|
||||
{
|
||||
throw std::out_of_range("len+offset out of range");
|
||||
}
|
||||
|
||||
// last case: reading past the end of the vector
|
||||
if (len + offset > size)
|
||||
{
|
||||
throw std::out_of_range("len+offset out of range");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief create a JSON value from a given MessagePack vector
|
||||
|
||||
|
@ -6847,6 +6904,9 @@ class basic_json
|
|||
*/
|
||||
static basic_json from_msgpack_internal(const std::vector<uint8_t>& v, size_t& idx)
|
||||
{
|
||||
// make sure reading 1 byte is safe
|
||||
check_length(v.size(), 1, idx);
|
||||
|
||||
// store and increment index
|
||||
const size_t current_idx = idx++;
|
||||
|
||||
|
@ -6882,6 +6942,7 @@ class basic_json
|
|||
const size_t len = v[current_idx] & 0x1f;
|
||||
const size_t offset = current_idx + 1;
|
||||
idx += len; // skip content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
}
|
||||
|
@ -6911,6 +6972,7 @@ class basic_json
|
|||
case 0xca: // float 32
|
||||
{
|
||||
// copy bytes in reverse order into the double variable
|
||||
check_length(v.size(), sizeof(float), 1);
|
||||
float res;
|
||||
for (size_t byte = 0; byte < sizeof(float); ++byte)
|
||||
{
|
||||
|
@ -6923,6 +6985,7 @@ class basic_json
|
|||
case 0xcb: // float 64
|
||||
{
|
||||
// copy bytes in reverse order into the double variable
|
||||
check_length(v.size(), sizeof(double), 1);
|
||||
double res;
|
||||
for (size_t byte = 0; byte < sizeof(double); ++byte)
|
||||
{
|
||||
|
@ -6985,6 +7048,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 2;
|
||||
idx += len + 1; // skip size byte + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -6993,6 +7057,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 3;
|
||||
idx += len + 2; // skip 2 size bytes + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7001,6 +7066,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 5;
|
||||
idx += len + 4; // skip 4 size bytes + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7081,7 +7147,7 @@ class basic_json
|
|||
// store and increment index
|
||||
const size_t current_idx = idx++;
|
||||
|
||||
switch (v[current_idx])
|
||||
switch (v.at(current_idx))
|
||||
{
|
||||
// Integer 0x00..0x17 (0..23)
|
||||
case 0x00:
|
||||
|
@ -7219,6 +7285,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(v[current_idx] - 0x60);
|
||||
const size_t offset = current_idx + 1;
|
||||
idx += len; // skip content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7227,6 +7294,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 2;
|
||||
idx += len + 1; // skip size byte + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7235,6 +7303,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 3;
|
||||
idx += len + 2; // skip 2 size bytes + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7243,6 +7312,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 5;
|
||||
idx += len + 4; // skip 4 size bytes + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7251,13 +7321,14 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 9;
|
||||
idx += len + 8; // skip 8 size bytes + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
case 0x7f: // UTF-8 string (indefinite length)
|
||||
{
|
||||
std::string result;
|
||||
while (v[idx] != 0xff)
|
||||
while (v.at(idx) != 0xff)
|
||||
{
|
||||
string_t s = from_cbor_internal(v, idx);
|
||||
result += s;
|
||||
|
@ -7353,7 +7424,7 @@ class basic_json
|
|||
case 0x9f: // array (indefinite length)
|
||||
{
|
||||
basic_json result = value_t::array;
|
||||
while (v[idx] != 0xff)
|
||||
while (v.at(idx) != 0xff)
|
||||
{
|
||||
result.push_back(from_cbor_internal(v, idx));
|
||||
}
|
||||
|
@ -7453,7 +7524,7 @@ class basic_json
|
|||
case 0xbf: // map (indefinite length)
|
||||
{
|
||||
basic_json result = value_t::object;
|
||||
while (v[idx] != 0xff)
|
||||
while (v.at(idx) != 0xff)
|
||||
{
|
||||
std::string key = from_cbor_internal(v, idx);
|
||||
result[key] = from_cbor_internal(v, idx);
|
||||
|
@ -7480,6 +7551,7 @@ class basic_json
|
|||
|
||||
case 0xf9: // Half-Precision Float (two-byte IEEE 754)
|
||||
{
|
||||
check_length(v.size(), 2, 1);
|
||||
idx += 2; // skip two content bytes
|
||||
|
||||
// code from RFC 7049, Appendix D, Figure 3:
|
||||
|
@ -7511,6 +7583,7 @@ class basic_json
|
|||
case 0xfa: // Single-Precision Float (four-byte IEEE 754)
|
||||
{
|
||||
// copy bytes in reverse order into the float variable
|
||||
check_length(v.size(), sizeof(float), 1);
|
||||
float res;
|
||||
for (size_t byte = 0; byte < sizeof(float); ++byte)
|
||||
{
|
||||
|
@ -7522,6 +7595,7 @@ class basic_json
|
|||
|
||||
case 0xfb: // Double-Precision Float (eight-byte IEEE 754)
|
||||
{
|
||||
check_length(v.size(), sizeof(double), 1);
|
||||
// copy bytes in reverse order into the double variable
|
||||
double res;
|
||||
for (size_t byte = 0; byte < sizeof(double); ++byte)
|
||||
|
@ -8995,7 +9069,7 @@ class basic_json
|
|||
// immediately abort if stream is erroneous
|
||||
if (s.fail())
|
||||
{
|
||||
throw std::invalid_argument("stream error: " + std::string(strerror(errno)));
|
||||
throw std::invalid_argument("stream error");
|
||||
}
|
||||
|
||||
// fill buffer
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -873,8 +873,17 @@ class basic_json
|
|||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if (t == value_t::null)
|
||||
{
|
||||
throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3793,7 +3802,7 @@ class basic_json
|
|||
container `c`, the expression `c.front()` is equivalent to `*c.begin()`.
|
||||
|
||||
@return In case of a structured type (array or object), a reference to the
|
||||
first element is returned. In cast of number, string, or boolean values, a
|
||||
first element is returned. In case of number, string, or boolean values, a
|
||||
reference to the value is returned.
|
||||
|
||||
@complexity Constant.
|
||||
|
@ -3836,7 +3845,7 @@ class basic_json
|
|||
@endcode
|
||||
|
||||
@return In case of a structured type (array or object), a reference to the
|
||||
last element is returned. In cast of number, string, or boolean values, a
|
||||
last element is returned. In case of number, string, or boolean values, a
|
||||
reference to the value is returned.
|
||||
|
||||
@complexity Constant.
|
||||
|
@ -4187,10 +4196,14 @@ class basic_json
|
|||
element is not found or the JSON value is not an object, end() is
|
||||
returned.
|
||||
|
||||
@note This method always returns @ref end() when executed on a JSON type
|
||||
that is not an object.
|
||||
|
||||
@param[in] key key value of the element to search for
|
||||
|
||||
@return Iterator to an element with key equivalent to @a key. If no such
|
||||
element is found, past-the-end (see end()) iterator is returned.
|
||||
element is found or the JSON value is not an object, past-the-end (see
|
||||
@ref end()) iterator is returned.
|
||||
|
||||
@complexity Logarithmic in the size of the JSON object.
|
||||
|
||||
|
@ -4233,6 +4246,9 @@ class basic_json
|
|||
default `std::map` type, the return value will always be `0` (@a key was
|
||||
not found) or `1` (@a key was found).
|
||||
|
||||
@note This method always returns `0` when executed on a JSON type that is
|
||||
not an object.
|
||||
|
||||
@param[in] key key value of the element to count
|
||||
|
||||
@return Number of elements with key @a key. If the JSON value is not an
|
||||
|
@ -4792,9 +4808,6 @@ class basic_json
|
|||
object | `{}`
|
||||
array | `[]`
|
||||
|
||||
@note Floating-point numbers are set to `0.0` which will be serialized to
|
||||
`0`. The vale type remains @ref number_float_t.
|
||||
|
||||
@complexity Linear in the size of the JSON value.
|
||||
|
||||
@liveexample{The example below shows the effect of `clear()` to different
|
||||
|
@ -6831,6 +6844,50 @@ class basic_json
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@brief checks if given lengths do not exceed the size of a given vector
|
||||
|
||||
To secure the access to the byte vector during CBOR/MessagePack
|
||||
deserialization, bytes are copied from the vector into buffers. This
|
||||
function checks if the number of bytes to copy (@a len) does not exceed the
|
||||
size @s size of the vector. Additionally, an @a offset is given from where
|
||||
to start reading the bytes.
|
||||
|
||||
This function checks whether reading the bytes is safe; that is, offset is a
|
||||
valid index in the vector, offset+len
|
||||
|
||||
@param[in] size size of the byte vector
|
||||
@param[in] len number of bytes to read
|
||||
@param[in] offset offset where to start reading
|
||||
|
||||
vec: x x x x x X X X X X
|
||||
^ ^ ^
|
||||
0 offset len
|
||||
|
||||
@throws out_of_range if `len > v.size()`
|
||||
*/
|
||||
static void check_length(const size_t size, const size_t len, const size_t offset)
|
||||
{
|
||||
// simple case: requested length is greater than the vector's length
|
||||
if (len > size or offset > size)
|
||||
{
|
||||
throw std::out_of_range("len out of range");
|
||||
}
|
||||
|
||||
// second case: adding offset would result in overflow
|
||||
if ((size > (std::numeric_limits<size_t>::max() - offset)))
|
||||
{
|
||||
throw std::out_of_range("len+offset out of range");
|
||||
}
|
||||
|
||||
// last case: reading past the end of the vector
|
||||
if (len + offset > size)
|
||||
{
|
||||
throw std::out_of_range("len+offset out of range");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief create a JSON value from a given MessagePack vector
|
||||
|
||||
|
@ -6847,6 +6904,9 @@ class basic_json
|
|||
*/
|
||||
static basic_json from_msgpack_internal(const std::vector<uint8_t>& v, size_t& idx)
|
||||
{
|
||||
// make sure reading 1 byte is safe
|
||||
check_length(v.size(), 1, idx);
|
||||
|
||||
// store and increment index
|
||||
const size_t current_idx = idx++;
|
||||
|
||||
|
@ -6882,6 +6942,7 @@ class basic_json
|
|||
const size_t len = v[current_idx] & 0x1f;
|
||||
const size_t offset = current_idx + 1;
|
||||
idx += len; // skip content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
}
|
||||
|
@ -6911,6 +6972,7 @@ class basic_json
|
|||
case 0xca: // float 32
|
||||
{
|
||||
// copy bytes in reverse order into the double variable
|
||||
check_length(v.size(), sizeof(float), 1);
|
||||
float res;
|
||||
for (size_t byte = 0; byte < sizeof(float); ++byte)
|
||||
{
|
||||
|
@ -6923,6 +6985,7 @@ class basic_json
|
|||
case 0xcb: // float 64
|
||||
{
|
||||
// copy bytes in reverse order into the double variable
|
||||
check_length(v.size(), sizeof(double), 1);
|
||||
double res;
|
||||
for (size_t byte = 0; byte < sizeof(double); ++byte)
|
||||
{
|
||||
|
@ -6985,6 +7048,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 2;
|
||||
idx += len + 1; // skip size byte + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -6993,6 +7057,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 3;
|
||||
idx += len + 2; // skip 2 size bytes + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7001,6 +7066,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 5;
|
||||
idx += len + 4; // skip 4 size bytes + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7081,7 +7147,7 @@ class basic_json
|
|||
// store and increment index
|
||||
const size_t current_idx = idx++;
|
||||
|
||||
switch (v[current_idx])
|
||||
switch (v.at(current_idx))
|
||||
{
|
||||
// Integer 0x00..0x17 (0..23)
|
||||
case 0x00:
|
||||
|
@ -7219,6 +7285,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(v[current_idx] - 0x60);
|
||||
const size_t offset = current_idx + 1;
|
||||
idx += len; // skip content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7227,6 +7294,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 2;
|
||||
idx += len + 1; // skip size byte + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7235,6 +7303,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 3;
|
||||
idx += len + 2; // skip 2 size bytes + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7243,6 +7312,7 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 5;
|
||||
idx += len + 4; // skip 4 size bytes + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
|
@ -7251,13 +7321,14 @@ class basic_json
|
|||
const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx));
|
||||
const size_t offset = current_idx + 9;
|
||||
idx += len + 8; // skip 8 size bytes + content bytes
|
||||
check_length(v.size(), len, offset);
|
||||
return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
|
||||
}
|
||||
|
||||
case 0x7f: // UTF-8 string (indefinite length)
|
||||
{
|
||||
std::string result;
|
||||
while (v[idx] != 0xff)
|
||||
while (v.at(idx) != 0xff)
|
||||
{
|
||||
string_t s = from_cbor_internal(v, idx);
|
||||
result += s;
|
||||
|
@ -7353,7 +7424,7 @@ class basic_json
|
|||
case 0x9f: // array (indefinite length)
|
||||
{
|
||||
basic_json result = value_t::array;
|
||||
while (v[idx] != 0xff)
|
||||
while (v.at(idx) != 0xff)
|
||||
{
|
||||
result.push_back(from_cbor_internal(v, idx));
|
||||
}
|
||||
|
@ -7453,7 +7524,7 @@ class basic_json
|
|||
case 0xbf: // map (indefinite length)
|
||||
{
|
||||
basic_json result = value_t::object;
|
||||
while (v[idx] != 0xff)
|
||||
while (v.at(idx) != 0xff)
|
||||
{
|
||||
std::string key = from_cbor_internal(v, idx);
|
||||
result[key] = from_cbor_internal(v, idx);
|
||||
|
@ -7480,6 +7551,7 @@ class basic_json
|
|||
|
||||
case 0xf9: // Half-Precision Float (two-byte IEEE 754)
|
||||
{
|
||||
check_length(v.size(), 2, 1);
|
||||
idx += 2; // skip two content bytes
|
||||
|
||||
// code from RFC 7049, Appendix D, Figure 3:
|
||||
|
@ -7511,6 +7583,7 @@ class basic_json
|
|||
case 0xfa: // Single-Precision Float (four-byte IEEE 754)
|
||||
{
|
||||
// copy bytes in reverse order into the float variable
|
||||
check_length(v.size(), sizeof(float), 1);
|
||||
float res;
|
||||
for (size_t byte = 0; byte < sizeof(float); ++byte)
|
||||
{
|
||||
|
@ -7522,6 +7595,7 @@ class basic_json
|
|||
|
||||
case 0xfb: // Double-Precision Float (eight-byte IEEE 754)
|
||||
{
|
||||
check_length(v.size(), sizeof(double), 1);
|
||||
// copy bytes in reverse order into the double variable
|
||||
double res;
|
||||
for (size_t byte = 0; byte < sizeof(double); ++byte)
|
||||
|
@ -8995,7 +9069,7 @@ class basic_json
|
|||
// immediately abort if stream is erroneous
|
||||
if (s.fail())
|
||||
{
|
||||
throw std::invalid_argument("stream error: " + std::string(strerror(errno)));
|
||||
throw std::invalid_argument("stream error");
|
||||
}
|
||||
|
||||
// fill buffer
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# The unit test executable.
|
||||
set(JSON_UNITTEST_TARGET_NAME "json_unit")
|
||||
add_executable(${JSON_UNITTEST_TARGET_NAME}
|
||||
"src/catch.hpp"
|
||||
"thirdparty/catch/catch.hpp"
|
||||
"src/unit.cpp"
|
||||
"src/unit-algorithms.cpp"
|
||||
"src/unit-allocator.cpp"
|
||||
|
@ -44,7 +44,7 @@ set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES
|
|||
COMPILE_OPTIONS "$<$<CXX_COMPILER_ID:MSVC>:/EHsc;$<$<CONFIG:Release>:/Od>>"
|
||||
)
|
||||
|
||||
target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src")
|
||||
target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src" "thirdparty/catch")
|
||||
target_link_libraries(${JSON_UNITTEST_TARGET_NAME} ${JSON_TARGET_NAME})
|
||||
|
||||
add_test(NAME "${JSON_UNITTEST_TARGET_NAME}_default"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
# additional flags
|
||||
CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wno-ctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-float-equal
|
||||
CPPFLAGS += -I ../src -I .
|
||||
CPPFLAGS += -I ../src -I . -I thirdparty/catch
|
||||
|
||||
SOURCES = src/unit.cpp \
|
||||
src/unit-algorithms.cpp \
|
||||
|
@ -57,11 +57,11 @@ clean:
|
|||
# single test file
|
||||
##############################################################################
|
||||
|
||||
json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp
|
||||
json_unit: $(OBJECTS) ../src/json.hpp thirdparty/catch/catch.hpp
|
||||
@echo "[CXXLD] $@"
|
||||
@$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@
|
||||
|
||||
%.o: %.cpp ../src/json.hpp src/catch.hpp
|
||||
%.o: %.cpp ../src/json.hpp thirdparty/catch/catch.hpp
|
||||
@echo "[CXX] $@"
|
||||
@$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
|
||||
|
@ -70,7 +70,7 @@ json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp
|
|||
# individual test cases
|
||||
##############################################################################
|
||||
|
||||
test-%: src/unit-%.cpp ../src/json.hpp src/catch.hpp
|
||||
test-%: src/unit-%.cpp ../src/json.hpp thirdparty/catch/catch.hpp
|
||||
@echo "[CXXLD] $@"
|
||||
@$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -DCATCH_CONFIG_MAIN $< -o $@
|
||||
|
||||
|
@ -78,3 +78,19 @@ TEST_PATTERN = "*"
|
|||
TEST_PREFIX = ""
|
||||
check: $(TESTCASES)
|
||||
@cd .. ; for testcase in $(TESTCASES); do echo "Executing $$testcase..."; $(TEST_PREFIX)test/$$testcase $(TEST_PATTERN) || exit 1; done
|
||||
|
||||
|
||||
##############################################################################
|
||||
# fuzzer
|
||||
##############################################################################
|
||||
|
||||
fuzzers: parse_afl_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer
|
||||
|
||||
parse_afl_fuzzer:
|
||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_json.cpp -o $@
|
||||
|
||||
parse_cbor_fuzzer:
|
||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_cbor.cpp -o $@
|
||||
|
||||
parse_msgpack_fuzzer:
|
||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_msgpack.cpp -o $@
|
||||
|
|
1
test/data/cbor_regression/test01
Normal file
1
test/data/cbor_regression/test01
Normal file
|
@ -0,0 +1 @@
|
|||
亄<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
BIN
test/data/cbor_regression/test02
Normal file
BIN
test/data/cbor_regression/test02
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test03
Normal file
BIN
test/data/cbor_regression/test03
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test04
Normal file
BIN
test/data/cbor_regression/test04
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test05
Normal file
BIN
test/data/cbor_regression/test05
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test06
Normal file
BIN
test/data/cbor_regression/test06
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test07
Normal file
BIN
test/data/cbor_regression/test07
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test08
Normal file
BIN
test/data/cbor_regression/test08
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test09
Normal file
BIN
test/data/cbor_regression/test09
Normal file
Binary file not shown.
1
test/data/cbor_regression/test10
Normal file
1
test/data/cbor_regression/test10
Normal file
|
@ -0,0 +1 @@
|
|||
”{˙˙˙˙˙˙˙˙˙’˙˙˙˙˙˙˙˙úúúúúúúúúúúúetú
|
BIN
test/data/cbor_regression/test11
Normal file
BIN
test/data/cbor_regression/test11
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test12
Normal file
BIN
test/data/cbor_regression/test12
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test13
Normal file
BIN
test/data/cbor_regression/test13
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test14
Normal file
BIN
test/data/cbor_regression/test14
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test15
Normal file
BIN
test/data/cbor_regression/test15
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test16
Normal file
BIN
test/data/cbor_regression/test16
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test17
Normal file
BIN
test/data/cbor_regression/test17
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test18
Normal file
BIN
test/data/cbor_regression/test18
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test19
Normal file
BIN
test/data/cbor_regression/test19
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test20
Normal file
BIN
test/data/cbor_regression/test20
Normal file
Binary file not shown.
BIN
test/data/cbor_regression/test21
Normal file
BIN
test/data/cbor_regression/test21
Normal file
Binary file not shown.
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Run "make fuzz_testing" and follow the instructions.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
while (__AFL_LOOP(1000))
|
||||
{
|
||||
#endif
|
||||
try
|
||||
{
|
||||
json j(std::cin);
|
||||
std::cout << j << std::endl;
|
||||
}
|
||||
catch (std::invalid_argument& e)
|
||||
{
|
||||
std::cout << "Invalid argument in parsing" << e.what() << '\n';
|
||||
}
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
}
|
||||
#endif
|
||||
}
|
38
test/src/fuzzer-driver_afl.cpp
Normal file
38
test/src/fuzzer-driver_afl.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on
|
||||
an implementation of the `LLVMFuzzerTestOneInput` function which processes a
|
||||
passed byte array.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <vector> // for vector
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <iostream> // for cin
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
while (__AFL_LOOP(1000))
|
||||
{
|
||||
#endif
|
||||
// copy stdin to byte vector
|
||||
std::vector<uint8_t> vec;
|
||||
char c;
|
||||
while (std::cin.get(c))
|
||||
{
|
||||
vec.push_back(static_cast<uint8_t>(c));
|
||||
}
|
||||
|
||||
LLVMFuzzerTestOneInput(vec.data(), vec.size());
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
}
|
||||
#endif
|
||||
}
|
68
test/src/fuzzer-parse_cbor.cpp
Normal file
68
test/src/fuzzer-parse_cbor.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||
array data, it performs the following steps:
|
||||
|
||||
- j1 = from_cbor(data)
|
||||
- vec = to_cbor(j1)
|
||||
- j2 = from_cbor(vec)
|
||||
- assert(j1 == j2)
|
||||
|
||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||
drivers.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// see http://llvm.org/docs/LibFuzzer.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
// step 1: parse input
|
||||
std::vector<uint8_t> vec1(data, data + size);
|
||||
json j1 = json::from_cbor(vec1);
|
||||
|
||||
try
|
||||
{
|
||||
// step 2: round trip
|
||||
std::vector<uint8_t> vec2 = json::to_cbor(j1);
|
||||
|
||||
// parse serialization
|
||||
json j2 = json::from_cbor(vec2);
|
||||
|
||||
// deserializations must match
|
||||
assert(j1 == j2);
|
||||
}
|
||||
catch (const std::invalid_argument&)
|
||||
{
|
||||
// parsing a CBOR serialization must not fail
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (const std::invalid_argument&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const std::out_of_range&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const std::domain_error&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
|
||||
// return 0 - non-zero return values are reserved for future use
|
||||
return 0;
|
||||
}
|
65
test/src/fuzzer-parse_json.cpp
Normal file
65
test/src/fuzzer-parse_json.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||
array data, it performs the following steps:
|
||||
|
||||
- j1 = parse(data)
|
||||
- s1 = serialize(j1)
|
||||
- j2 = parse(s1)
|
||||
- s2 = serialize(j2)
|
||||
- assert(s1 == s2)
|
||||
|
||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||
drivers.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// see http://llvm.org/docs/LibFuzzer.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
// step 1: parse input
|
||||
json j1 = json::parse(data, data + size);
|
||||
|
||||
try
|
||||
{
|
||||
// step 2: round trip
|
||||
|
||||
// first serialization
|
||||
std::string s1 = j1.dump();
|
||||
|
||||
// parse serialization
|
||||
json j2 = json::parse(s1);
|
||||
|
||||
// second serialization
|
||||
std::string s2 = j2.dump();
|
||||
|
||||
// serializations must match
|
||||
assert(s1 == s2);
|
||||
}
|
||||
catch (const std::invalid_argument&)
|
||||
{
|
||||
// parsing a JSON serialization must not fail
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (const std::invalid_argument&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
|
||||
// return 0 - non-zero return values are reserved for future use
|
||||
return 0;
|
||||
}
|
68
test/src/fuzzer-parse_msgpack.cpp
Normal file
68
test/src/fuzzer-parse_msgpack.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||
array data, it performs the following steps:
|
||||
|
||||
- j1 = from_msgpack(data)
|
||||
- vec = to_msgpack(j1)
|
||||
- j2 = from_msgpack(vec)
|
||||
- assert(j1 == j2)
|
||||
|
||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||
drivers.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// see http://llvm.org/docs/LibFuzzer.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
// step 1: parse input
|
||||
std::vector<uint8_t> vec1(data, data + size);
|
||||
json j1 = json::from_msgpack(vec1);
|
||||
|
||||
try
|
||||
{
|
||||
// step 2: round trip
|
||||
std::vector<uint8_t> vec2 = json::to_msgpack(j1);
|
||||
|
||||
// parse serialization
|
||||
json j2 = json::from_msgpack(vec2);
|
||||
|
||||
// deserializations must match
|
||||
assert(j1 == j2);
|
||||
}
|
||||
catch (const std::invalid_argument&)
|
||||
{
|
||||
// parsing a MessagePack serialization must not fail
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (const std::invalid_argument&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const std::out_of_range&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const std::domain_error&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
|
||||
// return 0 - non-zero return values are reserved for future use
|
||||
return 0;
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -111,6 +111,16 @@ struct my_allocator : std::allocator<T>
|
|||
}
|
||||
};
|
||||
|
||||
// allows deletion of raw pointer, usually hold by json_value
|
||||
template<class T>
|
||||
void my_allocator_clean_up(T* p)
|
||||
{
|
||||
assert(p != nullptr);
|
||||
my_allocator<T> alloc;
|
||||
alloc.destroy(p);
|
||||
alloc.deallocate(p, 1);
|
||||
}
|
||||
|
||||
TEST_CASE("controlled bad_alloc")
|
||||
{
|
||||
// create JSON type using the throwing allocator
|
||||
|
@ -131,7 +141,11 @@ TEST_CASE("controlled bad_alloc")
|
|||
{
|
||||
next_construct_fails = false;
|
||||
auto t = my_json::value_t::object;
|
||||
CHECK_NOTHROW(my_json::json_value j(t));
|
||||
auto clean_up = [](my_json::json_value & j)
|
||||
{
|
||||
my_allocator_clean_up(j.object);
|
||||
};
|
||||
CHECK_NOTHROW(my_json::json_value j(t); clean_up(j));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc);
|
||||
next_construct_fails = false;
|
||||
|
@ -140,7 +154,11 @@ TEST_CASE("controlled bad_alloc")
|
|||
{
|
||||
next_construct_fails = false;
|
||||
auto t = my_json::value_t::array;
|
||||
CHECK_NOTHROW(my_json::json_value j(t));
|
||||
auto clean_up = [](my_json::json_value & j)
|
||||
{
|
||||
my_allocator_clean_up(j.array);
|
||||
};
|
||||
CHECK_NOTHROW(my_json::json_value j(t); clean_up(j));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc);
|
||||
next_construct_fails = false;
|
||||
|
@ -149,7 +167,11 @@ TEST_CASE("controlled bad_alloc")
|
|||
{
|
||||
next_construct_fails = false;
|
||||
auto t = my_json::value_t::string;
|
||||
CHECK_NOTHROW(my_json::json_value j(t));
|
||||
auto clean_up = [](my_json::json_value & j)
|
||||
{
|
||||
my_allocator_clean_up(j.string);
|
||||
};
|
||||
CHECK_NOTHROW(my_json::json_value j(t); clean_up(j));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc);
|
||||
next_construct_fails = false;
|
||||
|
@ -160,7 +182,11 @@ TEST_CASE("controlled bad_alloc")
|
|||
{
|
||||
next_construct_fails = false;
|
||||
my_json::string_t v("foo");
|
||||
CHECK_NOTHROW(my_json::json_value j(v));
|
||||
auto clean_up = [](my_json::json_value & j)
|
||||
{
|
||||
my_allocator_clean_up(j.string);
|
||||
};
|
||||
CHECK_NOTHROW(my_json::json_value j(v); clean_up(j));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc);
|
||||
next_construct_fails = false;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -28,6 +28,7 @@ SOFTWARE.
|
|||
|
||||
#include "catch.hpp"
|
||||
|
||||
#define private public
|
||||
#include "json.hpp"
|
||||
using nlohmann::json;
|
||||
|
||||
|
@ -1186,6 +1187,90 @@ TEST_CASE("single CBOR roundtrip")
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("CBOR regressions")
|
||||
{
|
||||
SECTION("fuzz test results")
|
||||
{
|
||||
/*
|
||||
The following test cases were found during a two-day session with
|
||||
AFL-Fuzz. As a result, empty byte vectors and excessive lengths are
|
||||
detected.
|
||||
*/
|
||||
for (std::string filename :
|
||||
{
|
||||
"test/data/cbor_regression/test01",
|
||||
"test/data/cbor_regression/test02",
|
||||
"test/data/cbor_regression/test03",
|
||||
"test/data/cbor_regression/test04",
|
||||
"test/data/cbor_regression/test05",
|
||||
"test/data/cbor_regression/test06",
|
||||
"test/data/cbor_regression/test07",
|
||||
"test/data/cbor_regression/test08",
|
||||
"test/data/cbor_regression/test09",
|
||||
"test/data/cbor_regression/test10",
|
||||
"test/data/cbor_regression/test11",
|
||||
"test/data/cbor_regression/test12",
|
||||
"test/data/cbor_regression/test13",
|
||||
"test/data/cbor_regression/test14",
|
||||
"test/data/cbor_regression/test15",
|
||||
"test/data/cbor_regression/test16",
|
||||
"test/data/cbor_regression/test17",
|
||||
"test/data/cbor_regression/test18",
|
||||
"test/data/cbor_regression/test19",
|
||||
"test/data/cbor_regression/test20",
|
||||
"test/data/cbor_regression/test21"
|
||||
})
|
||||
{
|
||||
CAPTURE(filename);
|
||||
|
||||
try
|
||||
{
|
||||
// parse CBOR file
|
||||
std::ifstream f_cbor(filename, std::ios::binary);
|
||||
std::vector<uint8_t> vec1(
|
||||
(std::istreambuf_iterator<char>(f_cbor)),
|
||||
std::istreambuf_iterator<char>());
|
||||
json j1 = json::from_cbor(vec1);
|
||||
|
||||
try
|
||||
{
|
||||
// step 2: round trip
|
||||
std::vector<uint8_t> vec2 = json::to_cbor(j1);
|
||||
|
||||
// parse serialization
|
||||
json j2 = json::from_cbor(vec2);
|
||||
|
||||
// deserializations must match
|
||||
CHECK(j1 == j2);
|
||||
}
|
||||
catch (const std::invalid_argument&)
|
||||
{
|
||||
// parsing a CBOR serialization must not fail
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
catch (const std::invalid_argument&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const std::out_of_range&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const std::domain_error&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("improve code coverage")
|
||||
{
|
||||
// exotic edge case
|
||||
CHECK_THROWS_AS(json::check_length(0xffffffffffffffff, 0xfffffffffffffff0, 0xff), std::out_of_range);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("CBOR roundtrips", "[hide]")
|
||||
{
|
||||
SECTION("input from flynn")
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -540,4 +540,127 @@ TEST_CASE("regression tests")
|
|||
CHECK(j.is_number_float());
|
||||
CHECK(j.dump() == "1.66020696663386e+20");
|
||||
}
|
||||
|
||||
SECTION("issue #405 - Heap-buffer-overflow (OSS-Fuzz issue 342)")
|
||||
{
|
||||
// original test case
|
||||
std::vector<uint8_t> vec {0x65, 0xf5, 0x0a, 0x48, 0x21};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range);
|
||||
}
|
||||
|
||||
SECTION("issue #407 - Heap-buffer-overflow (OSS-Fuzz issue 343)")
|
||||
{
|
||||
// original test case: incomplete float64
|
||||
std::vector<uint8_t> vec1 {0xcb, 0x8f, 0x0a};
|
||||
CHECK_THROWS_AS(json::from_msgpack(vec1), std::out_of_range);
|
||||
|
||||
// related test case: incomplete float32
|
||||
std::vector<uint8_t> vec2 {0xca, 0x8f, 0x0a};
|
||||
CHECK_THROWS_AS(json::from_msgpack(vec2), std::out_of_range);
|
||||
|
||||
// related test case: incomplete Half-Precision Float (CBOR)
|
||||
std::vector<uint8_t> vec3 {0xf9, 0x8f};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range);
|
||||
|
||||
// related test case: incomplete Single-Precision Float (CBOR)
|
||||
std::vector<uint8_t> vec4 {0xfa, 0x8f, 0x0a};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec4), std::out_of_range);
|
||||
|
||||
// related test case: incomplete Double-Precision Float (CBOR)
|
||||
std::vector<uint8_t> vec5 {0xfb, 0x8f, 0x0a};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec5), std::out_of_range);
|
||||
}
|
||||
|
||||
SECTION("issue #408 - Heap-buffer-overflow (OSS-Fuzz issue 344)")
|
||||
{
|
||||
// original test case
|
||||
std::vector<uint8_t> vec1 {0x87};
|
||||
CHECK_THROWS_AS(json::from_msgpack(vec1), std::out_of_range);
|
||||
|
||||
// more test cases for MessagePack
|
||||
for (uint8_t b :
|
||||
{
|
||||
0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, // fixmap
|
||||
0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, // fixarray
|
||||
0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, // fixstr
|
||||
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf
|
||||
})
|
||||
{
|
||||
std::vector<uint8_t> vec(1, b);
|
||||
CHECK_THROWS_AS(json::from_msgpack(vec), std::out_of_range);
|
||||
}
|
||||
|
||||
// more test cases for CBOR
|
||||
for (uint8_t b :
|
||||
{
|
||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // UTF-8 string
|
||||
0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // array
|
||||
0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
||||
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7 // map
|
||||
})
|
||||
{
|
||||
std::vector<uint8_t> vec(1, b);
|
||||
CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range);
|
||||
}
|
||||
|
||||
// special case: empty input
|
||||
std::vector<uint8_t> vec2;
|
||||
CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range);
|
||||
CHECK_THROWS_AS(json::from_msgpack(vec2), std::out_of_range);
|
||||
}
|
||||
|
||||
SECTION("issue #411 - Heap-buffer-overflow (OSS-Fuzz issue 366)")
|
||||
{
|
||||
// original test case: empty UTF-8 string (indefinite length)
|
||||
std::vector<uint8_t> vec1 {0x7f};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range);
|
||||
|
||||
// related test case: empty array (indefinite length)
|
||||
std::vector<uint8_t> vec2 {0x9f};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range);
|
||||
|
||||
// related test case: empty map (indefinite length)
|
||||
std::vector<uint8_t> vec3 {0xbf};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range);
|
||||
}
|
||||
|
||||
SECTION("issue #412 - Heap-buffer-overflow (OSS-Fuzz issue 367)")
|
||||
{
|
||||
// original test case
|
||||
std::vector<uint8_t> vec
|
||||
{
|
||||
0xab, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
|
||||
0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x00,
|
||||
0x60, 0xab, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
|
||||
0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x00,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xa0, 0x9f,
|
||||
0x9f, 0x97, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60
|
||||
};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range);
|
||||
|
||||
// related test case: nonempty UTF-8 string (indefinite length)
|
||||
std::vector<uint8_t> vec1 {0x7f, 0x61, 0x61};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range);
|
||||
|
||||
// related test case: nonempty array (indefinite length)
|
||||
std::vector<uint8_t> vec2 {0x9f, 0x01};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range);
|
||||
|
||||
// related test case: nonempty map (indefinite length)
|
||||
std::vector<uint8_t> vec3 {0xbf, 0x61, 0x61, 0x01};
|
||||
CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 2.0.9
|
||||
| | |__ | | | | | | version 2.0.10
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
45
test/thirdparty/Fuzzer/CMakeLists.txt
vendored
Normal file
45
test/thirdparty/Fuzzer/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
set(LIBFUZZER_FLAGS_BASE "${CMAKE_CXX_FLAGS}")
|
||||
# Disable the coverage and sanitizer instrumentation for the fuzzer itself.
|
||||
set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters -Werror")
|
||||
if( LLVM_USE_SANITIZE_COVERAGE )
|
||||
if(NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address")
|
||||
message(FATAL_ERROR
|
||||
"LibFuzzer and its tests require LLVM_USE_SANITIZER=Address and "
|
||||
"LLVM_USE_SANITIZE_COVERAGE=YES to be set."
|
||||
)
|
||||
endif()
|
||||
add_library(LLVMFuzzerNoMainObjects OBJECT
|
||||
FuzzerCrossOver.cpp
|
||||
FuzzerDriver.cpp
|
||||
FuzzerExtFunctionsDlsym.cpp
|
||||
FuzzerExtFunctionsWeak.cpp
|
||||
FuzzerExtFunctionsWeakAlias.cpp
|
||||
FuzzerIO.cpp
|
||||
FuzzerIOPosix.cpp
|
||||
FuzzerIOWindows.cpp
|
||||
FuzzerLoop.cpp
|
||||
FuzzerMerge.cpp
|
||||
FuzzerMutate.cpp
|
||||
FuzzerSHA1.cpp
|
||||
FuzzerTracePC.cpp
|
||||
FuzzerTraceState.cpp
|
||||
FuzzerUtil.cpp
|
||||
FuzzerUtilDarwin.cpp
|
||||
FuzzerUtilLinux.cpp
|
||||
FuzzerUtilPosix.cpp
|
||||
FuzzerUtilWindows.cpp
|
||||
)
|
||||
add_library(LLVMFuzzerNoMain STATIC
|
||||
$<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
|
||||
)
|
||||
target_link_libraries(LLVMFuzzerNoMain ${PTHREAD_LIB})
|
||||
add_library(LLVMFuzzer STATIC
|
||||
FuzzerMain.cpp
|
||||
$<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
|
||||
)
|
||||
target_link_libraries(LLVMFuzzer ${PTHREAD_LIB})
|
||||
|
||||
if( LLVM_INCLUDE_TESTS )
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
endif()
|
217
test/thirdparty/Fuzzer/FuzzerCorpus.h
vendored
Normal file
217
test/thirdparty/Fuzzer/FuzzerCorpus.h
vendored
Normal file
|
@ -0,0 +1,217 @@
|
|||
//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::InputCorpus
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_CORPUS
|
||||
#define LLVM_FUZZER_CORPUS
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerSHA1.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct InputInfo {
|
||||
Unit U; // The actual input data.
|
||||
uint8_t Sha1[kSHA1NumBytes]; // Checksum.
|
||||
// Number of features that this input has and no smaller input has.
|
||||
size_t NumFeatures = 0;
|
||||
size_t Tmp = 0; // Used by ValidateFeatureSet.
|
||||
// Stats.
|
||||
size_t NumExecutedMutations = 0;
|
||||
size_t NumSuccessfullMutations = 0;
|
||||
bool MayDeleteFile = false;
|
||||
};
|
||||
|
||||
class InputCorpus {
|
||||
public:
|
||||
static const size_t kFeatureSetSize = 1 << 16;
|
||||
InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) {
|
||||
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
|
||||
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
|
||||
}
|
||||
~InputCorpus() {
|
||||
for (auto II : Inputs)
|
||||
delete II;
|
||||
}
|
||||
size_t size() const { return Inputs.size(); }
|
||||
size_t SizeInBytes() const {
|
||||
size_t Res = 0;
|
||||
for (auto II : Inputs)
|
||||
Res += II->U.size();
|
||||
return Res;
|
||||
}
|
||||
size_t NumActiveUnits() const {
|
||||
size_t Res = 0;
|
||||
for (auto II : Inputs)
|
||||
Res += !II->U.empty();
|
||||
return Res;
|
||||
}
|
||||
bool empty() const { return Inputs.empty(); }
|
||||
const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; }
|
||||
void AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile = false) {
|
||||
assert(!U.empty());
|
||||
uint8_t Hash[kSHA1NumBytes];
|
||||
if (FeatureDebug)
|
||||
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
|
||||
ComputeSHA1(U.data(), U.size(), Hash);
|
||||
Hashes.insert(Sha1ToString(Hash));
|
||||
Inputs.push_back(new InputInfo());
|
||||
InputInfo &II = *Inputs.back();
|
||||
II.U = U;
|
||||
II.NumFeatures = NumFeatures;
|
||||
II.MayDeleteFile = MayDeleteFile;
|
||||
memcpy(II.Sha1, Hash, kSHA1NumBytes);
|
||||
UpdateCorpusDistribution();
|
||||
ValidateFeatureSet();
|
||||
}
|
||||
|
||||
bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); }
|
||||
bool HasUnit(const std::string &H) { return Hashes.count(H); }
|
||||
InputInfo &ChooseUnitToMutate(Random &Rand) {
|
||||
InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)];
|
||||
assert(!II.U.empty());
|
||||
return II;
|
||||
};
|
||||
|
||||
// Returns an index of random unit from the corpus to mutate.
|
||||
// Hypothesis: units added to the corpus last are more likely to be
|
||||
// interesting. This function gives more weight to the more recent units.
|
||||
size_t ChooseUnitIdxToMutate(Random &Rand) {
|
||||
size_t Idx = static_cast<size_t>(CorpusDistribution(Rand.Get_mt19937()));
|
||||
assert(Idx < Inputs.size());
|
||||
return Idx;
|
||||
}
|
||||
|
||||
void PrintStats() {
|
||||
for (size_t i = 0; i < Inputs.size(); i++) {
|
||||
const auto &II = *Inputs[i];
|
||||
Printf(" [%zd %s]\tsz: %zd\truns: %zd\tsucc: %zd\n", i,
|
||||
Sha1ToString(II.Sha1).c_str(), II.U.size(),
|
||||
II.NumExecutedMutations, II.NumSuccessfullMutations);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintFeatureSet() {
|
||||
for (size_t i = 0; i < kFeatureSetSize; i++) {
|
||||
if(size_t Sz = GetFeature(i))
|
||||
Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz);
|
||||
}
|
||||
Printf("\n\t");
|
||||
for (size_t i = 0; i < Inputs.size(); i++)
|
||||
if (size_t N = Inputs[i]->NumFeatures)
|
||||
Printf(" %zd=>%zd ", i, N);
|
||||
Printf("\n");
|
||||
}
|
||||
|
||||
void DeleteInput(size_t Idx) {
|
||||
InputInfo &II = *Inputs[Idx];
|
||||
if (!OutputCorpus.empty() && II.MayDeleteFile)
|
||||
RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1)));
|
||||
Unit().swap(II.U);
|
||||
if (FeatureDebug)
|
||||
Printf("EVICTED %zd\n", Idx);
|
||||
}
|
||||
|
||||
bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) {
|
||||
assert(NewSize);
|
||||
Idx = Idx % kFeatureSetSize;
|
||||
uint32_t OldSize = GetFeature(Idx);
|
||||
if (OldSize == 0 || (Shrink && OldSize > NewSize)) {
|
||||
if (OldSize > 0) {
|
||||
size_t OldIdx = SmallestElementPerFeature[Idx];
|
||||
InputInfo &II = *Inputs[OldIdx];
|
||||
assert(II.NumFeatures > 0);
|
||||
II.NumFeatures--;
|
||||
if (II.NumFeatures == 0)
|
||||
DeleteInput(OldIdx);
|
||||
}
|
||||
if (FeatureDebug)
|
||||
Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize);
|
||||
SmallestElementPerFeature[Idx] = Inputs.size();
|
||||
InputSizesPerFeature[Idx] = NewSize;
|
||||
CountingFeatures = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t NumFeatures() const {
|
||||
size_t Res = 0;
|
||||
for (size_t i = 0; i < kFeatureSetSize; i++)
|
||||
Res += GetFeature(i) != 0;
|
||||
return Res;
|
||||
}
|
||||
|
||||
void ResetFeatureSet() {
|
||||
assert(Inputs.empty());
|
||||
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
|
||||
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static const bool FeatureDebug = false;
|
||||
|
||||
size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; }
|
||||
|
||||
void ValidateFeatureSet() {
|
||||
if (!CountingFeatures) return;
|
||||
if (FeatureDebug)
|
||||
PrintFeatureSet();
|
||||
for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++)
|
||||
if (GetFeature(Idx))
|
||||
Inputs[SmallestElementPerFeature[Idx]]->Tmp++;
|
||||
for (auto II: Inputs) {
|
||||
if (II->Tmp != II->NumFeatures)
|
||||
Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures);
|
||||
assert(II->Tmp == II->NumFeatures);
|
||||
II->Tmp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the probability distribution for the units in the corpus.
|
||||
// Must be called whenever the corpus or unit weights are changed.
|
||||
void UpdateCorpusDistribution() {
|
||||
size_t N = Inputs.size();
|
||||
Intervals.resize(N + 1);
|
||||
Weights.resize(N);
|
||||
std::iota(Intervals.begin(), Intervals.end(), 0);
|
||||
if (CountingFeatures)
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Weights[i] = Inputs[i]->NumFeatures * (i + 1);
|
||||
else
|
||||
std::iota(Weights.begin(), Weights.end(), 1);
|
||||
CorpusDistribution = std::piecewise_constant_distribution<double>(
|
||||
Intervals.begin(), Intervals.end(), Weights.begin());
|
||||
}
|
||||
std::piecewise_constant_distribution<double> CorpusDistribution;
|
||||
|
||||
std::vector<double> Intervals;
|
||||
std::vector<double> Weights;
|
||||
|
||||
std::unordered_set<std::string> Hashes;
|
||||
std::vector<InputInfo*> Inputs;
|
||||
|
||||
bool CountingFeatures = false;
|
||||
uint32_t InputSizesPerFeature[kFeatureSetSize];
|
||||
uint32_t SmallestElementPerFeature[kFeatureSetSize];
|
||||
|
||||
std::string OutputCorpus;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_CORPUS
|
52
test/thirdparty/Fuzzer/FuzzerCrossOver.cpp
vendored
Normal file
52
test/thirdparty/Fuzzer/FuzzerCrossOver.cpp
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Cross over test inputs.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out.
|
||||
size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1,
|
||||
const uint8_t *Data2, size_t Size2,
|
||||
uint8_t *Out, size_t MaxOutSize) {
|
||||
assert(Size1 || Size2);
|
||||
MaxOutSize = Rand(MaxOutSize) + 1;
|
||||
size_t OutPos = 0;
|
||||
size_t Pos1 = 0;
|
||||
size_t Pos2 = 0;
|
||||
size_t *InPos = &Pos1;
|
||||
size_t InSize = Size1;
|
||||
const uint8_t *Data = Data1;
|
||||
bool CurrentlyUsingFirstData = true;
|
||||
while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) {
|
||||
// Merge a part of Data into Out.
|
||||
size_t OutSizeLeft = MaxOutSize - OutPos;
|
||||
if (*InPos < InSize) {
|
||||
size_t InSizeLeft = InSize - *InPos;
|
||||
size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft);
|
||||
size_t ExtraSize = Rand(MaxExtraSize) + 1;
|
||||
memcpy(Out + OutPos, Data + *InPos, ExtraSize);
|
||||
OutPos += ExtraSize;
|
||||
(*InPos) += ExtraSize;
|
||||
}
|
||||
// Use the other input data on the next iteration.
|
||||
InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1;
|
||||
InSize = CurrentlyUsingFirstData ? Size2 : Size1;
|
||||
Data = CurrentlyUsingFirstData ? Data2 : Data1;
|
||||
CurrentlyUsingFirstData = !CurrentlyUsingFirstData;
|
||||
}
|
||||
return OutPos;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
89
test/thirdparty/Fuzzer/FuzzerDefs.h
vendored
Normal file
89
test/thirdparty/Fuzzer/FuzzerDefs.h
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Basic definitions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_DEFS_H
|
||||
#define LLVM_FUZZER_DEFS_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Platform detection.
|
||||
#ifdef __linux__
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_LINUX 1
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif __APPLE__
|
||||
#define LIBFUZZER_APPLE 1
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif _WIN32
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_WINDOWS 1
|
||||
#else
|
||||
#error "Support for your platform has not been implemented"
|
||||
#endif
|
||||
|
||||
#define LIBFUZZER_POSIX LIBFUZZER_APPLE || LIBFUZZER_LINUX
|
||||
|
||||
#ifdef __x86_64
|
||||
#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt")))
|
||||
#else
|
||||
#define ATTRIBUTE_TARGET_POPCNT
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __clang__ // avoid gcc warning.
|
||||
# define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
|
||||
#else
|
||||
# define ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
#endif
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
template <class T> T Min(T a, T b) { return a < b ? a : b; }
|
||||
template <class T> T Max(T a, T b) { return a > b ? a : b; }
|
||||
|
||||
class Random;
|
||||
class Dictionary;
|
||||
class DictionaryEntry;
|
||||
class MutationDispatcher;
|
||||
struct FuzzingOptions;
|
||||
class InputCorpus;
|
||||
struct InputInfo;
|
||||
struct ExternalFunctions;
|
||||
|
||||
// Global interface to functions that may or may not be available.
|
||||
extern ExternalFunctions *EF;
|
||||
|
||||
typedef std::vector<uint8_t> Unit;
|
||||
typedef std::vector<Unit> UnitVector;
|
||||
typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
|
||||
|
||||
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
|
||||
|
||||
struct ScopedDoingMyOwnMemmem {
|
||||
ScopedDoingMyOwnMemmem();
|
||||
~ScopedDoingMyOwnMemmem();
|
||||
};
|
||||
|
||||
inline uint8_t Bswap(uint8_t x) { return x; }
|
||||
inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
|
||||
inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
|
||||
inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_DEFS_H
|
124
test/thirdparty/Fuzzer/FuzzerDictionary.h
vendored
Normal file
124
test/thirdparty/Fuzzer/FuzzerDictionary.h
vendored
Normal file
|
@ -0,0 +1,124 @@
|
|||
//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::Dictionary
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_DICTIONARY_H
|
||||
#define LLVM_FUZZER_DICTIONARY_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerUtil.h"
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace fuzzer {
|
||||
// A simple POD sized array of bytes.
|
||||
template <size_t kMaxSize> class FixedWord {
|
||||
public:
|
||||
FixedWord() {}
|
||||
FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); }
|
||||
|
||||
void Set(const uint8_t *B, uint8_t S) {
|
||||
assert(S <= kMaxSize);
|
||||
memcpy(Data, B, S);
|
||||
Size = S;
|
||||
}
|
||||
|
||||
bool operator==(const FixedWord<kMaxSize> &w) const {
|
||||
return Size == w.Size && 0 == memcmp(Data, w.Data, Size);
|
||||
}
|
||||
|
||||
bool operator<(const FixedWord<kMaxSize> &w) const {
|
||||
if (Size != w.Size)
|
||||
return Size < w.Size;
|
||||
return memcmp(Data, w.Data, Size) < 0;
|
||||
}
|
||||
|
||||
static size_t GetMaxSize() { return kMaxSize; }
|
||||
const uint8_t *data() const { return Data; }
|
||||
uint8_t size() const { return Size; }
|
||||
|
||||
private:
|
||||
uint8_t Size = 0;
|
||||
uint8_t Data[kMaxSize];
|
||||
};
|
||||
|
||||
typedef FixedWord<27> Word; // 28 bytes.
|
||||
|
||||
class DictionaryEntry {
|
||||
public:
|
||||
DictionaryEntry() {}
|
||||
DictionaryEntry(Word W) : W(W) {}
|
||||
DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {}
|
||||
const Word &GetW() const { return W; }
|
||||
|
||||
bool HasPositionHint() const { return PositionHint != std::numeric_limits<size_t>::max(); }
|
||||
size_t GetPositionHint() const {
|
||||
assert(HasPositionHint());
|
||||
return PositionHint;
|
||||
}
|
||||
void IncUseCount() { UseCount++; }
|
||||
void IncSuccessCount() { SuccessCount++; }
|
||||
size_t GetUseCount() const { return UseCount; }
|
||||
size_t GetSuccessCount() const {return SuccessCount; }
|
||||
|
||||
void Print(const char *PrintAfter = "\n") {
|
||||
PrintASCII(W.data(), W.size());
|
||||
if (HasPositionHint())
|
||||
Printf("@%zd", GetPositionHint());
|
||||
Printf("%s", PrintAfter);
|
||||
}
|
||||
|
||||
private:
|
||||
Word W;
|
||||
size_t PositionHint = std::numeric_limits<size_t>::max();
|
||||
size_t UseCount = 0;
|
||||
size_t SuccessCount = 0;
|
||||
};
|
||||
|
||||
class Dictionary {
|
||||
public:
|
||||
static const size_t kMaxDictSize = 1 << 14;
|
||||
|
||||
bool ContainsWord(const Word &W) const {
|
||||
return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) {
|
||||
return DE.GetW() == W;
|
||||
});
|
||||
}
|
||||
const DictionaryEntry *begin() const { return &DE[0]; }
|
||||
const DictionaryEntry *end() const { return begin() + Size; }
|
||||
DictionaryEntry & operator[] (size_t Idx) {
|
||||
assert(Idx < Size);
|
||||
return DE[Idx];
|
||||
}
|
||||
void push_back(DictionaryEntry DE) {
|
||||
if (Size < kMaxDictSize)
|
||||
this->DE[Size++] = DE;
|
||||
}
|
||||
void clear() { Size = 0; }
|
||||
bool empty() const { return Size == 0; }
|
||||
size_t size() const { return Size; }
|
||||
|
||||
private:
|
||||
DictionaryEntry DE[kMaxDictSize];
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
// Parses one dictionary entry.
|
||||
// If successfull, write the enty to Unit and returns true,
|
||||
// otherwise returns false.
|
||||
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U);
|
||||
// Parses the dictionary file, fills Units, returns true iff all lines
|
||||
// were parsed succesfully.
|
||||
bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_DICTIONARY_H
|
545
test/thirdparty/Fuzzer/FuzzerDriver.cpp
vendored
Normal file
545
test/thirdparty/Fuzzer/FuzzerDriver.cpp
vendored
Normal file
|
@ -0,0 +1,545 @@
|
|||
//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// FuzzerDriver and flag parsing.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerInterface.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// This function should be present in the libFuzzer so that the client
|
||||
// binary can test for its existence.
|
||||
extern "C" __attribute__((used)) void __libfuzzer_is_present() {}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// Program arguments.
|
||||
struct FlagDescription {
|
||||
const char *Name;
|
||||
const char *Description;
|
||||
int Default;
|
||||
int *IntFlag;
|
||||
const char **StrFlag;
|
||||
unsigned int *UIntFlag;
|
||||
};
|
||||
|
||||
struct {
|
||||
#define FUZZER_DEPRECATED_FLAG(Name)
|
||||
#define FUZZER_FLAG_INT(Name, Default, Description) int Name;
|
||||
#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name;
|
||||
#define FUZZER_FLAG_STRING(Name, Description) const char *Name;
|
||||
#include "FuzzerFlags.def"
|
||||
#undef FUZZER_DEPRECATED_FLAG
|
||||
#undef FUZZER_FLAG_INT
|
||||
#undef FUZZER_FLAG_UNSIGNED
|
||||
#undef FUZZER_FLAG_STRING
|
||||
} Flags;
|
||||
|
||||
static const FlagDescription FlagDescriptions [] {
|
||||
#define FUZZER_DEPRECATED_FLAG(Name) \
|
||||
{#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr},
|
||||
#define FUZZER_FLAG_INT(Name, Default, Description) \
|
||||
{#Name, Description, Default, &Flags.Name, nullptr, nullptr},
|
||||
#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \
|
||||
{#Name, Description, static_cast<int>(Default), \
|
||||
nullptr, nullptr, &Flags.Name},
|
||||
#define FUZZER_FLAG_STRING(Name, Description) \
|
||||
{#Name, Description, 0, nullptr, &Flags.Name, nullptr},
|
||||
#include "FuzzerFlags.def"
|
||||
#undef FUZZER_DEPRECATED_FLAG
|
||||
#undef FUZZER_FLAG_INT
|
||||
#undef FUZZER_FLAG_UNSIGNED
|
||||
#undef FUZZER_FLAG_STRING
|
||||
};
|
||||
|
||||
static const size_t kNumFlags =
|
||||
sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]);
|
||||
|
||||
static std::vector<std::string> *Inputs;
|
||||
static std::string *ProgName;
|
||||
|
||||
static void PrintHelp() {
|
||||
Printf("Usage:\n");
|
||||
auto Prog = ProgName->c_str();
|
||||
Printf("\nTo run fuzzing pass 0 or more directories.\n");
|
||||
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog);
|
||||
|
||||
Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n");
|
||||
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog);
|
||||
|
||||
Printf("\nFlags: (strictly in form -flag=value)\n");
|
||||
size_t MaxFlagLen = 0;
|
||||
for (size_t F = 0; F < kNumFlags; F++)
|
||||
MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen);
|
||||
|
||||
for (size_t F = 0; F < kNumFlags; F++) {
|
||||
const auto &D = FlagDescriptions[F];
|
||||
if (strstr(D.Description, "internal flag") == D.Description) continue;
|
||||
Printf(" %s", D.Name);
|
||||
for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++)
|
||||
Printf(" ");
|
||||
Printf("\t");
|
||||
Printf("%d\t%s\n", D.Default, D.Description);
|
||||
}
|
||||
Printf("\nFlags starting with '--' will be ignored and "
|
||||
"will be passed verbatim to subprocesses.\n");
|
||||
}
|
||||
|
||||
static const char *FlagValue(const char *Param, const char *Name) {
|
||||
size_t Len = strlen(Name);
|
||||
if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 &&
|
||||
Param[Len + 1] == '=')
|
||||
return &Param[Len + 2];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Avoid calling stol as it triggers a bug in clang/glibc build.
|
||||
static long MyStol(const char *Str) {
|
||||
long Res = 0;
|
||||
long Sign = 1;
|
||||
if (*Str == '-') {
|
||||
Str++;
|
||||
Sign = -1;
|
||||
}
|
||||
for (size_t i = 0; Str[i]; i++) {
|
||||
char Ch = Str[i];
|
||||
if (Ch < '0' || Ch > '9')
|
||||
return Res;
|
||||
Res = Res * 10 + (Ch - '0');
|
||||
}
|
||||
return Res * Sign;
|
||||
}
|
||||
|
||||
static bool ParseOneFlag(const char *Param) {
|
||||
if (Param[0] != '-') return false;
|
||||
if (Param[1] == '-') {
|
||||
static bool PrintedWarning = false;
|
||||
if (!PrintedWarning) {
|
||||
PrintedWarning = true;
|
||||
Printf("INFO: libFuzzer ignores flags that start with '--'\n");
|
||||
}
|
||||
for (size_t F = 0; F < kNumFlags; F++)
|
||||
if (FlagValue(Param + 1, FlagDescriptions[F].Name))
|
||||
Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1);
|
||||
return true;
|
||||
}
|
||||
for (size_t F = 0; F < kNumFlags; F++) {
|
||||
const char *Name = FlagDescriptions[F].Name;
|
||||
const char *Str = FlagValue(Param, Name);
|
||||
if (Str) {
|
||||
if (FlagDescriptions[F].IntFlag) {
|
||||
int Val = MyStol(Str);
|
||||
*FlagDescriptions[F].IntFlag = Val;
|
||||
if (Flags.verbosity >= 2)
|
||||
Printf("Flag: %s %d\n", Name, Val);;
|
||||
return true;
|
||||
} else if (FlagDescriptions[F].UIntFlag) {
|
||||
unsigned int Val = std::stoul(Str);
|
||||
*FlagDescriptions[F].UIntFlag = Val;
|
||||
if (Flags.verbosity >= 2)
|
||||
Printf("Flag: %s %u\n", Name, Val);
|
||||
return true;
|
||||
} else if (FlagDescriptions[F].StrFlag) {
|
||||
*FlagDescriptions[F].StrFlag = Str;
|
||||
if (Flags.verbosity >= 2)
|
||||
Printf("Flag: %s %s\n", Name, Str);
|
||||
return true;
|
||||
} else { // Deprecated flag.
|
||||
Printf("Flag: %s: deprecated, don't use\n", Name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Printf("\n\nWARNING: unrecognized flag '%s'; "
|
||||
"use -help=1 to list all flags\n\n", Param);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't use any library to minimize dependencies.
|
||||
static void ParseFlags(const std::vector<std::string> &Args) {
|
||||
for (size_t F = 0; F < kNumFlags; F++) {
|
||||
if (FlagDescriptions[F].IntFlag)
|
||||
*FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default;
|
||||
if (FlagDescriptions[F].UIntFlag)
|
||||
*FlagDescriptions[F].UIntFlag =
|
||||
static_cast<unsigned int>(FlagDescriptions[F].Default);
|
||||
if (FlagDescriptions[F].StrFlag)
|
||||
*FlagDescriptions[F].StrFlag = nullptr;
|
||||
}
|
||||
Inputs = new std::vector<std::string>;
|
||||
for (size_t A = 1; A < Args.size(); A++) {
|
||||
if (ParseOneFlag(Args[A].c_str())) continue;
|
||||
Inputs->push_back(Args[A]);
|
||||
}
|
||||
}
|
||||
|
||||
static std::mutex Mu;
|
||||
|
||||
static void PulseThread() {
|
||||
while (true) {
|
||||
SleepSeconds(600);
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
Printf("pulse...\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void WorkerThread(const std::string &Cmd, std::atomic<unsigned> *Counter,
|
||||
unsigned NumJobs, std::atomic<bool> *HasErrors) {
|
||||
while (true) {
|
||||
unsigned C = (*Counter)++;
|
||||
if (C >= NumJobs) break;
|
||||
std::string Log = "fuzz-" + std::to_string(C) + ".log";
|
||||
std::string ToRun = Cmd + " > " + Log + " 2>&1\n";
|
||||
if (Flags.verbosity)
|
||||
Printf("%s", ToRun.c_str());
|
||||
int ExitCode = ExecuteCommand(ToRun);
|
||||
if (ExitCode != 0)
|
||||
*HasErrors = true;
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
Printf("================== Job %u exited with exit code %d ============\n",
|
||||
C, ExitCode);
|
||||
fuzzer::CopyFileToErr(Log);
|
||||
}
|
||||
}
|
||||
|
||||
std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
|
||||
const char *X1, const char *X2) {
|
||||
std::string Cmd;
|
||||
for (auto &S : Args) {
|
||||
if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2))
|
||||
continue;
|
||||
Cmd += S + " ";
|
||||
}
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
static int RunInMultipleProcesses(const std::vector<std::string> &Args,
|
||||
unsigned NumWorkers, unsigned NumJobs) {
|
||||
std::atomic<unsigned> Counter(0);
|
||||
std::atomic<bool> HasErrors(false);
|
||||
std::string Cmd = CloneArgsWithoutX(Args, "jobs", "workers");
|
||||
std::vector<std::thread> V;
|
||||
std::thread Pulse(PulseThread);
|
||||
Pulse.detach();
|
||||
for (unsigned i = 0; i < NumWorkers; i++)
|
||||
V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs, &HasErrors));
|
||||
for (auto &T : V)
|
||||
T.join();
|
||||
return HasErrors ? 1 : 0;
|
||||
}
|
||||
|
||||
static void RssThread(Fuzzer *F, size_t RssLimitMb) {
|
||||
while (true) {
|
||||
SleepSeconds(1);
|
||||
size_t Peak = GetPeakRSSMb();
|
||||
if (Peak > RssLimitMb)
|
||||
F->RssLimitCallback();
|
||||
}
|
||||
}
|
||||
|
||||
static void StartRssThread(Fuzzer *F, size_t RssLimitMb) {
|
||||
if (!RssLimitMb) return;
|
||||
std::thread T(RssThread, F, RssLimitMb);
|
||||
T.detach();
|
||||
}
|
||||
|
||||
int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) {
|
||||
Unit U = FileToVector(InputFilePath);
|
||||
if (MaxLen && MaxLen < U.size())
|
||||
U.resize(MaxLen);
|
||||
F->RunOne(U.data(), U.size());
|
||||
F->TryDetectingAMemoryLeak(U.data(), U.size(), true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool AllInputsAreFiles() {
|
||||
if (Inputs->empty()) return false;
|
||||
for (auto &Path : *Inputs)
|
||||
if (!IsFile(Path))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int MinimizeCrashInput(const std::vector<std::string> &Args) {
|
||||
if (Inputs->size() != 1) {
|
||||
Printf("ERROR: -minimize_crash should be given one input file\n");
|
||||
exit(1);
|
||||
}
|
||||
std::string InputFilePath = Inputs->at(0);
|
||||
std::string BaseCmd =
|
||||
CloneArgsWithoutX(Args, "minimize_crash", "exact_artifact_path");
|
||||
auto InputPos = BaseCmd.find(" " + InputFilePath + " ");
|
||||
assert(InputPos != std::string::npos);
|
||||
BaseCmd.erase(InputPos, InputFilePath.size() + 1);
|
||||
if (Flags.runs <= 0 && Flags.max_total_time == 0) {
|
||||
Printf("INFO: you need to specify -runs=N or "
|
||||
"-max_total_time=N with -minimize_crash=1\n"
|
||||
"INFO: defaulting to -max_total_time=600\n");
|
||||
BaseCmd += " -max_total_time=600";
|
||||
}
|
||||
// BaseCmd += " > /dev/null 2>&1 ";
|
||||
|
||||
std::string CurrentFilePath = InputFilePath;
|
||||
while (true) {
|
||||
Unit U = FileToVector(CurrentFilePath);
|
||||
if (U.size() < 2) {
|
||||
Printf("CRASH_MIN: '%s' is small enough\n", CurrentFilePath.c_str());
|
||||
return 0;
|
||||
}
|
||||
Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n",
|
||||
CurrentFilePath.c_str(), U.size());
|
||||
|
||||
auto Cmd = BaseCmd + " " + CurrentFilePath;
|
||||
|
||||
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
|
||||
int ExitCode = ExecuteCommand(Cmd);
|
||||
if (ExitCode == 0) {
|
||||
Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str());
|
||||
exit(1);
|
||||
}
|
||||
Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize "
|
||||
"it further\n",
|
||||
CurrentFilePath.c_str(), U.size());
|
||||
|
||||
std::string ArtifactPath = "minimized-from-" + Hash(U);
|
||||
Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" +
|
||||
ArtifactPath;
|
||||
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
|
||||
ExitCode = ExecuteCommand(Cmd);
|
||||
if (ExitCode == 0) {
|
||||
if (Flags.exact_artifact_path) {
|
||||
CurrentFilePath = Flags.exact_artifact_path;
|
||||
WriteToFile(U, CurrentFilePath);
|
||||
}
|
||||
Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n",
|
||||
CurrentFilePath.c_str(), U.size());
|
||||
return 0;
|
||||
}
|
||||
CurrentFilePath = ArtifactPath;
|
||||
Printf("\n\n\n\n\n\n*********************************\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
|
||||
assert(Inputs->size() == 1);
|
||||
std::string InputFilePath = Inputs->at(0);
|
||||
Unit U = FileToVector(InputFilePath);
|
||||
assert(U.size() > 2);
|
||||
Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size());
|
||||
Corpus->AddToCorpus(U, 0);
|
||||
F->SetMaxInputLen(U.size());
|
||||
F->SetMaxMutationLen(U.size() - 1);
|
||||
F->MinimizeCrashLoop(U);
|
||||
Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n");
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
|
||||
using namespace fuzzer;
|
||||
assert(argc && argv && "Argument pointers cannot be nullptr");
|
||||
EF = new ExternalFunctions();
|
||||
if (EF->LLVMFuzzerInitialize)
|
||||
EF->LLVMFuzzerInitialize(argc, argv);
|
||||
const std::vector<std::string> Args(*argv, *argv + *argc);
|
||||
assert(!Args.empty());
|
||||
ProgName = new std::string(Args[0]);
|
||||
ParseFlags(Args);
|
||||
if (Flags.help) {
|
||||
PrintHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Flags.minimize_crash)
|
||||
return MinimizeCrashInput(Args);
|
||||
|
||||
if (Flags.close_fd_mask & 2)
|
||||
DupAndCloseStderr();
|
||||
if (Flags.close_fd_mask & 1)
|
||||
CloseStdout();
|
||||
|
||||
if (Flags.jobs > 0 && Flags.workers == 0) {
|
||||
Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs);
|
||||
if (Flags.workers > 1)
|
||||
Printf("Running %u workers\n", Flags.workers);
|
||||
}
|
||||
|
||||
if (Flags.workers > 0 && Flags.jobs > 0)
|
||||
return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs);
|
||||
|
||||
const size_t kMaxSaneLen = 1 << 20;
|
||||
const size_t kMinDefaultLen = 64;
|
||||
FuzzingOptions Options;
|
||||
Options.Verbosity = Flags.verbosity;
|
||||
Options.MaxLen = Flags.max_len;
|
||||
Options.UnitTimeoutSec = Flags.timeout;
|
||||
Options.ErrorExitCode = Flags.error_exitcode;
|
||||
Options.TimeoutExitCode = Flags.timeout_exitcode;
|
||||
Options.MaxTotalTimeSec = Flags.max_total_time;
|
||||
Options.DoCrossOver = Flags.cross_over;
|
||||
Options.MutateDepth = Flags.mutate_depth;
|
||||
Options.UseCounters = Flags.use_counters;
|
||||
Options.UseIndirCalls = Flags.use_indir_calls;
|
||||
Options.UseMemcmp = Flags.use_memcmp;
|
||||
Options.UseMemmem = Flags.use_memmem;
|
||||
Options.UseCmp = Flags.use_cmp;
|
||||
Options.UseValueProfile = Flags.use_value_profile;
|
||||
Options.Shrink = Flags.shrink;
|
||||
Options.ShuffleAtStartUp = Flags.shuffle;
|
||||
Options.PreferSmall = Flags.prefer_small;
|
||||
Options.ReloadIntervalSec = Flags.reload;
|
||||
Options.OnlyASCII = Flags.only_ascii;
|
||||
Options.OutputCSV = Flags.output_csv;
|
||||
Options.DetectLeaks = Flags.detect_leaks;
|
||||
Options.TraceMalloc = Flags.trace_malloc;
|
||||
Options.RssLimitMb = Flags.rss_limit_mb;
|
||||
if (Flags.runs >= 0)
|
||||
Options.MaxNumberOfRuns = Flags.runs;
|
||||
if (!Inputs->empty() && !Flags.minimize_crash_internal_step)
|
||||
Options.OutputCorpus = (*Inputs)[0];
|
||||
Options.ReportSlowUnits = Flags.report_slow_units;
|
||||
if (Flags.artifact_prefix)
|
||||
Options.ArtifactPrefix = Flags.artifact_prefix;
|
||||
if (Flags.exact_artifact_path)
|
||||
Options.ExactArtifactPath = Flags.exact_artifact_path;
|
||||
std::vector<Unit> Dictionary;
|
||||
if (Flags.dict)
|
||||
if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary))
|
||||
return 1;
|
||||
if (Flags.verbosity > 0 && !Dictionary.empty())
|
||||
Printf("Dictionary: %zd entries\n", Dictionary.size());
|
||||
bool DoPlainRun = AllInputsAreFiles();
|
||||
Options.SaveArtifacts =
|
||||
!DoPlainRun || Flags.minimize_crash_internal_step;
|
||||
Options.PrintNewCovPcs = Flags.print_pcs;
|
||||
Options.PrintFinalStats = Flags.print_final_stats;
|
||||
Options.PrintCorpusStats = Flags.print_corpus_stats;
|
||||
Options.PrintCoverage = Flags.print_coverage;
|
||||
Options.DumpCoverage = Flags.dump_coverage;
|
||||
if (Flags.exit_on_src_pos)
|
||||
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
|
||||
if (Flags.exit_on_item)
|
||||
Options.ExitOnItem = Flags.exit_on_item;
|
||||
|
||||
unsigned Seed = Flags.seed;
|
||||
// Initialize Seed.
|
||||
if (Seed == 0)
|
||||
Seed = (std::chrono::system_clock::now().time_since_epoch().count() << 10) +
|
||||
GetPid();
|
||||
if (Flags.verbosity)
|
||||
Printf("INFO: Seed: %u\n", Seed);
|
||||
|
||||
Random Rand(Seed);
|
||||
auto *MD = new MutationDispatcher(Rand, Options);
|
||||
auto *Corpus = new InputCorpus(Options.OutputCorpus);
|
||||
auto *F = new Fuzzer(Callback, *Corpus, *MD, Options);
|
||||
|
||||
for (auto &U: Dictionary)
|
||||
if (U.size() <= Word::GetMaxSize())
|
||||
MD->AddWordToManualDictionary(Word(U.data(), U.size()));
|
||||
|
||||
StartRssThread(F, Flags.rss_limit_mb);
|
||||
|
||||
Options.HandleAbrt = Flags.handle_abrt;
|
||||
Options.HandleBus = Flags.handle_bus;
|
||||
Options.HandleFpe = Flags.handle_fpe;
|
||||
Options.HandleIll = Flags.handle_ill;
|
||||
Options.HandleInt = Flags.handle_int;
|
||||
Options.HandleSegv = Flags.handle_segv;
|
||||
Options.HandleTerm = Flags.handle_term;
|
||||
SetSignalHandler(Options);
|
||||
|
||||
if (Flags.minimize_crash_internal_step)
|
||||
return MinimizeCrashInputInternalStep(F, Corpus);
|
||||
|
||||
if (DoPlainRun) {
|
||||
Options.SaveArtifacts = false;
|
||||
int Runs = std::max(1, Flags.runs);
|
||||
Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(),
|
||||
Inputs->size(), Runs);
|
||||
for (auto &Path : *Inputs) {
|
||||
auto StartTime = system_clock::now();
|
||||
Printf("Running: %s\n", Path.c_str());
|
||||
for (int Iter = 0; Iter < Runs; Iter++)
|
||||
RunOneTest(F, Path.c_str(), Options.MaxLen);
|
||||
auto StopTime = system_clock::now();
|
||||
auto MS = duration_cast<milliseconds>(StopTime - StartTime).count();
|
||||
Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS);
|
||||
}
|
||||
Printf("***\n"
|
||||
"*** NOTE: fuzzing was not performed, you have only\n"
|
||||
"*** executed the target code on a fixed set of inputs.\n"
|
||||
"***\n");
|
||||
F->PrintFinalStats();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (Flags.merge) {
|
||||
if (Options.MaxLen == 0)
|
||||
F->SetMaxInputLen(kMaxSaneLen);
|
||||
if (TPC.UsingTracePcGuard()) {
|
||||
if (Flags.merge_control_file)
|
||||
F->CrashResistantMergeInternalStep(Flags.merge_control_file);
|
||||
else
|
||||
F->CrashResistantMerge(Args, *Inputs);
|
||||
} else {
|
||||
F->Merge(*Inputs);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
size_t TemporaryMaxLen = Options.MaxLen ? Options.MaxLen : kMaxSaneLen;
|
||||
|
||||
UnitVector InitialCorpus;
|
||||
for (auto &Inp : *Inputs) {
|
||||
Printf("Loading corpus dir: %s\n", Inp.c_str());
|
||||
ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr,
|
||||
TemporaryMaxLen, /*ExitOnError=*/false);
|
||||
}
|
||||
|
||||
if (Options.MaxLen == 0) {
|
||||
size_t MaxLen = 0;
|
||||
for (auto &U : InitialCorpus)
|
||||
MaxLen = std::max(U.size(), MaxLen);
|
||||
F->SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxLen), kMaxSaneLen));
|
||||
}
|
||||
|
||||
if (InitialCorpus.empty()) {
|
||||
InitialCorpus.push_back(Unit({'\n'})); // Valid ASCII input.
|
||||
if (Options.Verbosity)
|
||||
Printf("INFO: A corpus is not provided, starting from an empty corpus\n");
|
||||
}
|
||||
F->ShuffleAndMinimize(&InitialCorpus);
|
||||
InitialCorpus.clear(); // Don't need this memory any more.
|
||||
F->Loop();
|
||||
|
||||
if (Flags.verbosity)
|
||||
Printf("Done %d runs in %zd second(s)\n", F->getTotalNumberOfRuns(),
|
||||
F->secondsSinceProcessStartUp());
|
||||
F->PrintFinalStats();
|
||||
|
||||
exit(0); // Don't let F destroy itself.
|
||||
}
|
||||
|
||||
// Storage for global ExternalFunctions object.
|
||||
ExternalFunctions *EF = nullptr;
|
||||
|
||||
} // namespace fuzzer
|
50
test/thirdparty/Fuzzer/FuzzerExtFunctions.def
vendored
Normal file
50
test/thirdparty/Fuzzer/FuzzerExtFunctions.def
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This defines the external function pointers that
|
||||
// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The
|
||||
// EXT_FUNC macro must be defined at the point of inclusion. The signature of
|
||||
// the macro is:
|
||||
//
|
||||
// EXT_FUNC(<name>, <return_type>, <function_signature>, <warn_if_missing>)
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Optional user functions
|
||||
EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false);
|
||||
EXT_FUNC(LLVMFuzzerCustomMutator, size_t,
|
||||
(uint8_t * Data, size_t Size, size_t MaxSize, unsigned int Seed),
|
||||
false);
|
||||
EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
|
||||
(const uint8_t * Data1, size_t Size1,
|
||||
const uint8_t * Data2, size_t Size2,
|
||||
uint8_t * Out, size_t MaxOutSize, unsigned int Seed),
|
||||
false);
|
||||
|
||||
// Sanitizer functions
|
||||
EXT_FUNC(__lsan_enable, void, (), false);
|
||||
EXT_FUNC(__lsan_disable, void, (), false);
|
||||
EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false);
|
||||
EXT_FUNC(__sanitizer_get_number_of_counters, size_t, (), false);
|
||||
EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int,
|
||||
(void (*malloc_hook)(const volatile void *, size_t),
|
||||
void (*free_hook)(const volatile void *)),
|
||||
false);
|
||||
EXT_FUNC(__sanitizer_get_total_unique_caller_callee_pairs, size_t, (), false);
|
||||
EXT_FUNC(__sanitizer_get_total_unique_coverage, size_t, (), true);
|
||||
EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t), false);
|
||||
EXT_FUNC(__sanitizer_print_stack_trace, void, (), true);
|
||||
EXT_FUNC(__sanitizer_symbolize_pc, void,
|
||||
(void *, const char *fmt, char *out_buf, size_t out_buf_size), false);
|
||||
EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int,
|
||||
(void *pc, char *module_path,
|
||||
size_t module_path_len,void **pc_offset), false);
|
||||
EXT_FUNC(__sanitizer_reset_coverage, void, (), true);
|
||||
EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true);
|
||||
EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false);
|
||||
EXT_FUNC(__sanitizer_update_counter_bitset_and_clear_counters, uintptr_t,
|
||||
(uint8_t*), false);
|
35
test/thirdparty/Fuzzer/FuzzerExtFunctions.h
vendored
Normal file
35
test/thirdparty/Fuzzer/FuzzerExtFunctions.h
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Defines an interface to (possibly optional) functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H
|
||||
#define LLVM_FUZZER_EXT_FUNCTIONS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct ExternalFunctions {
|
||||
// Initialize function pointers. Functions that are not available will be set
|
||||
// to nullptr. Do not call this constructor before ``main()`` has been
|
||||
// entered.
|
||||
ExternalFunctions();
|
||||
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
RETURN_TYPE(*NAME) FUNC_SIG = nullptr
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
};
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif
|
52
test/thirdparty/Fuzzer/FuzzerExtFunctionsDlsym.cpp
vendored
Normal file
52
test/thirdparty/Fuzzer/FuzzerExtFunctionsDlsym.cpp
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation for operating systems that support dlsym(). We only use it on
|
||||
// Apple platforms for now. We don't use this approach on Linux because it
|
||||
// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker.
|
||||
// That is a complication we don't wish to expose to clients right now.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_APPLE
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
template <typename T>
|
||||
static T GetFnPtr(const char *FnName, bool WarnIfMissing) {
|
||||
dlerror(); // Clear any previous errors.
|
||||
void *Fn = dlsym(RTLD_DEFAULT, FnName);
|
||||
if (Fn == nullptr) {
|
||||
if (WarnIfMissing) {
|
||||
const char *ErrorMsg = dlerror();
|
||||
Printf("WARNING: Failed to find function \"%s\".", FnName);
|
||||
if (ErrorMsg)
|
||||
Printf(" Reason %s.", ErrorMsg);
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
return reinterpret_cast<T>(Fn);
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = GetFnPtr<decltype(ExternalFunctions::NAME)>(#NAME, WARN)
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_APPLE
|
53
test/thirdparty/Fuzzer/FuzzerExtFunctionsWeak.cpp
vendored
Normal file
53
test/thirdparty/Fuzzer/FuzzerExtFunctionsWeak.cpp
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation for Linux. This relies on the linker's support for weak
|
||||
// symbols. We don't use this approach on Apple platforms because it requires
|
||||
// clients of LibFuzzer to pass ``-U _<symbol_name>`` to the linker to allow
|
||||
// weak symbols to be undefined. That is a complication we don't want to expose
|
||||
// to clients right now.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_LINUX
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
|
||||
extern "C" {
|
||||
// Declare these symbols as weak to allow them to be optionally defined.
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
__attribute__((weak)) RETURN_TYPE NAME FUNC_SIG
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) {
|
||||
if (FnPtr == nullptr && WarnIfMissing) {
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
|
||||
}
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = ::NAME; \
|
||||
CheckFnPtr((void *)::NAME, #NAME, WARN);
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_LINUX
|
56
test/thirdparty/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp
vendored
Normal file
56
test/thirdparty/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
//===- FuzzerExtFunctionsWeakAlias.cpp - Interface to external functions --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation using weak aliases. Works for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
extern "C" {
|
||||
// Declare these symbols as weak to allow them to be optionally defined.
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
RETURN_TYPE NAME##Def FUNC_SIG { \
|
||||
Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
|
||||
exit(1); \
|
||||
} \
|
||||
RETURN_TYPE NAME FUNC_SIG __attribute__((weak, alias(#NAME "Def")));
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
|
||||
if (Fun == FunDef) {
|
||||
if (WarnIfMissing)
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
|
||||
return nullptr;
|
||||
}
|
||||
return Fun;
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
115
test/thirdparty/Fuzzer/FuzzerFlags.def
vendored
Normal file
115
test/thirdparty/Fuzzer/FuzzerFlags.def
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the
|
||||
// point of inclusion. We are not using any flag parsing library for better
|
||||
// portability and independence.
|
||||
//===----------------------------------------------------------------------===//
|
||||
FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.")
|
||||
FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.")
|
||||
FUZZER_FLAG_INT(runs, -1,
|
||||
"Number of individual test runs (-1 for infinite runs).")
|
||||
FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. "
|
||||
"If 0, libFuzzer tries to guess a good value based on the corpus "
|
||||
"and reports it. ")
|
||||
FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.")
|
||||
FUZZER_FLAG_INT(mutate_depth, 5,
|
||||
"Apply this number of consecutive mutations to each input.")
|
||||
FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup")
|
||||
FUZZER_FLAG_INT(prefer_small, 1,
|
||||
"If 1, always prefer smaller inputs during the corpus shuffle.")
|
||||
FUZZER_FLAG_INT(
|
||||
timeout, 1200,
|
||||
"Timeout in seconds (if positive). "
|
||||
"If one unit runs more than this number of seconds the process will abort.")
|
||||
FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug "
|
||||
"this exit code will be used.")
|
||||
FUZZER_FLAG_INT(timeout_exitcode, 77, "When libFuzzer reports a timeout "
|
||||
"this exit code will be used.")
|
||||
FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total "
|
||||
"time in seconds to run the fuzzer.")
|
||||
FUZZER_FLAG_INT(help, 0, "Print help.")
|
||||
FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be "
|
||||
"merged into the 1-st corpus. Only interesting units will be taken. "
|
||||
"This flag can be used to minimize a corpus.")
|
||||
FUZZER_FLAG_STRING(merge_control_file, "internal flag")
|
||||
FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided"
|
||||
" crash input. Use with -runs=N or -max_total_time=N to limit "
|
||||
"the number attempts")
|
||||
FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag")
|
||||
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
|
||||
FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters")
|
||||
FUZZER_FLAG_INT(use_memcmp, 1,
|
||||
"Use hints from intercepting memcmp, strcmp, etc")
|
||||
FUZZER_FLAG_INT(use_memmem, 1,
|
||||
"Use hints from intercepting memmem, strstr, etc")
|
||||
FUZZER_FLAG_INT(use_value_profile, 0,
|
||||
"Experimental. Use value profile to guide fuzzing.")
|
||||
FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations")
|
||||
FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus elements.")
|
||||
FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn"
|
||||
" this number of jobs in separate worker processes"
|
||||
" with stdout/stderr redirected to fuzz-JOB.log.")
|
||||
FUZZER_FLAG_UNSIGNED(workers, 0,
|
||||
"Number of simultaneous worker processes to run the jobs."
|
||||
" If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.")
|
||||
FUZZER_FLAG_INT(reload, 1,
|
||||
"Reload the main corpus every <N> seconds to get new units"
|
||||
" discovered by other processes. If 0, disabled")
|
||||
FUZZER_FLAG_INT(report_slow_units, 10,
|
||||
"Report slowest units if they run for more than this number of seconds.")
|
||||
FUZZER_FLAG_INT(only_ascii, 0,
|
||||
"If 1, generate only ASCII (isprint+isspace) inputs.")
|
||||
FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.")
|
||||
FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, "
|
||||
"timeout, or slow inputs) as "
|
||||
"$(artifact_prefix)file")
|
||||
FUZZER_FLAG_STRING(exact_artifact_path,
|
||||
"Write the single artifact on failure (crash, timeout) "
|
||||
"as $(exact_artifact_path). This overrides -artifact_prefix "
|
||||
"and will not use checksum in the file name. Do not "
|
||||
"use the same path for several parallel processes.")
|
||||
FUZZER_FLAG_INT(output_csv, 0, "Enable pulse output in CSV format.")
|
||||
FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.")
|
||||
FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.")
|
||||
FUZZER_FLAG_INT(print_corpus_stats, 0,
|
||||
"If 1, print statistics on corpus elements at exit.")
|
||||
FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information at exit."
|
||||
" Experimental, only with trace-pc-guard")
|
||||
FUZZER_FLAG_INT(dump_coverage, 0, "If 1, dump coverage information at exit."
|
||||
" Experimental, only with trace-pc-guard")
|
||||
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
|
||||
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGSEGV.")
|
||||
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
|
||||
FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.")
|
||||
FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.")
|
||||
FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.")
|
||||
FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.")
|
||||
FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; "
|
||||
"if 2, close stderr; if 3, close both. "
|
||||
"Be careful, this will also close e.g. asan's stderr/stdout.")
|
||||
FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled "
|
||||
"try to detect memory leaks during fuzzing (i.e. not only at shut down).")
|
||||
FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. "
|
||||
"If >= 2 will also print stack traces.")
|
||||
FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon"
|
||||
"reaching this limit of RSS memory usage.")
|
||||
FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates"
|
||||
" from the given source location. Example: -exit_on_src_pos=foo.cc:123. "
|
||||
"Used primarily for testing libFuzzer itself.")
|
||||
FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum"
|
||||
" was added to the corpus. "
|
||||
"Used primarily for testing libFuzzer itself.")
|
||||
|
||||
FUZZER_DEPRECATED_FLAG(exit_on_first)
|
||||
FUZZER_DEPRECATED_FLAG(save_minimized_corpus)
|
||||
FUZZER_DEPRECATED_FLAG(sync_command)
|
||||
FUZZER_DEPRECATED_FLAG(sync_timeout)
|
||||
FUZZER_DEPRECATED_FLAG(test_single_input)
|
||||
FUZZER_DEPRECATED_FLAG(drill)
|
||||
FUZZER_DEPRECATED_FLAG(truncate_units)
|
117
test/thirdparty/Fuzzer/FuzzerIO.cpp
vendored
Normal file
117
test/thirdparty/Fuzzer/FuzzerIO.cpp
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
//===- FuzzerIO.cpp - IO utils. -------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static FILE *OutputFile = stderr;
|
||||
|
||||
long GetEpoch(const std::string &Path) {
|
||||
struct stat St;
|
||||
if (stat(Path.c_str(), &St))
|
||||
return 0; // Can't stat, be conservative.
|
||||
return St.st_mtime;
|
||||
}
|
||||
|
||||
Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
|
||||
std::ifstream T(Path);
|
||||
if (ExitOnError && !T) {
|
||||
Printf("No such directory: %s; exiting\n", Path.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
T.seekg(0, T.end);
|
||||
size_t FileLen = T.tellg();
|
||||
if (MaxSize)
|
||||
FileLen = std::min(FileLen, MaxSize);
|
||||
|
||||
T.seekg(0, T.beg);
|
||||
Unit Res(FileLen);
|
||||
T.read(reinterpret_cast<char *>(Res.data()), FileLen);
|
||||
return Res;
|
||||
}
|
||||
|
||||
std::string FileToString(const std::string &Path) {
|
||||
std::ifstream T(Path);
|
||||
return std::string((std::istreambuf_iterator<char>(T)),
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
void CopyFileToErr(const std::string &Path) {
|
||||
Printf("%s", FileToString(Path).c_str());
|
||||
}
|
||||
|
||||
void WriteToFile(const Unit &U, const std::string &Path) {
|
||||
// Use raw C interface because this function may be called from a sig handler.
|
||||
FILE *Out = fopen(Path.c_str(), "w");
|
||||
if (!Out) return;
|
||||
fwrite(U.data(), sizeof(U[0]), U.size(), Out);
|
||||
fclose(Out);
|
||||
}
|
||||
|
||||
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
|
||||
long *Epoch, size_t MaxSize, bool ExitOnError) {
|
||||
long E = Epoch ? *Epoch : 0;
|
||||
std::vector<std::string> Files;
|
||||
ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true);
|
||||
size_t NumLoaded = 0;
|
||||
for (size_t i = 0; i < Files.size(); i++) {
|
||||
auto &X = Files[i];
|
||||
if (Epoch && GetEpoch(X) < E) continue;
|
||||
NumLoaded++;
|
||||
if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024)
|
||||
Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path);
|
||||
auto S = FileToVector(X, MaxSize, ExitOnError);
|
||||
if (!S.empty())
|
||||
V->push_back(S);
|
||||
}
|
||||
}
|
||||
|
||||
std::string DirPlusFile(const std::string &DirPath,
|
||||
const std::string &FileName) {
|
||||
return DirPath + GetSeparator() + FileName;
|
||||
}
|
||||
|
||||
void DupAndCloseStderr() {
|
||||
int OutputFd = DuplicateFile(2);
|
||||
if (OutputFd > 0) {
|
||||
FILE *NewOutputFile = OpenFile(OutputFd, "w");
|
||||
if (NewOutputFile) {
|
||||
OutputFile = NewOutputFile;
|
||||
if (EF->__sanitizer_set_report_fd)
|
||||
EF->__sanitizer_set_report_fd(reinterpret_cast<void *>(OutputFd));
|
||||
CloseFile(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CloseStdout() {
|
||||
CloseFile(1);
|
||||
}
|
||||
|
||||
void Printf(const char *Fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, Fmt);
|
||||
vfprintf(OutputFile, Fmt, ap);
|
||||
va_end(ap);
|
||||
fflush(OutputFile);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
64
test/thirdparty/Fuzzer/FuzzerIO.h
vendored
Normal file
64
test/thirdparty/Fuzzer/FuzzerIO.h
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO interface.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_IO_H
|
||||
#define LLVM_FUZZER_IO_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
long GetEpoch(const std::string &Path);
|
||||
|
||||
Unit FileToVector(const std::string &Path, size_t MaxSize = 0,
|
||||
bool ExitOnError = true);
|
||||
|
||||
std::string FileToString(const std::string &Path);
|
||||
|
||||
void CopyFileToErr(const std::string &Path);
|
||||
|
||||
void WriteToFile(const Unit &U, const std::string &Path);
|
||||
|
||||
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
|
||||
long *Epoch, size_t MaxSize, bool ExitOnError);
|
||||
|
||||
// Returns "Dir/FileName" or equivalent for the current OS.
|
||||
std::string DirPlusFile(const std::string &DirPath,
|
||||
const std::string &FileName);
|
||||
|
||||
// Returns the name of the dir, similar to the 'dirname' utility.
|
||||
std::string DirName(const std::string &FileName);
|
||||
|
||||
void DupAndCloseStderr();
|
||||
|
||||
void CloseStdout();
|
||||
|
||||
void Printf(const char *Fmt, ...);
|
||||
|
||||
// Platform specific functions:
|
||||
bool IsFile(const std::string &Path);
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
std::vector<std::string> *V, bool TopDir);
|
||||
|
||||
char GetSeparator();
|
||||
|
||||
FILE* OpenFile(int Fd, const char *Mode);
|
||||
|
||||
int CloseFile(int Fd);
|
||||
|
||||
int DuplicateFile(int Fd);
|
||||
|
||||
void RemoveFile(const std::string &Path);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_IO_H
|
88
test/thirdparty/Fuzzer/FuzzerIOPosix.cpp
vendored
Normal file
88
test/thirdparty/Fuzzer/FuzzerIOPosix.cpp
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO functions implementation using Posix API.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_POSIX
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <dirent.h>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
bool IsFile(const std::string &Path) {
|
||||
struct stat St;
|
||||
if (stat(Path.c_str(), &St))
|
||||
return false;
|
||||
return S_ISREG(St.st_mode);
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
std::vector<std::string> *V, bool TopDir) {
|
||||
auto E = GetEpoch(Dir);
|
||||
if (Epoch)
|
||||
if (E && *Epoch >= E) return;
|
||||
|
||||
DIR *D = opendir(Dir.c_str());
|
||||
if (!D) {
|
||||
Printf("No such directory: %s; exiting\n", Dir.c_str());
|
||||
exit(1);
|
||||
}
|
||||
while (auto E = readdir(D)) {
|
||||
std::string Path = DirPlusFile(Dir, E->d_name);
|
||||
if (E->d_type == DT_REG || E->d_type == DT_LNK)
|
||||
V->push_back(Path);
|
||||
else if (E->d_type == DT_DIR && *E->d_name != '.')
|
||||
ListFilesInDirRecursive(Path, Epoch, V, false);
|
||||
}
|
||||
closedir(D);
|
||||
if (Epoch && TopDir)
|
||||
*Epoch = E;
|
||||
}
|
||||
|
||||
char GetSeparator() {
|
||||
return '/';
|
||||
}
|
||||
|
||||
FILE* OpenFile(int Fd, const char* Mode) {
|
||||
return fdopen(Fd, Mode);
|
||||
}
|
||||
|
||||
int CloseFile(int fd) {
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
int DuplicateFile(int Fd) {
|
||||
return dup(Fd);
|
||||
}
|
||||
|
||||
void RemoveFile(const std::string &Path) {
|
||||
unlink(Path.c_str());
|
||||
}
|
||||
|
||||
std::string DirName(const std::string &FileName) {
|
||||
char *Tmp = new char[FileName.size() + 1];
|
||||
memcpy(Tmp, FileName.c_str(), FileName.size() + 1);
|
||||
std::string Res = dirname(Tmp);
|
||||
delete [] Tmp;
|
||||
return Res;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_POSIX
|
282
test/thirdparty/Fuzzer/FuzzerIOWindows.cpp
vendored
Normal file
282
test/thirdparty/Fuzzer/FuzzerIOWindows.cpp
vendored
Normal file
|
@ -0,0 +1,282 @@
|
|||
//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO functions implementation for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <io.h>
|
||||
#include <iterator>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static bool IsFile(const std::string &Path, const DWORD &FileAttributes) {
|
||||
|
||||
if (FileAttributes & FILE_ATTRIBUTE_NORMAL)
|
||||
return true;
|
||||
|
||||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return false;
|
||||
|
||||
HANDLE FileHandle(
|
||||
CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, 0));
|
||||
|
||||
if (FileHandle == INVALID_HANDLE_VALUE) {
|
||||
Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
|
||||
GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD FileType = GetFileType(FileHandle);
|
||||
|
||||
if (FileType == FILE_TYPE_UNKNOWN) {
|
||||
Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
|
||||
GetLastError());
|
||||
CloseHandle(FileHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FileType != FILE_TYPE_DISK) {
|
||||
CloseHandle(FileHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseHandle(FileHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsFile(const std::string &Path) {
|
||||
DWORD Att = GetFileAttributesA(Path.c_str());
|
||||
|
||||
if (Att == INVALID_FILE_ATTRIBUTES) {
|
||||
Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
|
||||
Path.c_str(), GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsFile(Path, Att);
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
std::vector<std::string> *V, bool TopDir) {
|
||||
auto E = GetEpoch(Dir);
|
||||
if (Epoch)
|
||||
if (E && *Epoch >= E) return;
|
||||
|
||||
std::string Path(Dir);
|
||||
assert(!Path.empty());
|
||||
if (Path.back() != '\\')
|
||||
Path.push_back('\\');
|
||||
Path.push_back('*');
|
||||
|
||||
// Get the first directory entry.
|
||||
WIN32_FIND_DATAA FindInfo;
|
||||
HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
|
||||
if (FindHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Printf("No file found in: %s.\n", Dir.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
|
||||
|
||||
if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
size_t FilenameLen = strlen(FindInfo.cFileName);
|
||||
if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') ||
|
||||
(FilenameLen == 2 && FindInfo.cFileName[0] == '.' &&
|
||||
FindInfo.cFileName[1] == '.'))
|
||||
continue;
|
||||
|
||||
ListFilesInDirRecursive(FileName, Epoch, V, false);
|
||||
}
|
||||
else if (IsFile(FileName, FindInfo.dwFileAttributes))
|
||||
V->push_back(FileName);
|
||||
} while (FindNextFileA(FindHandle, &FindInfo));
|
||||
|
||||
DWORD LastError = GetLastError();
|
||||
if (LastError != ERROR_NO_MORE_FILES)
|
||||
Printf("FindNextFileA failed (Error code: %lu).\n", LastError);
|
||||
|
||||
FindClose(FindHandle);
|
||||
|
||||
if (Epoch && TopDir)
|
||||
*Epoch = E;
|
||||
}
|
||||
|
||||
char GetSeparator() {
|
||||
return '\\';
|
||||
}
|
||||
|
||||
FILE* OpenFile(int Fd, const char* Mode) {
|
||||
return _fdopen(Fd, Mode);
|
||||
}
|
||||
|
||||
int CloseFile(int Fd) {
|
||||
return _close(Fd);
|
||||
}
|
||||
|
||||
int DuplicateFile(int Fd) {
|
||||
return _dup(Fd);
|
||||
}
|
||||
|
||||
void RemoveFile(const std::string &Path) {
|
||||
_unlink(Path.c_str());
|
||||
}
|
||||
|
||||
static bool IsSeparator(char C) {
|
||||
return C == '\\' || C == '/';
|
||||
}
|
||||
|
||||
// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseDrive(const std::string &FileName, const size_t Offset,
|
||||
bool Relative = true) {
|
||||
if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':')
|
||||
return 0;
|
||||
if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
|
||||
if (!Relative) // Accept relative path?
|
||||
return 0;
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Parse a file name, like: SomeFile.txt
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
|
||||
size_t Pos = Offset;
|
||||
const size_t End = FileName.size();
|
||||
for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
|
||||
;
|
||||
return Pos - Offset;
|
||||
}
|
||||
|
||||
// Parse a directory ending in separator, like: SomeDir\
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseDir(const std::string &FileName, const size_t Offset) {
|
||||
size_t Pos = Offset;
|
||||
const size_t End = FileName.size();
|
||||
if (Pos >= End || IsSeparator(FileName[Pos]))
|
||||
return 0;
|
||||
for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
|
||||
;
|
||||
if (Pos >= End)
|
||||
return 0;
|
||||
++Pos; // Include separator.
|
||||
return Pos - Offset;
|
||||
}
|
||||
|
||||
// Parse a servername and share, like: SomeServer\SomeShare\
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseServerAndShare(const std::string &FileName,
|
||||
const size_t Offset) {
|
||||
size_t Pos = Offset, Res;
|
||||
if (!(Res = ParseDir(FileName, Pos)))
|
||||
return 0;
|
||||
Pos += Res;
|
||||
if (!(Res = ParseDir(FileName, Pos)))
|
||||
return 0;
|
||||
Pos += Res;
|
||||
return Pos - Offset;
|
||||
}
|
||||
|
||||
// Parse the given Ref string from the position Offset, to exactly match the given
|
||||
// string Patt.
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseCustomString(const std::string &Ref, size_t Offset,
|
||||
const char *Patt) {
|
||||
size_t Len = strlen(Patt);
|
||||
if (Offset + Len > Ref.size())
|
||||
return 0;
|
||||
return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
|
||||
}
|
||||
|
||||
// Parse a location, like:
|
||||
// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C:
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseLocation(const std::string &FileName) {
|
||||
size_t Pos = 0, Res;
|
||||
|
||||
if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) {
|
||||
Pos += Res;
|
||||
if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) {
|
||||
Pos += Res;
|
||||
if ((Res = ParseServerAndShare(FileName, Pos)))
|
||||
return Pos + Res;
|
||||
return 0;
|
||||
}
|
||||
if ((Res = ParseDrive(FileName, Pos, false)))
|
||||
return Pos + Res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
|
||||
++Pos;
|
||||
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
|
||||
++Pos;
|
||||
if ((Res = ParseServerAndShare(FileName, Pos)))
|
||||
return Pos + Res;
|
||||
return 0;
|
||||
}
|
||||
return Pos;
|
||||
}
|
||||
|
||||
if ((Res = ParseDrive(FileName, Pos)))
|
||||
return Pos + Res;
|
||||
|
||||
return Pos;
|
||||
}
|
||||
|
||||
std::string DirName(const std::string &FileName) {
|
||||
size_t LocationLen = ParseLocation(FileName);
|
||||
size_t DirLen = 0, Res;
|
||||
while ((Res = ParseDir(FileName, LocationLen + DirLen)))
|
||||
DirLen += Res;
|
||||
size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
|
||||
|
||||
if (LocationLen + DirLen + FileLen != FileName.size()) {
|
||||
Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (DirLen) {
|
||||
--DirLen; // Remove trailing separator.
|
||||
if (!FileLen) { // Path ended in separator.
|
||||
assert(DirLen);
|
||||
// Remove file name from Dir.
|
||||
while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
|
||||
--DirLen;
|
||||
if (DirLen) // Remove trailing separator.
|
||||
--DirLen;
|
||||
}
|
||||
}
|
||||
|
||||
if (!LocationLen) { // Relative path.
|
||||
if (!DirLen)
|
||||
return ".";
|
||||
return std::string(".\\").append(FileName, 0, DirLen);
|
||||
}
|
||||
|
||||
return FileName.substr(0, LocationLen + DirLen);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
67
test/thirdparty/Fuzzer/FuzzerInterface.h
vendored
Normal file
67
test/thirdparty/Fuzzer/FuzzerInterface.h
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Define the interface between libFuzzer and the library being tested.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// NOTE: the libFuzzer interface is thin and in the majority of cases
|
||||
// you should not include this file into your target. In 95% of cases
|
||||
// all you need is to define the following function in your file:
|
||||
// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
|
||||
// WARNING: keep the interface in C.
|
||||
|
||||
#ifndef LLVM_FUZZER_INTERFACE_H
|
||||
#define LLVM_FUZZER_INTERFACE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// Mandatory user-provided target function.
|
||||
// Executes the code under test with [Data, Data+Size) as the input.
|
||||
// libFuzzer will invoke this function *many* times with different inputs.
|
||||
// Must return 0.
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
|
||||
// Optional user-provided initialization function.
|
||||
// If provided, this function will be called by libFuzzer once at startup.
|
||||
// It may read and modify argc/argv.
|
||||
// Must return 0.
|
||||
int LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||
|
||||
// Optional user-provided custom mutator.
|
||||
// Mutates raw data in [Data, Data+Size) inplace.
|
||||
// Returns the new size, which is not greater than MaxSize.
|
||||
// Given the same Seed produces the same mutation.
|
||||
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
unsigned int Seed);
|
||||
|
||||
// Optional user-provided custom cross-over function.
|
||||
// Combines pieces of Data1 & Data2 together into Out.
|
||||
// Returns the new size, which is not greater than MaxOutSize.
|
||||
// Should produce the same mutation given the same Seed.
|
||||
size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
|
||||
const uint8_t *Data2, size_t Size2,
|
||||
uint8_t *Out, size_t MaxOutSize,
|
||||
unsigned int Seed);
|
||||
|
||||
// Experimental, may go away in future.
|
||||
// libFuzzer-provided function to be used inside LLVMFuzzerTestOneInput.
|
||||
// Mutates raw data in [Data, Data+Size) inplace.
|
||||
// Returns the new size, which is not greater than MaxSize.
|
||||
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // LLVM_FUZZER_INTERFACE_H
|
182
test/thirdparty/Fuzzer/FuzzerInternal.h
vendored
Normal file
182
test/thirdparty/Fuzzer/FuzzerInternal.h
vendored
Normal file
|
@ -0,0 +1,182 @@
|
|||
//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Define the main class fuzzer::Fuzzer and most functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_INTERNAL_H
|
||||
#define LLVM_FUZZER_INTERNAL_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerInterface.h"
|
||||
#include "FuzzerOptions.h"
|
||||
#include "FuzzerSHA1.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <string.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
class Fuzzer {
|
||||
public:
|
||||
|
||||
// Aggregates all available coverage measurements.
|
||||
struct Coverage {
|
||||
Coverage() { Reset(); }
|
||||
|
||||
void Reset() {
|
||||
BlockCoverage = 0;
|
||||
CallerCalleeCoverage = 0;
|
||||
CounterBitmapBits = 0;
|
||||
CounterBitmap.clear();
|
||||
VPMap.Reset();
|
||||
}
|
||||
|
||||
size_t BlockCoverage;
|
||||
size_t CallerCalleeCoverage;
|
||||
// Precalculated number of bits in CounterBitmap.
|
||||
size_t CounterBitmapBits;
|
||||
std::vector<uint8_t> CounterBitmap;
|
||||
ValueBitMap VPMap;
|
||||
};
|
||||
|
||||
Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
|
||||
FuzzingOptions Options);
|
||||
~Fuzzer();
|
||||
void Loop();
|
||||
void MinimizeCrashLoop(const Unit &U);
|
||||
void ShuffleAndMinimize(UnitVector *V);
|
||||
void InitializeTraceState();
|
||||
void RereadOutputCorpus(size_t MaxSize);
|
||||
|
||||
size_t secondsSinceProcessStartUp() {
|
||||
return duration_cast<seconds>(system_clock::now() - ProcessStartTime)
|
||||
.count();
|
||||
}
|
||||
|
||||
bool TimedOut() {
|
||||
return Options.MaxTotalTimeSec > 0 &&
|
||||
secondsSinceProcessStartUp() >
|
||||
static_cast<size_t>(Options.MaxTotalTimeSec);
|
||||
}
|
||||
|
||||
size_t execPerSec() {
|
||||
size_t Seconds = secondsSinceProcessStartUp();
|
||||
return Seconds ? TotalNumberOfRuns / Seconds : 0;
|
||||
}
|
||||
|
||||
size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; }
|
||||
|
||||
static void StaticAlarmCallback();
|
||||
static void StaticCrashSignalCallback();
|
||||
static void StaticInterruptCallback();
|
||||
|
||||
void ExecuteCallback(const uint8_t *Data, size_t Size);
|
||||
size_t RunOne(const uint8_t *Data, size_t Size);
|
||||
|
||||
// Merge Corpora[1:] into Corpora[0].
|
||||
void Merge(const std::vector<std::string> &Corpora);
|
||||
void CrashResistantMerge(const std::vector<std::string> &Args,
|
||||
const std::vector<std::string> &Corpora);
|
||||
void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
|
||||
// Returns a subset of 'Extra' that adds coverage to 'Initial'.
|
||||
UnitVector FindExtraUnits(const UnitVector &Initial, const UnitVector &Extra);
|
||||
MutationDispatcher &GetMD() { return MD; }
|
||||
void PrintFinalStats();
|
||||
void SetMaxInputLen(size_t MaxInputLen);
|
||||
void SetMaxMutationLen(size_t MaxMutationLen);
|
||||
void RssLimitCallback();
|
||||
|
||||
// Public for tests.
|
||||
void ResetCoverage();
|
||||
|
||||
bool InFuzzingThread() const { return IsMyThread; }
|
||||
size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const;
|
||||
void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
|
||||
bool DuringInitialCorpusExecution);
|
||||
|
||||
void HandleMalloc(size_t Size);
|
||||
|
||||
private:
|
||||
void AlarmCallback();
|
||||
void CrashCallback();
|
||||
void InterruptCallback();
|
||||
void MutateAndTestOne();
|
||||
void ReportNewCoverage(InputInfo *II, const Unit &U);
|
||||
size_t RunOne(const Unit &U) { return RunOne(U.data(), U.size()); }
|
||||
void WriteToOutputCorpus(const Unit &U);
|
||||
void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix);
|
||||
void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0);
|
||||
void PrintStatusForNewUnit(const Unit &U);
|
||||
void ShuffleCorpus(UnitVector *V);
|
||||
void AddToCorpus(const Unit &U);
|
||||
void CheckExitOnSrcPosOrItem();
|
||||
|
||||
// Trace-based fuzzing: we run a unit with some kind of tracing
|
||||
// enabled and record potentially useful mutations. Then
|
||||
// We apply these mutations one by one to the unit and run it again.
|
||||
|
||||
// Start tracing; forget all previously proposed mutations.
|
||||
void StartTraceRecording();
|
||||
// Stop tracing.
|
||||
void StopTraceRecording();
|
||||
|
||||
void SetDeathCallback();
|
||||
static void StaticDeathCallback();
|
||||
void DumpCurrentUnit(const char *Prefix);
|
||||
void DeathCallback();
|
||||
|
||||
void ResetEdgeCoverage();
|
||||
void ResetCounters();
|
||||
void PrepareCounters(Fuzzer::Coverage *C);
|
||||
bool RecordMaxCoverage(Fuzzer::Coverage *C);
|
||||
|
||||
void AllocateCurrentUnitData();
|
||||
uint8_t *CurrentUnitData = nullptr;
|
||||
std::atomic<size_t> CurrentUnitSize;
|
||||
uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit.
|
||||
bool RunningCB = false;
|
||||
|
||||
size_t TotalNumberOfRuns = 0;
|
||||
size_t NumberOfNewUnitsAdded = 0;
|
||||
|
||||
bool HasMoreMallocsThanFrees = false;
|
||||
size_t NumberOfLeakDetectionAttempts = 0;
|
||||
|
||||
UserCallback CB;
|
||||
InputCorpus &Corpus;
|
||||
MutationDispatcher &MD;
|
||||
FuzzingOptions Options;
|
||||
|
||||
system_clock::time_point ProcessStartTime = system_clock::now();
|
||||
system_clock::time_point UnitStartTime, UnitStopTime;
|
||||
long TimeOfLongestUnitInSeconds = 0;
|
||||
long EpochOfLastReadOfOutputCorpus = 0;
|
||||
|
||||
// Maximum recorded coverage.
|
||||
Coverage MaxCoverage;
|
||||
|
||||
size_t MaxInputLen = 0;
|
||||
size_t MaxMutationLen = 0;
|
||||
|
||||
// Need to know our own thread.
|
||||
static thread_local bool IsMyThread;
|
||||
|
||||
bool InMergeMode = false;
|
||||
};
|
||||
|
||||
}; // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_INTERNAL_H
|
792
test/thirdparty/Fuzzer/FuzzerLoop.cpp
vendored
Normal file
792
test/thirdparty/Fuzzer/FuzzerLoop.cpp
vendored
Normal file
|
@ -0,0 +1,792 @@
|
|||
//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Fuzzer's main loop.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<sanitizer / coverage_interface.h>)
|
||||
#include <sanitizer/coverage_interface.h>
|
||||
#endif
|
||||
#if __has_include(<sanitizer / lsan_interface.h>)
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NO_SANITIZE_MEMORY
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#undef NO_SANITIZE_MEMORY
|
||||
#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace fuzzer {
|
||||
static const size_t kMaxUnitSizeToPrint = 256;
|
||||
|
||||
thread_local bool Fuzzer::IsMyThread;
|
||||
|
||||
static void MissingExternalApiFunction(const char *FnName) {
|
||||
Printf("ERROR: %s is not defined. Exiting.\n"
|
||||
"Did you use -fsanitize-coverage=... to build your code?\n",
|
||||
FnName);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define CHECK_EXTERNAL_FUNCTION(fn) \
|
||||
do { \
|
||||
if (!(EF->fn)) \
|
||||
MissingExternalApiFunction(#fn); \
|
||||
} while (false)
|
||||
|
||||
// Only one Fuzzer per process.
|
||||
static Fuzzer *F;
|
||||
|
||||
void Fuzzer::ResetEdgeCoverage() {
|
||||
CHECK_EXTERNAL_FUNCTION(__sanitizer_reset_coverage);
|
||||
EF->__sanitizer_reset_coverage();
|
||||
}
|
||||
|
||||
void Fuzzer::ResetCounters() {
|
||||
if (Options.UseCounters)
|
||||
EF->__sanitizer_update_counter_bitset_and_clear_counters(0);
|
||||
}
|
||||
|
||||
void Fuzzer::PrepareCounters(Fuzzer::Coverage *C) {
|
||||
if (Options.UseCounters) {
|
||||
size_t NumCounters = EF->__sanitizer_get_number_of_counters();
|
||||
C->CounterBitmap.resize(NumCounters);
|
||||
}
|
||||
}
|
||||
|
||||
// Records data to a maximum coverage tracker. Returns true if additional
|
||||
// coverage was discovered.
|
||||
bool Fuzzer::RecordMaxCoverage(Fuzzer::Coverage *C) {
|
||||
bool Res = false;
|
||||
|
||||
uint64_t NewBlockCoverage = EF->__sanitizer_get_total_unique_coverage();
|
||||
if (NewBlockCoverage > C->BlockCoverage) {
|
||||
Res = true;
|
||||
C->BlockCoverage = NewBlockCoverage;
|
||||
}
|
||||
|
||||
if (Options.UseIndirCalls &&
|
||||
EF->__sanitizer_get_total_unique_caller_callee_pairs) {
|
||||
uint64_t NewCallerCalleeCoverage =
|
||||
EF->__sanitizer_get_total_unique_caller_callee_pairs();
|
||||
if (NewCallerCalleeCoverage > C->CallerCalleeCoverage) {
|
||||
Res = true;
|
||||
C->CallerCalleeCoverage = NewCallerCalleeCoverage;
|
||||
}
|
||||
}
|
||||
|
||||
if (Options.UseCounters) {
|
||||
uint64_t CounterDelta =
|
||||
EF->__sanitizer_update_counter_bitset_and_clear_counters(
|
||||
C->CounterBitmap.data());
|
||||
if (CounterDelta > 0) {
|
||||
Res = true;
|
||||
C->CounterBitmapBits += CounterDelta;
|
||||
}
|
||||
}
|
||||
|
||||
return Res;
|
||||
}
|
||||
|
||||
// Leak detection is expensive, so we first check if there were more mallocs
|
||||
// than frees (using the sanitizer malloc hooks) and only then try to call lsan.
|
||||
struct MallocFreeTracer {
|
||||
void Start(int TraceLevel) {
|
||||
this->TraceLevel = TraceLevel;
|
||||
if (TraceLevel)
|
||||
Printf("MallocFreeTracer: START\n");
|
||||
Mallocs = 0;
|
||||
Frees = 0;
|
||||
}
|
||||
// Returns true if there were more mallocs than frees.
|
||||
bool Stop() {
|
||||
if (TraceLevel)
|
||||
Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(),
|
||||
Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT");
|
||||
bool Result = Mallocs > Frees;
|
||||
Mallocs = 0;
|
||||
Frees = 0;
|
||||
TraceLevel = 0;
|
||||
return Result;
|
||||
}
|
||||
std::atomic<size_t> Mallocs;
|
||||
std::atomic<size_t> Frees;
|
||||
int TraceLevel = 0;
|
||||
};
|
||||
|
||||
static MallocFreeTracer AllocTracer;
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void MallocHook(const volatile void *ptr, size_t size) {
|
||||
size_t N = AllocTracer.Mallocs++;
|
||||
F->HandleMalloc(size);
|
||||
if (int TraceLevel = AllocTracer.TraceLevel) {
|
||||
Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);
|
||||
if (TraceLevel >= 2 && EF)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
}
|
||||
}
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void FreeHook(const volatile void *ptr) {
|
||||
size_t N = AllocTracer.Frees++;
|
||||
if (int TraceLevel = AllocTracer.TraceLevel) {
|
||||
Printf("FREE[%zd] %p\n", N, ptr);
|
||||
if (TraceLevel >= 2 && EF)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
}
|
||||
}
|
||||
|
||||
// Crash on a single malloc that exceeds the rss limit.
|
||||
void Fuzzer::HandleMalloc(size_t Size) {
|
||||
if (!Options.RssLimitMb || (Size >> 20) < (size_t)Options.RssLimitMb)
|
||||
return;
|
||||
Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(),
|
||||
Size);
|
||||
Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
|
||||
if (EF->__sanitizer_print_stack_trace)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
DumpCurrentUnit("oom-");
|
||||
Printf("SUMMARY: libFuzzer: out-of-memory\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode); // Stop right now.
|
||||
}
|
||||
|
||||
Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
|
||||
FuzzingOptions Options)
|
||||
: CB(CB), Corpus(Corpus), MD(MD), Options(Options) {
|
||||
SetDeathCallback();
|
||||
InitializeTraceState();
|
||||
assert(!F);
|
||||
F = this;
|
||||
TPC.ResetMaps();
|
||||
ResetCoverage();
|
||||
IsMyThread = true;
|
||||
if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks)
|
||||
EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);
|
||||
TPC.SetUseCounters(Options.UseCounters);
|
||||
TPC.SetUseValueProfile(Options.UseValueProfile);
|
||||
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
|
||||
|
||||
if (Options.Verbosity)
|
||||
TPC.PrintModuleInfo();
|
||||
if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)
|
||||
EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);
|
||||
MaxInputLen = MaxMutationLen = Options.MaxLen;
|
||||
AllocateCurrentUnitData();
|
||||
CurrentUnitSize = 0;
|
||||
memset(BaseSha1, 0, sizeof(BaseSha1));
|
||||
}
|
||||
|
||||
Fuzzer::~Fuzzer() { }
|
||||
|
||||
void Fuzzer::AllocateCurrentUnitData() {
|
||||
if (CurrentUnitData || MaxInputLen == 0) return;
|
||||
CurrentUnitData = new uint8_t[MaxInputLen];
|
||||
}
|
||||
|
||||
void Fuzzer::SetDeathCallback() {
|
||||
CHECK_EXTERNAL_FUNCTION(__sanitizer_set_death_callback);
|
||||
EF->__sanitizer_set_death_callback(StaticDeathCallback);
|
||||
}
|
||||
|
||||
void Fuzzer::StaticDeathCallback() {
|
||||
assert(F);
|
||||
F->DeathCallback();
|
||||
}
|
||||
|
||||
static void WarnOnUnsuccessfullMerge(bool DoWarn) {
|
||||
if (!DoWarn) return;
|
||||
Printf(
|
||||
"***\n"
|
||||
"***\n"
|
||||
"***\n"
|
||||
"*** NOTE: merge did not succeed due to a failure on one of the inputs.\n"
|
||||
"*** You will need to filter out crashes from the corpus, e.g. like this:\n"
|
||||
"*** for f in WITH_CRASHES/*; do ./fuzzer $f && cp $f NO_CRASHES; done\n"
|
||||
"*** Future versions may have crash-resistant merge, stay tuned.\n"
|
||||
"***\n"
|
||||
"***\n"
|
||||
"***\n");
|
||||
}
|
||||
|
||||
void Fuzzer::DumpCurrentUnit(const char *Prefix) {
|
||||
WarnOnUnsuccessfullMerge(InMergeMode);
|
||||
if (!CurrentUnitData) return; // Happens when running individual inputs.
|
||||
MD.PrintMutationSequence();
|
||||
Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str());
|
||||
size_t UnitSize = CurrentUnitSize;
|
||||
if (UnitSize <= kMaxUnitSizeToPrint) {
|
||||
PrintHexArray(CurrentUnitData, UnitSize, "\n");
|
||||
PrintASCII(CurrentUnitData, UnitSize, "\n");
|
||||
}
|
||||
WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize},
|
||||
Prefix);
|
||||
}
|
||||
|
||||
NO_SANITIZE_MEMORY
|
||||
void Fuzzer::DeathCallback() {
|
||||
DumpCurrentUnit("crash-");
|
||||
PrintFinalStats();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticAlarmCallback() {
|
||||
assert(F);
|
||||
F->AlarmCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticCrashSignalCallback() {
|
||||
assert(F);
|
||||
F->CrashCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticInterruptCallback() {
|
||||
assert(F);
|
||||
F->InterruptCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::CrashCallback() {
|
||||
Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
|
||||
if (EF->__sanitizer_print_stack_trace)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
Printf("NOTE: libFuzzer has rudimentary signal handlers.\n"
|
||||
" Combine libFuzzer with AddressSanitizer or similar for better "
|
||||
"crash reports.\n");
|
||||
Printf("SUMMARY: libFuzzer: deadly signal\n");
|
||||
DumpCurrentUnit("crash-");
|
||||
PrintFinalStats();
|
||||
exit(Options.ErrorExitCode);
|
||||
}
|
||||
|
||||
void Fuzzer::InterruptCallback() {
|
||||
Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());
|
||||
PrintFinalStats();
|
||||
_Exit(0); // Stop right now, don't perform any at-exit actions.
|
||||
}
|
||||
|
||||
NO_SANITIZE_MEMORY
|
||||
void Fuzzer::AlarmCallback() {
|
||||
assert(Options.UnitTimeoutSec > 0);
|
||||
if (!InFuzzingThread()) return;
|
||||
if (!RunningCB)
|
||||
return; // We have not started running units yet.
|
||||
size_t Seconds =
|
||||
duration_cast<seconds>(system_clock::now() - UnitStartTime).count();
|
||||
if (Seconds == 0)
|
||||
return;
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("AlarmCallback %zd\n", Seconds);
|
||||
if (Seconds >= (size_t)Options.UnitTimeoutSec) {
|
||||
Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds);
|
||||
Printf(" and the timeout value is %d (use -timeout=N to change)\n",
|
||||
Options.UnitTimeoutSec);
|
||||
DumpCurrentUnit("timeout-");
|
||||
Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(),
|
||||
Seconds);
|
||||
if (EF->__sanitizer_print_stack_trace)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
Printf("SUMMARY: libFuzzer: timeout\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.TimeoutExitCode); // Stop right now.
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::RssLimitCallback() {
|
||||
Printf(
|
||||
"==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n",
|
||||
GetPid(), GetPeakRSSMb(), Options.RssLimitMb);
|
||||
Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
|
||||
if (EF->__sanitizer_print_memory_profile)
|
||||
EF->__sanitizer_print_memory_profile(95);
|
||||
DumpCurrentUnit("oom-");
|
||||
Printf("SUMMARY: libFuzzer: out-of-memory\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode); // Stop right now.
|
||||
}
|
||||
|
||||
void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
|
||||
size_t ExecPerSec = execPerSec();
|
||||
if (Options.OutputCSV) {
|
||||
static bool csvHeaderPrinted = false;
|
||||
if (!csvHeaderPrinted) {
|
||||
csvHeaderPrinted = true;
|
||||
Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n");
|
||||
}
|
||||
Printf("%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns,
|
||||
MaxCoverage.BlockCoverage, MaxCoverage.CounterBitmapBits,
|
||||
MaxCoverage.CallerCalleeCoverage, Corpus.size(), ExecPerSec, Where);
|
||||
}
|
||||
|
||||
if (!Options.Verbosity)
|
||||
return;
|
||||
Printf("#%zd\t%s", TotalNumberOfRuns, Where);
|
||||
if (MaxCoverage.BlockCoverage)
|
||||
Printf(" cov: %zd", MaxCoverage.BlockCoverage);
|
||||
if (size_t N = MaxCoverage.VPMap.GetNumBitsSinceLastMerge())
|
||||
Printf(" vp: %zd", N);
|
||||
if (size_t N = TPC.GetTotalPCCoverage())
|
||||
Printf(" cov: %zd", N);
|
||||
if (auto TB = MaxCoverage.CounterBitmapBits)
|
||||
Printf(" bits: %zd", TB);
|
||||
if (size_t N = Corpus.NumFeatures())
|
||||
Printf( " ft: %zd", N);
|
||||
if (MaxCoverage.CallerCalleeCoverage)
|
||||
Printf(" indir: %zd", MaxCoverage.CallerCalleeCoverage);
|
||||
if (!Corpus.empty()) {
|
||||
Printf(" corp: %zd", Corpus.NumActiveUnits());
|
||||
if (size_t N = Corpus.SizeInBytes()) {
|
||||
if (N < (1<<14))
|
||||
Printf("/%zdb", N);
|
||||
else if (N < (1 << 24))
|
||||
Printf("/%zdKb", N >> 10);
|
||||
else
|
||||
Printf("/%zdMb", N >> 20);
|
||||
}
|
||||
}
|
||||
if (Units)
|
||||
Printf(" units: %zd", Units);
|
||||
|
||||
Printf(" exec/s: %zd", ExecPerSec);
|
||||
Printf(" rss: %zdMb", GetPeakRSSMb());
|
||||
Printf("%s", End);
|
||||
}
|
||||
|
||||
void Fuzzer::PrintFinalStats() {
|
||||
if (Options.PrintCoverage)
|
||||
TPC.PrintCoverage();
|
||||
if (Options.DumpCoverage)
|
||||
TPC.DumpCoverage();
|
||||
if (Options.PrintCorpusStats)
|
||||
Corpus.PrintStats();
|
||||
if (!Options.PrintFinalStats) return;
|
||||
size_t ExecPerSec = execPerSec();
|
||||
Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns);
|
||||
Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec);
|
||||
Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded);
|
||||
Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds);
|
||||
Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb());
|
||||
}
|
||||
|
||||
void Fuzzer::SetMaxInputLen(size_t MaxInputLen) {
|
||||
assert(this->MaxInputLen == 0); // Can only reset MaxInputLen from 0 to non-0.
|
||||
assert(MaxInputLen);
|
||||
this->MaxInputLen = MaxInputLen;
|
||||
this->MaxMutationLen = MaxInputLen;
|
||||
AllocateCurrentUnitData();
|
||||
Printf("INFO: -max_len is not provided, using %zd\n", MaxInputLen);
|
||||
}
|
||||
|
||||
void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {
|
||||
assert(MaxMutationLen && MaxMutationLen <= MaxInputLen);
|
||||
this->MaxMutationLen = MaxMutationLen;
|
||||
}
|
||||
|
||||
void Fuzzer::CheckExitOnSrcPosOrItem() {
|
||||
if (!Options.ExitOnSrcPos.empty()) {
|
||||
static auto *PCsSet = new std::set<uintptr_t>;
|
||||
for (size_t i = 1, N = TPC.GetNumPCs(); i < N; i++) {
|
||||
uintptr_t PC = TPC.GetPC(i);
|
||||
if (!PC) continue;
|
||||
if (!PCsSet->insert(PC).second) continue;
|
||||
std::string Descr = DescribePC("%L", PC);
|
||||
if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {
|
||||
Printf("INFO: found line matching '%s', exiting.\n",
|
||||
Options.ExitOnSrcPos.c_str());
|
||||
_Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Options.ExitOnItem.empty()) {
|
||||
if (Corpus.HasUnit(Options.ExitOnItem)) {
|
||||
Printf("INFO: found item with checksum '%s', exiting.\n",
|
||||
Options.ExitOnItem.c_str());
|
||||
_Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::RereadOutputCorpus(size_t MaxSize) {
|
||||
if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return;
|
||||
std::vector<Unit> AdditionalCorpus;
|
||||
ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus,
|
||||
&EpochOfLastReadOfOutputCorpus, MaxSize,
|
||||
/*ExitOnError*/ false);
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("Reload: read %zd new units.\n", AdditionalCorpus.size());
|
||||
bool Reloaded = false;
|
||||
for (auto &U : AdditionalCorpus) {
|
||||
if (U.size() > MaxSize)
|
||||
U.resize(MaxSize);
|
||||
if (!Corpus.HasUnit(U)) {
|
||||
if (size_t NumFeatures = RunOne(U)) {
|
||||
CheckExitOnSrcPosOrItem();
|
||||
Corpus.AddToCorpus(U, NumFeatures);
|
||||
Reloaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Reloaded)
|
||||
PrintStats("RELOAD");
|
||||
}
|
||||
|
||||
void Fuzzer::ShuffleCorpus(UnitVector *V) {
|
||||
std::random_shuffle(V->begin(), V->end(), MD.GetRand());
|
||||
if (Options.PreferSmall)
|
||||
std::stable_sort(V->begin(), V->end(), [](const Unit &A, const Unit &B) {
|
||||
return A.size() < B.size();
|
||||
});
|
||||
}
|
||||
|
||||
void Fuzzer::ShuffleAndMinimize(UnitVector *InitialCorpus) {
|
||||
Printf("#0\tREAD units: %zd\n", InitialCorpus->size());
|
||||
if (Options.ShuffleAtStartUp)
|
||||
ShuffleCorpus(InitialCorpus);
|
||||
|
||||
// Test the callback with empty input and never try it again.
|
||||
uint8_t dummy;
|
||||
ExecuteCallback(&dummy, 0);
|
||||
|
||||
for (const auto &U : *InitialCorpus) {
|
||||
if (size_t NumFeatures = RunOne(U)) {
|
||||
CheckExitOnSrcPosOrItem();
|
||||
Corpus.AddToCorpus(U, NumFeatures);
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size());
|
||||
}
|
||||
TryDetectingAMemoryLeak(U.data(), U.size(),
|
||||
/*DuringInitialCorpusExecution*/ true);
|
||||
}
|
||||
PrintStats("INITED");
|
||||
if (Corpus.empty()) {
|
||||
Printf("ERROR: no interesting inputs were found. "
|
||||
"Is the code instrumented for coverage? Exiting.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) {
|
||||
if (!Size) return 0;
|
||||
TotalNumberOfRuns++;
|
||||
|
||||
ExecuteCallback(Data, Size);
|
||||
|
||||
size_t Res = 0;
|
||||
if (size_t NumFeatures = TPC.CollectFeatures([&](size_t Feature) -> bool {
|
||||
return Corpus.AddFeature(Feature, Size, Options.Shrink);
|
||||
}))
|
||||
Res = NumFeatures;
|
||||
|
||||
if (!TPC.UsingTracePcGuard()) {
|
||||
if (TPC.UpdateValueProfileMap(&MaxCoverage.VPMap))
|
||||
Res = 1;
|
||||
if (!Res && RecordMaxCoverage(&MaxCoverage))
|
||||
Res = 1;
|
||||
}
|
||||
|
||||
auto TimeOfUnit =
|
||||
duration_cast<seconds>(UnitStopTime - UnitStartTime).count();
|
||||
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) &&
|
||||
secondsSinceProcessStartUp() >= 2)
|
||||
PrintStats("pulse ");
|
||||
if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 &&
|
||||
TimeOfUnit >= Options.ReportSlowUnits) {
|
||||
TimeOfLongestUnitInSeconds = TimeOfUnit;
|
||||
Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds);
|
||||
WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-");
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {
|
||||
assert(InFuzzingThread());
|
||||
*Data = CurrentUnitData;
|
||||
return CurrentUnitSize;
|
||||
}
|
||||
|
||||
void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
|
||||
assert(InFuzzingThread());
|
||||
// We copy the contents of Unit into a separate heap buffer
|
||||
// so that we reliably find buffer overflows in it.
|
||||
uint8_t *DataCopy = new uint8_t[Size];
|
||||
memcpy(DataCopy, Data, Size);
|
||||
if (CurrentUnitData && CurrentUnitData != Data)
|
||||
memcpy(CurrentUnitData, Data, Size);
|
||||
CurrentUnitSize = Size;
|
||||
AllocTracer.Start(Options.TraceMalloc);
|
||||
UnitStartTime = system_clock::now();
|
||||
ResetCounters(); // Reset coverage right before the callback.
|
||||
TPC.ResetMaps();
|
||||
RunningCB = true;
|
||||
int Res = CB(DataCopy, Size);
|
||||
RunningCB = false;
|
||||
UnitStopTime = system_clock::now();
|
||||
(void)Res;
|
||||
assert(Res == 0);
|
||||
HasMoreMallocsThanFrees = AllocTracer.Stop();
|
||||
CurrentUnitSize = 0;
|
||||
delete[] DataCopy;
|
||||
}
|
||||
|
||||
void Fuzzer::WriteToOutputCorpus(const Unit &U) {
|
||||
if (Options.OnlyASCII)
|
||||
assert(IsASCII(U));
|
||||
if (Options.OutputCorpus.empty())
|
||||
return;
|
||||
std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));
|
||||
WriteToFile(U, Path);
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("Written to %s\n", Path.c_str());
|
||||
}
|
||||
|
||||
void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
|
||||
if (!Options.SaveArtifacts)
|
||||
return;
|
||||
std::string Path = Options.ArtifactPrefix + Prefix + Hash(U);
|
||||
if (!Options.ExactArtifactPath.empty())
|
||||
Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix.
|
||||
WriteToFile(U, Path);
|
||||
Printf("artifact_prefix='%s'; Test unit written to %s\n",
|
||||
Options.ArtifactPrefix.c_str(), Path.c_str());
|
||||
if (U.size() <= kMaxUnitSizeToPrint)
|
||||
Printf("Base64: %s\n", Base64(U).c_str());
|
||||
}
|
||||
|
||||
void Fuzzer::PrintStatusForNewUnit(const Unit &U) {
|
||||
if (!Options.PrintNEW)
|
||||
return;
|
||||
PrintStats("NEW ", "");
|
||||
if (Options.Verbosity) {
|
||||
Printf(" L: %zd ", U.size());
|
||||
MD.PrintMutationSequence();
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) {
|
||||
II->NumSuccessfullMutations++;
|
||||
MD.RecordSuccessfulMutationSequence();
|
||||
PrintStatusForNewUnit(U);
|
||||
WriteToOutputCorpus(U);
|
||||
NumberOfNewUnitsAdded++;
|
||||
TPC.PrintNewPCs();
|
||||
}
|
||||
|
||||
// Finds minimal number of units in 'Extra' that add coverage to 'Initial'.
|
||||
// We do it by actually executing the units, sometimes more than once,
|
||||
// because we may be using different coverage-like signals and the only
|
||||
// common thing between them is that we can say "this unit found new stuff".
|
||||
UnitVector Fuzzer::FindExtraUnits(const UnitVector &Initial,
|
||||
const UnitVector &Extra) {
|
||||
UnitVector Res = Extra;
|
||||
UnitVector Tmp;
|
||||
size_t OldSize = Res.size();
|
||||
for (int Iter = 0; Iter < 10; Iter++) {
|
||||
ShuffleCorpus(&Res);
|
||||
TPC.ResetMaps();
|
||||
Corpus.ResetFeatureSet();
|
||||
ResetCoverage();
|
||||
|
||||
for (auto &U : Initial) {
|
||||
TPC.ResetMaps();
|
||||
RunOne(U);
|
||||
}
|
||||
|
||||
Tmp.clear();
|
||||
for (auto &U : Res) {
|
||||
TPC.ResetMaps();
|
||||
if (RunOne(U))
|
||||
Tmp.push_back(U);
|
||||
}
|
||||
|
||||
char Stat[7] = "MIN ";
|
||||
Stat[3] = '0' + Iter;
|
||||
PrintStats(Stat, "\n", Tmp.size());
|
||||
|
||||
size_t NewSize = Tmp.size();
|
||||
assert(NewSize <= OldSize);
|
||||
Res.swap(Tmp);
|
||||
|
||||
if (NewSize + 5 >= OldSize)
|
||||
break;
|
||||
OldSize = NewSize;
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
void Fuzzer::Merge(const std::vector<std::string> &Corpora) {
|
||||
if (Corpora.size() <= 1) {
|
||||
Printf("Merge requires two or more corpus dirs\n");
|
||||
return;
|
||||
}
|
||||
InMergeMode = true;
|
||||
std::vector<std::string> ExtraCorpora(Corpora.begin() + 1, Corpora.end());
|
||||
|
||||
assert(MaxInputLen > 0);
|
||||
UnitVector Initial, Extra;
|
||||
ReadDirToVectorOfUnits(Corpora[0].c_str(), &Initial, nullptr, MaxInputLen,
|
||||
true);
|
||||
for (auto &C : ExtraCorpora)
|
||||
ReadDirToVectorOfUnits(C.c_str(), &Extra, nullptr, MaxInputLen, true);
|
||||
|
||||
if (!Initial.empty()) {
|
||||
Printf("=== Minimizing the initial corpus of %zd units\n", Initial.size());
|
||||
Initial = FindExtraUnits({}, Initial);
|
||||
}
|
||||
|
||||
Printf("=== Merging extra %zd units\n", Extra.size());
|
||||
auto Res = FindExtraUnits(Initial, Extra);
|
||||
|
||||
for (auto &U: Res)
|
||||
WriteToOutputCorpus(U);
|
||||
|
||||
Printf("=== Merge: written %zd units\n", Res.size());
|
||||
}
|
||||
|
||||
// Tries detecting a memory leak on the particular input that we have just
|
||||
// executed before calling this function.
|
||||
void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
|
||||
bool DuringInitialCorpusExecution) {
|
||||
if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely.
|
||||
if (!Options.DetectLeaks) return;
|
||||
if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) ||
|
||||
!(EF->__lsan_do_recoverable_leak_check))
|
||||
return; // No lsan.
|
||||
// Run the target once again, but with lsan disabled so that if there is
|
||||
// a real leak we do not report it twice.
|
||||
EF->__lsan_disable();
|
||||
ExecuteCallback(Data, Size);
|
||||
EF->__lsan_enable();
|
||||
if (!HasMoreMallocsThanFrees) return; // a leak is unlikely.
|
||||
if (NumberOfLeakDetectionAttempts++ > 1000) {
|
||||
Options.DetectLeaks = false;
|
||||
Printf("INFO: libFuzzer disabled leak detection after every mutation.\n"
|
||||
" Most likely the target function accumulates allocated\n"
|
||||
" memory in a global state w/o actually leaking it.\n"
|
||||
" You may try running this binary with -trace_malloc=[12]"
|
||||
" to get a trace of mallocs and frees.\n"
|
||||
" If LeakSanitizer is enabled in this process it will still\n"
|
||||
" run on the process shutdown.\n");
|
||||
return;
|
||||
}
|
||||
// Now perform the actual lsan pass. This is expensive and we must ensure
|
||||
// we don't call it too often.
|
||||
if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it.
|
||||
if (DuringInitialCorpusExecution)
|
||||
Printf("\nINFO: a leak has been found in the initial corpus.\n\n");
|
||||
Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n");
|
||||
CurrentUnitSize = Size;
|
||||
DumpCurrentUnit("leak-");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode); // not exit() to disable lsan further on.
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::MutateAndTestOne() {
|
||||
MD.StartMutationSequence();
|
||||
|
||||
auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());
|
||||
const auto &U = II.U;
|
||||
memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1));
|
||||
assert(CurrentUnitData);
|
||||
size_t Size = U.size();
|
||||
assert(Size <= MaxInputLen && "Oversized Unit");
|
||||
memcpy(CurrentUnitData, U.data(), Size);
|
||||
|
||||
assert(MaxMutationLen > 0);
|
||||
|
||||
for (int i = 0; i < Options.MutateDepth; i++) {
|
||||
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
|
||||
break;
|
||||
size_t NewSize = 0;
|
||||
NewSize = MD.Mutate(CurrentUnitData, Size, MaxMutationLen);
|
||||
assert(NewSize > 0 && "Mutator returned empty unit");
|
||||
assert(NewSize <= MaxMutationLen && "Mutator return overisized unit");
|
||||
Size = NewSize;
|
||||
if (i == 0)
|
||||
StartTraceRecording();
|
||||
II.NumExecutedMutations++;
|
||||
if (size_t NumFeatures = RunOne(CurrentUnitData, Size)) {
|
||||
Corpus.AddToCorpus({CurrentUnitData, CurrentUnitData + Size}, NumFeatures,
|
||||
/*MayDeleteFile=*/true);
|
||||
ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size});
|
||||
CheckExitOnSrcPosOrItem();
|
||||
}
|
||||
StopTraceRecording();
|
||||
TryDetectingAMemoryLeak(CurrentUnitData, Size,
|
||||
/*DuringInitialCorpusExecution*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::ResetCoverage() {
|
||||
ResetEdgeCoverage();
|
||||
MaxCoverage.Reset();
|
||||
PrepareCounters(&MaxCoverage);
|
||||
}
|
||||
|
||||
void Fuzzer::Loop() {
|
||||
system_clock::time_point LastCorpusReload = system_clock::now();
|
||||
if (Options.DoCrossOver)
|
||||
MD.SetCorpus(&Corpus);
|
||||
while (true) {
|
||||
auto Now = system_clock::now();
|
||||
if (duration_cast<seconds>(Now - LastCorpusReload).count() >=
|
||||
Options.ReloadIntervalSec) {
|
||||
RereadOutputCorpus(MaxInputLen);
|
||||
LastCorpusReload = system_clock::now();
|
||||
}
|
||||
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
|
||||
break;
|
||||
if (TimedOut()) break;
|
||||
// Perform several mutations and runs.
|
||||
MutateAndTestOne();
|
||||
}
|
||||
|
||||
PrintStats("DONE ", "\n");
|
||||
MD.PrintRecommendedDictionary();
|
||||
}
|
||||
|
||||
void Fuzzer::MinimizeCrashLoop(const Unit &U) {
|
||||
if (U.size() <= 2) return;
|
||||
while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) {
|
||||
MD.StartMutationSequence();
|
||||
memcpy(CurrentUnitData, U.data(), U.size());
|
||||
for (int i = 0; i < Options.MutateDepth; i++) {
|
||||
size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen);
|
||||
assert(NewSize > 0 && NewSize <= MaxMutationLen);
|
||||
RunOne(CurrentUnitData, NewSize);
|
||||
TryDetectingAMemoryLeak(CurrentUnitData, NewSize,
|
||||
/*DuringInitialCorpusExecution*/ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
extern "C" {
|
||||
|
||||
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
assert(fuzzer::F);
|
||||
return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
|
||||
}
|
||||
} // extern "C"
|
21
test/thirdparty/Fuzzer/FuzzerMain.cpp
vendored
Normal file
21
test/thirdparty/Fuzzer/FuzzerMain.cpp
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
//===- FuzzerMain.cpp - main() function and flags -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// main() and flags.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
extern "C" {
|
||||
// This function should be defined by the user.
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
} // extern "C"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput);
|
||||
}
|
261
test/thirdparty/Fuzzer/FuzzerMerge.cpp
vendored
Normal file
261
test/thirdparty/Fuzzer/FuzzerMerge.cpp
vendored
Normal file
|
@ -0,0 +1,261 @@
|
|||
//===- FuzzerMerge.cpp - merging corpora ----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Merging corpora.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerMerge.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include "FuzzerUtil.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
bool Merger::Parse(const std::string &Str, bool ParseCoverage) {
|
||||
std::istringstream SS(Str);
|
||||
return Parse(SS, ParseCoverage);
|
||||
}
|
||||
|
||||
void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) {
|
||||
if (!Parse(IS, ParseCoverage)) {
|
||||
Printf("MERGE: failed to parse the control file (unexpected error)\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// The control file example:
|
||||
//
|
||||
// 3 # The number of inputs
|
||||
// 1 # The number of inputs in the first corpus, <= the previous number
|
||||
// file0
|
||||
// file1
|
||||
// file2 # One file name per line.
|
||||
// STARTED 0 123 # FileID, file size
|
||||
// DONE 0 1 4 6 8 # FileID COV1 COV2 ...
|
||||
// STARTED 1 456 # If DONE is missing, the input crashed while processing.
|
||||
// STARTED 2 567
|
||||
// DONE 2 8 9
|
||||
bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
|
||||
LastFailure.clear();
|
||||
std::string Line;
|
||||
|
||||
// Parse NumFiles.
|
||||
if (!std::getline(IS, Line, '\n')) return false;
|
||||
std::istringstream L1(Line);
|
||||
size_t NumFiles = 0;
|
||||
L1 >> NumFiles;
|
||||
if (NumFiles == 0 || NumFiles > 10000000) return false;
|
||||
|
||||
// Parse NumFilesInFirstCorpus.
|
||||
if (!std::getline(IS, Line, '\n')) return false;
|
||||
std::istringstream L2(Line);
|
||||
NumFilesInFirstCorpus = NumFiles + 1;
|
||||
L2 >> NumFilesInFirstCorpus;
|
||||
if (NumFilesInFirstCorpus > NumFiles) return false;
|
||||
|
||||
// Parse file names.
|
||||
Files.resize(NumFiles);
|
||||
for (size_t i = 0; i < NumFiles; i++)
|
||||
if (!std::getline(IS, Files[i].Name, '\n'))
|
||||
return false;
|
||||
|
||||
// Parse STARTED and DONE lines.
|
||||
size_t ExpectedStartMarker = 0;
|
||||
const size_t kInvalidStartMarker = -1;
|
||||
size_t LastSeenStartMarker = kInvalidStartMarker;
|
||||
while (std::getline(IS, Line, '\n')) {
|
||||
std::istringstream ISS1(Line);
|
||||
std::string Marker;
|
||||
size_t N;
|
||||
ISS1 >> Marker;
|
||||
ISS1 >> N;
|
||||
if (Marker == "STARTED") {
|
||||
// STARTED FILE_ID FILE_SIZE
|
||||
if (ExpectedStartMarker != N)
|
||||
return false;
|
||||
ISS1 >> Files[ExpectedStartMarker].Size;
|
||||
LastSeenStartMarker = ExpectedStartMarker;
|
||||
assert(ExpectedStartMarker < Files.size());
|
||||
ExpectedStartMarker++;
|
||||
} else if (Marker == "DONE") {
|
||||
// DONE FILE_SIZE COV1 COV2 COV3 ...
|
||||
size_t CurrentFileIdx = N;
|
||||
if (CurrentFileIdx != LastSeenStartMarker)
|
||||
return false;
|
||||
LastSeenStartMarker = kInvalidStartMarker;
|
||||
if (ParseCoverage) {
|
||||
auto &V = Files[CurrentFileIdx].Features;
|
||||
V.clear();
|
||||
while (ISS1 >> std::hex >> N)
|
||||
V.push_back(N);
|
||||
std::sort(V.begin(), V.end());
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (LastSeenStartMarker != kInvalidStartMarker)
|
||||
LastFailure = Files[LastSeenStartMarker].Name;
|
||||
|
||||
FirstNotProcessedFile = ExpectedStartMarker;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Decides which files need to be merged (add thost to NewFiles).
|
||||
// Returns the number of new features added.
|
||||
size_t Merger::Merge(std::vector<std::string> *NewFiles) {
|
||||
NewFiles->clear();
|
||||
assert(NumFilesInFirstCorpus <= Files.size());
|
||||
std::set<uint32_t> AllFeatures;
|
||||
|
||||
// What features are in the initial corpus?
|
||||
for (size_t i = 0; i < NumFilesInFirstCorpus; i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
AllFeatures.insert(Cur.begin(), Cur.end());
|
||||
}
|
||||
size_t InitialNumFeatures = AllFeatures.size();
|
||||
|
||||
// Remove all features that we already know from all other inputs.
|
||||
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
std::vector<uint32_t> Tmp;
|
||||
std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(),
|
||||
AllFeatures.end(), std::inserter(Tmp, Tmp.begin()));
|
||||
Cur.swap(Tmp);
|
||||
}
|
||||
|
||||
// Sort. Give preference to
|
||||
// * smaller files
|
||||
// * files with more features.
|
||||
std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(),
|
||||
[&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool {
|
||||
if (a.Size != b.Size)
|
||||
return a.Size < b.Size;
|
||||
return a.Features.size() > b.Features.size();
|
||||
});
|
||||
|
||||
// One greedy pass: add the file's features to AllFeatures.
|
||||
// If new features were added, add this file to NewFiles.
|
||||
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
// Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(),
|
||||
// Files[i].Size, Cur.size());
|
||||
size_t OldSize = AllFeatures.size();
|
||||
AllFeatures.insert(Cur.begin(), Cur.end());
|
||||
if (AllFeatures.size() > OldSize)
|
||||
NewFiles->push_back(Files[i].Name);
|
||||
}
|
||||
return AllFeatures.size() - InitialNumFeatures;
|
||||
}
|
||||
|
||||
// Inner process. May crash if the target crashes.
|
||||
void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
|
||||
Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str());
|
||||
Merger M;
|
||||
std::ifstream IF(CFPath);
|
||||
M.ParseOrExit(IF, false);
|
||||
IF.close();
|
||||
if (!M.LastFailure.empty())
|
||||
Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n",
|
||||
M.LastFailure.c_str());
|
||||
|
||||
Printf("MERGE-INNER: %zd total files;"
|
||||
" %zd processed earlier; will process %zd files now\n",
|
||||
M.Files.size(), M.FirstNotProcessedFile,
|
||||
M.Files.size() - M.FirstNotProcessedFile);
|
||||
|
||||
std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app);
|
||||
for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
|
||||
auto U = FileToVector(M.Files[i].Name);
|
||||
if (U.size() > MaxInputLen) {
|
||||
U.resize(MaxInputLen);
|
||||
U.shrink_to_fit();
|
||||
}
|
||||
std::ostringstream StartedLine;
|
||||
// Write the pre-run marker.
|
||||
OF << "STARTED " << std::dec << i << " " << U.size() << "\n";
|
||||
OF.flush(); // Flush is important since ExecuteCommand may crash.
|
||||
// Run.
|
||||
TPC.ResetMaps();
|
||||
ExecuteCallback(U.data(), U.size());
|
||||
// Collect coverage.
|
||||
std::set<size_t> Features;
|
||||
TPC.CollectFeatures([&](size_t Feature) -> bool {
|
||||
Features.insert(Feature);
|
||||
return true;
|
||||
});
|
||||
// Show stats.
|
||||
TotalNumberOfRuns++;
|
||||
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)))
|
||||
PrintStats("pulse ");
|
||||
// Write the post-run marker and the coverage.
|
||||
OF << "DONE " << i;
|
||||
for (size_t F : Features)
|
||||
OF << " " << std::hex << F;
|
||||
OF << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Outer process. Does not call the target code and thus sohuld not fail.
|
||||
void Fuzzer::CrashResistantMerge(const std::vector<std::string> &Args,
|
||||
const std::vector<std::string> &Corpora) {
|
||||
if (Corpora.size() <= 1) {
|
||||
Printf("Merge requires two or more corpus dirs\n");
|
||||
return;
|
||||
}
|
||||
std::vector<std::string> AllFiles;
|
||||
ListFilesInDirRecursive(Corpora[0], nullptr, &AllFiles, /*TopDir*/true);
|
||||
size_t NumFilesInFirstCorpus = AllFiles.size();
|
||||
for (size_t i = 1; i < Corpora.size(); i++)
|
||||
ListFilesInDirRecursive(Corpora[i], nullptr, &AllFiles, /*TopDir*/true);
|
||||
Printf("MERGE-OUTER: %zd files, %zd in the initial corpus\n",
|
||||
AllFiles.size(), NumFilesInFirstCorpus);
|
||||
std::string CFPath =
|
||||
"libFuzzerTemp." + std::to_string(GetPid()) + ".txt";
|
||||
// Write the control file.
|
||||
RemoveFile(CFPath);
|
||||
std::ofstream ControlFile(CFPath);
|
||||
ControlFile << AllFiles.size() << "\n";
|
||||
ControlFile << NumFilesInFirstCorpus << "\n";
|
||||
for (auto &Path: AllFiles)
|
||||
ControlFile << Path << "\n";
|
||||
ControlFile.close();
|
||||
|
||||
// Execute the inner process untill it passes.
|
||||
// Every inner process should execute at least one input.
|
||||
std::string BaseCmd = CloneArgsWithoutX(Args, "keep-all-flags");
|
||||
for (size_t i = 1; i <= AllFiles.size(); i++) {
|
||||
Printf("MERGE-OUTER: attempt %zd\n", i);
|
||||
auto ExitCode =
|
||||
ExecuteCommand(BaseCmd + " -merge_control_file=" + CFPath);
|
||||
if (!ExitCode) {
|
||||
Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Read the control file and do the merge.
|
||||
Merger M;
|
||||
std::ifstream IF(CFPath);
|
||||
M.ParseOrExit(IF, true);
|
||||
IF.close();
|
||||
std::vector<std::string> NewFiles;
|
||||
size_t NumNewFeatures = M.Merge(&NewFiles);
|
||||
Printf("MERGE-OUTER: %zd new files with %zd new features added\n",
|
||||
NewFiles.size(), NumNewFeatures);
|
||||
for (auto &F: NewFiles)
|
||||
WriteToOutputCorpus(FileToVector(F));
|
||||
// We are done, delete the control file.
|
||||
RemoveFile(CFPath);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
70
test/thirdparty/Fuzzer/FuzzerMerge.h
vendored
Normal file
70
test/thirdparty/Fuzzer/FuzzerMerge.h
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Merging Corpora.
|
||||
//
|
||||
// The task:
|
||||
// Take the existing corpus (possibly empty) and merge new inputs into
|
||||
// it so that only inputs with new coverage ('features') are added.
|
||||
// The process should tolerate the crashes, OOMs, leaks, etc.
|
||||
//
|
||||
// Algorithm:
|
||||
// The outter process collects the set of files and writes their names
|
||||
// into a temporary "control" file, then repeatedly launches the inner
|
||||
// process until all inputs are processed.
|
||||
// The outer process does not actually execute the target code.
|
||||
//
|
||||
// The inner process reads the control file and sees a) list of all the inputs
|
||||
// and b) the last processed input. Then it starts processing the inputs one
|
||||
// by one. Before processing every input it writes one line to control file:
|
||||
// STARTED INPUT_ID INPUT_SIZE
|
||||
// After processing an input it write another line:
|
||||
// DONE INPUT_ID Feature1 Feature2 Feature3 ...
|
||||
// If a crash happens while processing an input the last line in the control
|
||||
// file will be "STARTED INPUT_ID" and so the next process will know
|
||||
// where to resume.
|
||||
//
|
||||
// Once all inputs are processed by the innner process(es) the outer process
|
||||
// reads the control files and does the merge based entirely on the contents
|
||||
// of control file.
|
||||
// It uses a single pass greedy algorithm choosing first the smallest inputs
|
||||
// within the same size the inputs that have more new features.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MERGE_H
|
||||
#define LLVM_FUZZER_MERGE_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
#include <istream>
|
||||
#include <set>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct MergeFileInfo {
|
||||
std::string Name;
|
||||
size_t Size = 0;
|
||||
std::vector<uint32_t> Features;
|
||||
};
|
||||
|
||||
struct Merger {
|
||||
std::vector<MergeFileInfo> Files;
|
||||
size_t NumFilesInFirstCorpus = 0;
|
||||
size_t FirstNotProcessedFile = 0;
|
||||
std::string LastFailure;
|
||||
|
||||
bool Parse(std::istream &IS, bool ParseCoverage);
|
||||
bool Parse(const std::string &Str, bool ParseCoverage);
|
||||
void ParseOrExit(std::istream &IS, bool ParseCoverage);
|
||||
size_t Merge(std::vector<std::string> *NewFiles);
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_MERGE_H
|
527
test/thirdparty/Fuzzer/FuzzerMutate.cpp
vendored
Normal file
527
test/thirdparty/Fuzzer/FuzzerMutate.cpp
vendored
Normal file
|
@ -0,0 +1,527 @@
|
|||
//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Mutate a test input.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerOptions.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
const size_t Dictionary::kMaxDictSize;
|
||||
|
||||
static void PrintASCII(const Word &W, const char *PrintAfter) {
|
||||
PrintASCII(W.data(), W.size(), PrintAfter);
|
||||
}
|
||||
|
||||
MutationDispatcher::MutationDispatcher(Random &Rand,
|
||||
const FuzzingOptions &Options)
|
||||
: Rand(Rand), Options(Options) {
|
||||
DefaultMutators.insert(
|
||||
DefaultMutators.begin(),
|
||||
{
|
||||
{&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
|
||||
{&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
|
||||
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
|
||||
"InsertRepeatedBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
|
||||
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
|
||||
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
|
||||
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
|
||||
{&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
|
||||
{&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
|
||||
"ManualDict"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary,
|
||||
"TempAutoDict"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
|
||||
"PersAutoDict"},
|
||||
});
|
||||
if(Options.UseCmp)
|
||||
DefaultMutators.push_back(
|
||||
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
|
||||
|
||||
if (EF->LLVMFuzzerCustomMutator)
|
||||
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
|
||||
else
|
||||
Mutators = DefaultMutators;
|
||||
|
||||
if (EF->LLVMFuzzerCustomCrossOver)
|
||||
Mutators.push_back(
|
||||
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
|
||||
}
|
||||
|
||||
static char RandCh(Random &Rand) {
|
||||
if (Rand.RandBool()) return Rand(256);
|
||||
const char *Special = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
|
||||
return Special[Rand(sizeof(Special) - 1)];
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand());
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (!Corpus || Corpus->size() < 2 || Size == 0)
|
||||
return 0;
|
||||
size_t Idx = Rand(Corpus->size());
|
||||
const Unit &Other = (*Corpus)[Idx];
|
||||
if (Other.empty())
|
||||
return 0;
|
||||
MutateInPlaceHere.resize(MaxSize);
|
||||
auto &U = MutateInPlaceHere;
|
||||
size_t NewSize = EF->LLVMFuzzerCustomCrossOver(
|
||||
Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand());
|
||||
if (!NewSize)
|
||||
return 0;
|
||||
assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit");
|
||||
memcpy(Data, U.data(), NewSize);
|
||||
return NewSize;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
assert(Size);
|
||||
size_t ShuffleAmount =
|
||||
Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size.
|
||||
size_t ShuffleStart = Rand(Size - ShuffleAmount);
|
||||
assert(ShuffleStart + ShuffleAmount <= Size);
|
||||
std::random_shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount,
|
||||
Rand);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
assert(Size);
|
||||
if (Size == 1) return 0;
|
||||
size_t N = Rand(Size / 2) + 1;
|
||||
assert(N < Size);
|
||||
size_t Idx = Rand(Size - N + 1);
|
||||
// Erase Data[Idx:Idx+N].
|
||||
memmove(Data + Idx, Data + Idx + N, Size - Idx - N);
|
||||
// Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx);
|
||||
return Size - N;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size >= MaxSize) return 0;
|
||||
size_t Idx = Rand(Size + 1);
|
||||
// Insert new value at Data[Idx].
|
||||
memmove(Data + Idx + 1, Data + Idx, Size - Idx);
|
||||
Data[Idx] = RandCh(Rand);
|
||||
return Size + 1;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
const size_t kMinBytesToInsert = 3;
|
||||
if (Size + kMinBytesToInsert >= MaxSize) return 0;
|
||||
size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128);
|
||||
size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert;
|
||||
assert(Size + N <= MaxSize && N);
|
||||
size_t Idx = Rand(Size + 1);
|
||||
// Insert new values at Data[Idx].
|
||||
memmove(Data + Idx + N, Data + Idx, Size - Idx);
|
||||
// Give preference to 0x00 and 0xff.
|
||||
uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255);
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Data[Idx + i] = Byte;
|
||||
return Size + N;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t Idx = Rand(Size);
|
||||
Data[Idx] = RandCh(Rand);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t Idx = Rand(Size);
|
||||
Data[Idx] ^= 1 << Rand(8);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return AddWordFromDictionary(TempAutoDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
DictionaryEntry &DE) {
|
||||
const Word &W = DE.GetW();
|
||||
bool UsePositionHint = DE.HasPositionHint() &&
|
||||
DE.GetPositionHint() + W.size() < Size &&
|
||||
Rand.RandBool();
|
||||
if (Rand.RandBool()) { // Insert W.
|
||||
if (Size + W.size() > MaxSize) return 0;
|
||||
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1);
|
||||
memmove(Data + Idx + W.size(), Data + Idx, Size - Idx);
|
||||
memcpy(Data + Idx, W.data(), W.size());
|
||||
Size += W.size();
|
||||
} else { // Overwrite some bytes with W.
|
||||
if (W.size() > Size) return 0;
|
||||
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size());
|
||||
memcpy(Data + Idx, W.data(), W.size());
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Somewhere in the past we have observed a comparison instructions
|
||||
// with arguments Arg1 Arg2. This function tries to guess a dictionary
|
||||
// entry that will satisfy that comparison.
|
||||
// It first tries to find one of the arguments (possibly swapped) in the
|
||||
// input and if it succeeds it creates a DE with a position hint.
|
||||
// Otherwise it creates a DE with one of the arguments w/o a position hint.
|
||||
template <class T>
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
T Arg1, T Arg2, const uint8_t *Data, size_t Size) {
|
||||
ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
|
||||
bool HandleFirst = Rand.RandBool();
|
||||
T ExistingBytes, DesiredBytes;
|
||||
Word W;
|
||||
const uint8_t *End = Data + Size;
|
||||
for (int Arg = 0; Arg < 2; Arg++) {
|
||||
ExistingBytes = HandleFirst ? Arg1 : Arg2;
|
||||
DesiredBytes = HandleFirst ? Arg2 : Arg1;
|
||||
DesiredBytes += Rand(-1, 1);
|
||||
if (Rand.RandBool()) ExistingBytes = Bswap(ExistingBytes);
|
||||
if (Rand.RandBool()) DesiredBytes = Bswap(DesiredBytes);
|
||||
HandleFirst = !HandleFirst;
|
||||
W.Set(reinterpret_cast<uint8_t*>(&DesiredBytes), sizeof(T));
|
||||
const size_t kMaxNumPositions = 8;
|
||||
size_t Positions[kMaxNumPositions];
|
||||
size_t NumPositions = 0;
|
||||
for (const uint8_t *Cur = Data;
|
||||
Cur < End && NumPositions < kMaxNumPositions; Cur++) {
|
||||
Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &ExistingBytes, sizeof(T));
|
||||
if (!Cur) break;
|
||||
Positions[NumPositions++] = Cur - Data;
|
||||
}
|
||||
if (!NumPositions) break;
|
||||
return DictionaryEntry(W, Positions[Rand(NumPositions)]);
|
||||
}
|
||||
DictionaryEntry DE(W);
|
||||
return DE;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromTORC(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
Word W;
|
||||
DictionaryEntry DE;
|
||||
if (Rand.RandBool()) {
|
||||
auto X = TPC.TORC8.Get(Rand.Rand());
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
} else {
|
||||
auto X = TPC.TORC4.Get(Rand.Rand());
|
||||
if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool())
|
||||
DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data,
|
||||
Size);
|
||||
else
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
}
|
||||
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||
if (!Size) return 0;
|
||||
DictionaryEntry &DERef =
|
||||
CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ %
|
||||
kCmpDictionaryEntriesDequeSize];
|
||||
DERef = DE;
|
||||
CurrentDictionaryEntrySequence.push_back(&DERef);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data,
|
||||
size_t Size, size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
if (D.empty()) return 0;
|
||||
DictionaryEntry &DE = D[Rand(D.size())];
|
||||
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||
if (!Size) return 0;
|
||||
DE.IncUseCount();
|
||||
CurrentDictionaryEntrySequence.push_back(&DE);
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Overwrites part of To[0,ToSize) with a part of From[0,FromSize).
|
||||
// Returns ToSize.
|
||||
size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize,
|
||||
uint8_t *To, size_t ToSize) {
|
||||
// Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize).
|
||||
size_t ToBeg = Rand(ToSize);
|
||||
size_t CopySize = Rand(ToSize - ToBeg) + 1;
|
||||
assert(ToBeg + CopySize <= ToSize);
|
||||
CopySize = std::min(CopySize, FromSize);
|
||||
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||
assert(FromBeg + CopySize <= FromSize);
|
||||
memmove(To + ToBeg, From + FromBeg, CopySize);
|
||||
return ToSize;
|
||||
}
|
||||
|
||||
// Inserts part of From[0,ToSize) into To.
|
||||
// Returns new size of To on success or 0 on failure.
|
||||
size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize,
|
||||
uint8_t *To, size_t ToSize,
|
||||
size_t MaxToSize) {
|
||||
if (ToSize >= MaxToSize) return 0;
|
||||
size_t AvailableSpace = MaxToSize - ToSize;
|
||||
size_t MaxCopySize = std::min(AvailableSpace, FromSize);
|
||||
size_t CopySize = Rand(MaxCopySize) + 1;
|
||||
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||
assert(FromBeg + CopySize <= FromSize);
|
||||
size_t ToInsertPos = Rand(ToSize + 1);
|
||||
assert(ToInsertPos + CopySize <= MaxToSize);
|
||||
size_t TailSize = ToSize - ToInsertPos;
|
||||
if (To == From) {
|
||||
MutateInPlaceHere.resize(MaxToSize);
|
||||
memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize);
|
||||
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||
memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize);
|
||||
} else {
|
||||
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||
memmove(To + ToInsertPos, From + FromBeg, CopySize);
|
||||
}
|
||||
return ToSize + CopySize;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
if (Rand.RandBool())
|
||||
return CopyPartOf(Data, Size, Data, Size);
|
||||
else
|
||||
return InsertPartOf(Data, Size, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t B = Rand(Size);
|
||||
while (B < Size && !isdigit(Data[B])) B++;
|
||||
if (B == Size) return 0;
|
||||
size_t E = B;
|
||||
while (E < Size && isdigit(Data[E])) E++;
|
||||
assert(B < E);
|
||||
// now we have digits in [B, E).
|
||||
// strtol and friends don't accept non-zero-teminated data, parse it manually.
|
||||
uint64_t Val = Data[B] - '0';
|
||||
for (size_t i = B + 1; i < E; i++)
|
||||
Val = Val * 10 + Data[i] - '0';
|
||||
|
||||
// Mutate the integer value.
|
||||
switch(Rand(5)) {
|
||||
case 0: Val++; break;
|
||||
case 1: Val--; break;
|
||||
case 2: Val /= 2; break;
|
||||
case 3: Val *= 2; break;
|
||||
case 4: Val = Rand(Val * Val); break;
|
||||
default: assert(0);
|
||||
}
|
||||
// Just replace the bytes with the new ones, don't bother moving bytes.
|
||||
for (size_t i = B; i < E; i++) {
|
||||
size_t Idx = E + B - i - 1;
|
||||
assert(Idx >= B && Idx < E);
|
||||
Data[Idx] = (Val % 10) + '0';
|
||||
Val /= 10;
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) {
|
||||
if (Size < sizeof(T)) return 0;
|
||||
size_t Off = Rand(Size - sizeof(T) + 1);
|
||||
assert(Off + sizeof(T) <= Size);
|
||||
T Val;
|
||||
if (Off < 64 && !Rand(4)) {
|
||||
Val = Size;
|
||||
if (Rand.RandBool())
|
||||
Val = Bswap(Val);
|
||||
} else {
|
||||
memcpy(&Val, Data + Off, sizeof(Val));
|
||||
T Add = Rand(21);
|
||||
Add -= 10;
|
||||
if (Rand.RandBool())
|
||||
Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes.
|
||||
else
|
||||
Val = Val + Add; // Add assuming current endiannes.
|
||||
if (Add == 0 || Rand.RandBool()) // Maybe negate.
|
||||
Val = -Val;
|
||||
}
|
||||
memcpy(Data + Off, &Val, sizeof(Val));
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
switch (Rand(4)) {
|
||||
case 3: return ChangeBinaryInteger<uint64_t>(Data, Size, Rand);
|
||||
case 2: return ChangeBinaryInteger<uint32_t>(Data, Size, Rand);
|
||||
case 1: return ChangeBinaryInteger<uint16_t>(Data, Size, Rand);
|
||||
case 0: return ChangeBinaryInteger<uint8_t>(Data, Size, Rand);
|
||||
default: assert(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
if (!Corpus || Corpus->size() < 2 || Size == 0) return 0;
|
||||
size_t Idx = Rand(Corpus->size());
|
||||
const Unit &O = (*Corpus)[Idx];
|
||||
if (O.empty()) return 0;
|
||||
MutateInPlaceHere.resize(MaxSize);
|
||||
auto &U = MutateInPlaceHere;
|
||||
size_t NewSize = 0;
|
||||
switch(Rand(3)) {
|
||||
case 0:
|
||||
NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size());
|
||||
break;
|
||||
case 1:
|
||||
NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize);
|
||||
if (NewSize)
|
||||
break;
|
||||
// LLVM_FALLTHROUGH;
|
||||
case 2:
|
||||
NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
assert(NewSize > 0 && "CrossOver returned empty unit");
|
||||
assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
|
||||
memcpy(Data, U.data(), NewSize);
|
||||
return NewSize;
|
||||
}
|
||||
|
||||
void MutationDispatcher::StartMutationSequence() {
|
||||
CurrentMutatorSequence.clear();
|
||||
CurrentDictionaryEntrySequence.clear();
|
||||
}
|
||||
|
||||
// Copy successful dictionary entries to PersistentAutoDictionary.
|
||||
void MutationDispatcher::RecordSuccessfulMutationSequence() {
|
||||
for (auto DE : CurrentDictionaryEntrySequence) {
|
||||
// PersistentAutoDictionary.AddWithSuccessCountOne(DE);
|
||||
DE->IncSuccessCount();
|
||||
// Linear search is fine here as this happens seldom.
|
||||
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
|
||||
PersistentAutoDictionary.push_back({DE->GetW(), 1});
|
||||
}
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintRecommendedDictionary() {
|
||||
std::vector<DictionaryEntry> V;
|
||||
for (auto &DE : PersistentAutoDictionary)
|
||||
if (!ManualDictionary.ContainsWord(DE.GetW()))
|
||||
V.push_back(DE);
|
||||
if (V.empty()) return;
|
||||
Printf("###### Recommended dictionary. ######\n");
|
||||
for (auto &DE: V) {
|
||||
Printf("\"");
|
||||
PrintASCII(DE.GetW(), "\"");
|
||||
Printf(" # Uses: %zd\n", DE.GetUseCount());
|
||||
}
|
||||
Printf("###### End of recommended dictionary. ######\n");
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintMutationSequence() {
|
||||
Printf("MS: %zd ", CurrentMutatorSequence.size());
|
||||
for (auto M : CurrentMutatorSequence)
|
||||
Printf("%s-", M.Name);
|
||||
if (!CurrentDictionaryEntrySequence.empty()) {
|
||||
Printf(" DE: ");
|
||||
for (auto DE : CurrentDictionaryEntrySequence) {
|
||||
Printf("\"");
|
||||
PrintASCII(DE->GetW(), "\"-");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return MutateImpl(Data, Size, MaxSize, Mutators);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
return MutateImpl(Data, Size, MaxSize, DefaultMutators);
|
||||
}
|
||||
|
||||
// Mutates Data in place, returns new size.
|
||||
size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
const std::vector<Mutator> &Mutators) {
|
||||
assert(MaxSize > 0);
|
||||
if (Size == 0) {
|
||||
for (size_t i = 0; i < MaxSize; i++)
|
||||
Data[i] = RandCh(Rand);
|
||||
if (Options.OnlyASCII)
|
||||
ToASCII(Data, MaxSize);
|
||||
return MaxSize;
|
||||
}
|
||||
assert(Size > 0);
|
||||
// Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
|
||||
// in which case they will return 0.
|
||||
// Try several times before returning un-mutated data.
|
||||
for (int Iter = 0; Iter < 100; Iter++) {
|
||||
auto M = Mutators[Rand(Mutators.size())];
|
||||
size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
|
||||
if (NewSize && NewSize <= MaxSize) {
|
||||
if (Options.OnlyASCII)
|
||||
ToASCII(Data, NewSize);
|
||||
CurrentMutatorSequence.push_back(M);
|
||||
return NewSize;
|
||||
}
|
||||
}
|
||||
return std::min(Size, MaxSize);
|
||||
}
|
||||
|
||||
void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
|
||||
ManualDictionary.push_back(
|
||||
{W, std::numeric_limits<size_t>::max()});
|
||||
}
|
||||
|
||||
void MutationDispatcher::AddWordToAutoDictionary(DictionaryEntry DE) {
|
||||
static const size_t kMaxAutoDictSize = 1 << 14;
|
||||
if (TempAutoDictionary.size() >= kMaxAutoDictSize) return;
|
||||
TempAutoDictionary.push_back(DE);
|
||||
}
|
||||
|
||||
void MutationDispatcher::ClearAutoDictionary() {
|
||||
TempAutoDictionary.clear();
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
145
test/thirdparty/Fuzzer/FuzzerMutate.h
vendored
Normal file
145
test/thirdparty/Fuzzer/FuzzerMutate.h
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::MutationDispatcher
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MUTATE_H
|
||||
#define LLVM_FUZZER_MUTATE_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerRandom.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
class MutationDispatcher {
|
||||
public:
|
||||
MutationDispatcher(Random &Rand, const FuzzingOptions &Options);
|
||||
~MutationDispatcher() {}
|
||||
/// Indicate that we are about to start a new sequence of mutations.
|
||||
void StartMutationSequence();
|
||||
/// Print the current sequence of mutations.
|
||||
void PrintMutationSequence();
|
||||
/// Indicate that the current sequence of mutations was successfull.
|
||||
void RecordSuccessfulMutationSequence();
|
||||
/// Mutates data by invoking user-provided mutator.
|
||||
size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by invoking user-provided crossover.
|
||||
size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by shuffling bytes.
|
||||
size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by erasing bytes.
|
||||
size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by inserting a byte.
|
||||
size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by inserting several repeated bytes.
|
||||
size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by chanding one byte.
|
||||
size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by chanding one bit.
|
||||
size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by copying/inserting a part of data into a different place.
|
||||
size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the manual dictionary.
|
||||
size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the temporary automatic dictionary.
|
||||
size_t Mutate_AddWordFromTemporaryAutoDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the TORC.
|
||||
size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the persistent automatic dictionary.
|
||||
size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
/// Tries to find an ASCII integer in Data, changes it to another ASCII int.
|
||||
size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways.
|
||||
size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// CrossOver Data with some other element of the corpus.
|
||||
size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Applies one of the configured mutations.
|
||||
/// Returns the new size of data which could be up to MaxSize.
|
||||
size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Applies one of the default mutations. Provided as a service
|
||||
/// to mutation authors.
|
||||
size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Creates a cross-over of two pieces of Data, returns its size.
|
||||
size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
|
||||
size_t Size2, uint8_t *Out, size_t MaxOutSize);
|
||||
|
||||
void AddWordToManualDictionary(const Word &W);
|
||||
|
||||
void AddWordToAutoDictionary(DictionaryEntry DE);
|
||||
void ClearAutoDictionary();
|
||||
void PrintRecommendedDictionary();
|
||||
|
||||
void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; }
|
||||
|
||||
Random &GetRand() { return Rand; }
|
||||
|
||||
private:
|
||||
|
||||
struct Mutator {
|
||||
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
|
||||
const char *Name;
|
||||
};
|
||||
|
||||
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
const std::vector<Mutator> &Mutators);
|
||||
|
||||
size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||
size_t ToSize, size_t MaxToSize);
|
||||
size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||
size_t ToSize);
|
||||
size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
DictionaryEntry &DE);
|
||||
|
||||
template <class T>
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2,
|
||||
const uint8_t *Data, size_t Size);
|
||||
|
||||
Random &Rand;
|
||||
const FuzzingOptions &Options;
|
||||
|
||||
// Dictionary provided by the user via -dict=DICT_FILE.
|
||||
Dictionary ManualDictionary;
|
||||
// Temporary dictionary modified by the fuzzer itself,
|
||||
// recreated periodically.
|
||||
Dictionary TempAutoDictionary;
|
||||
// Persistent dictionary modified by the fuzzer, consists of
|
||||
// entries that led to successfull discoveries in the past mutations.
|
||||
Dictionary PersistentAutoDictionary;
|
||||
|
||||
std::vector<Mutator> CurrentMutatorSequence;
|
||||
std::vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
|
||||
|
||||
static const size_t kCmpDictionaryEntriesDequeSize = 16;
|
||||
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
|
||||
size_t CmpDictionaryEntriesDequeIdx = 0;
|
||||
|
||||
const InputCorpus *Corpus = nullptr;
|
||||
std::vector<uint8_t> MutateInPlaceHere;
|
||||
|
||||
std::vector<Mutator> Mutators;
|
||||
std::vector<Mutator> DefaultMutators;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_MUTATE_H
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue