mirror of
https://github.com/zeek/zeek.git
synced 2025-10-11 19:18:19 +00:00
Add standalone driver for fuzz targets
Useful for cases that don't need to use a fuzzing engine, but just run the fuzz targets over some set of inputs, like for regression/CI tests. Also added a POP3 fuzzer dictionary, seed corpus, and README with examples.
This commit is contained in:
parent
8f1b34b915
commit
78b0b2183d
8 changed files with 168 additions and 8 deletions
|
@ -481,6 +481,9 @@ message(
|
||||||
"\n debugging: ${USE_PERFTOOLS_DEBUG}"
|
"\n debugging: ${USE_PERFTOOLS_DEBUG}"
|
||||||
"\njemalloc: ${ENABLE_JEMALLOC}"
|
"\njemalloc: ${ENABLE_JEMALLOC}"
|
||||||
"\n"
|
"\n"
|
||||||
|
"\nFuzz Targets: ${ZEEK_ENABLE_FUZZING}"
|
||||||
|
"\nFuzz Engine: ${ZEEK_FUZZING_ENGINE}"
|
||||||
|
"\n"
|
||||||
"\n================================================================\n"
|
"\n================================================================\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,17 @@ if ( NOT ZEEK_ENABLE_FUZZING )
|
||||||
return()
|
return()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if ( NOT DEFINED ZEEK_FUZZING_ENGINE AND DEFINED ENV{LIB_FUZZING_ENGINE} )
|
||||||
|
set(ZEEK_FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE} CACHE INTERNAL "" FORCE)
|
||||||
|
endif ()
|
||||||
|
|
||||||
macro(ADD_FUZZ_TARGET _name)
|
macro(ADD_FUZZ_TARGET _name)
|
||||||
set(_fuzz_target zeek_fuzzer_${_name})
|
set(_fuzz_target zeek-${_name}-fuzzer)
|
||||||
set(_fuzz_source ${_name}.cc)
|
set(_fuzz_source ${_name}-fuzzer.cc)
|
||||||
|
|
||||||
add_executable(${_fuzz_target} ${_fuzz_source} ${ARGN}
|
add_executable(${_fuzz_target} ${_fuzz_source} ${ARGN}
|
||||||
$<TARGET_OBJECTS:zeek_objs>
|
$<TARGET_OBJECTS:zeek_objs>
|
||||||
$<TARGET_OBJECTS:zeek_fuzzer_objs>
|
$<TARGET_OBJECTS:zeek_fuzzer_common>
|
||||||
${zeek_HEADERS}
|
${zeek_HEADERS}
|
||||||
${bro_SUBDIR_LIBS}
|
${bro_SUBDIR_LIBS}
|
||||||
${bro_PLUGIN_LIBS})
|
${bro_PLUGIN_LIBS})
|
||||||
|
@ -19,12 +23,17 @@ macro(ADD_FUZZ_TARGET _name)
|
||||||
target_link_libraries(${_fuzz_target} ${zeekdeps}
|
target_link_libraries(${_fuzz_target} ${zeekdeps}
|
||||||
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})
|
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})
|
||||||
|
|
||||||
# TODO: Use LIB_FUZZING_ENGINE env. var. if it exists
|
if ( DEFINED ZEEK_FUZZING_ENGINE )
|
||||||
set_target_properties(${_fuzz_target} PROPERTIES LINK_FLAGS
|
set_target_properties(${_fuzz_target} PROPERTIES LINK_FLAGS
|
||||||
"-fsanitize=fuzzer")
|
${ZEEK_FUZZING_ENGINE})
|
||||||
|
else ()
|
||||||
|
target_link_libraries(${_fuzz_target}
|
||||||
|
$<TARGET_OBJECTS:zeek_fuzzer_standalone>)
|
||||||
|
endif ()
|
||||||
endmacro ()
|
endmacro ()
|
||||||
|
|
||||||
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
add_library(zeek_fuzzer_objs OBJECT FuzzBuffer.cc)
|
add_library(zeek_fuzzer_common OBJECT FuzzBuffer.cc)
|
||||||
|
add_library(zeek_fuzzer_standalone OBJECT standalone-driver.cc)
|
||||||
|
|
||||||
add_fuzz_target(pop3)
|
add_fuzz_target(pop3)
|
||||||
|
|
79
src/fuzzers/README
Normal file
79
src/fuzzers/README
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
Fuzz Testing
|
||||||
|
============
|
||||||
|
|
||||||
|
This directory contains fuzzing targets for various Zeek components. The
|
||||||
|
primary way to use these directly would be with a fuzzing engine such as
|
||||||
|
libFuzzer: https://llvm.org/docs/LibFuzzer.html
|
||||||
|
|
||||||
|
Example Build: Initial Fuzzing and Seed Corpus
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
First configure and build for fuzzing (with libFuzzer) and code coverage::
|
||||||
|
|
||||||
|
$ LIB_FUZZING_ENGINE="-fsanitize=fuzzer" CC=clang CXX=clang++ \
|
||||||
|
CFLAGS="-fprofile-instr-generate -fcoverage-mapping" \
|
||||||
|
CXXFLAGS="-fprofile-instr-generate -fcoverage-mapping" \
|
||||||
|
./configure --build-type=RelWithDebInfo --build-dir=./build-fuzz-cov \
|
||||||
|
--sanitizers=fuzzer-no-link --enable-fuzzing
|
||||||
|
|
||||||
|
$ cd build-fuzz-cov && make -j $(nproc)
|
||||||
|
|
||||||
|
Now start fuzzing to generate an initial corpus (this uses the POP3 fuzzer as
|
||||||
|
an example)::
|
||||||
|
|
||||||
|
$ mkdir corpus && ./src/fuzzers/zeek-pop3-fuzzer corpus \
|
||||||
|
-dict=../src/fuzzers/pop3.dict -max_total_time=300 -fork=$(($(nproc) - 1))
|
||||||
|
|
||||||
|
You can set options, like the runtime and parallelism level, to taste. For
|
||||||
|
other fuzz targets, you'd also want to use a different dictionary or omit
|
||||||
|
entirely.
|
||||||
|
|
||||||
|
To minimize the size of the corpus::
|
||||||
|
|
||||||
|
$ mkdir min-corpus && ./src/fuzzers/zeek-pop3-fuzzer -merge=1 min-corpus corpus
|
||||||
|
|
||||||
|
To check the code coverage of the corpus::
|
||||||
|
|
||||||
|
$ ./src/fuzzers/zeek-pop3-fuzzer min-corpus/*
|
||||||
|
|
||||||
|
$ llvm-profdata merge -sparse default.profraw -o zeek.profdata && \
|
||||||
|
llvm-cov report ./src/fuzzers/zeek-pop3-fuzzer -instr-profile=zeek.profdata \
|
||||||
|
../src/analyzer/protocol/pop3/
|
||||||
|
|
||||||
|
If the code coverage isn't satisfying, there may be something wrong with
|
||||||
|
the fuzzer, it may need a better dictionary, or it may need to fuzz for longer.
|
||||||
|
|
||||||
|
The corpus can be added to revision control for use in regression testing and
|
||||||
|
as seed for OSS-Fuzz (check first that the zip file is a size that's sane to
|
||||||
|
commit)::
|
||||||
|
|
||||||
|
zip -j ../src/fuzzers/pop3-corpus.zip min-corpus/*
|
||||||
|
|
||||||
|
Example Build: Run Standalone Fuzz Targets
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
Fuzz targets can still be run without a fuzzing engine driving them. In
|
||||||
|
standalone mode, they'll process all input files provided as arguments
|
||||||
|
(e.g. useful for regression testing).
|
||||||
|
|
||||||
|
First configure and build::
|
||||||
|
|
||||||
|
$ CC=clang CXX=clang++ \
|
||||||
|
./configure --build-type=debug --build-dir=./build-fuzz-check \
|
||||||
|
--sanitizers=address,fuzzer-no-link --enable-fuzzing
|
||||||
|
|
||||||
|
$ cd build-fuzz-cov && make -j $(nproc)
|
||||||
|
|
||||||
|
Get a set of inputs to process (we're using the POP3 fuzzer/corpus as example)::
|
||||||
|
|
||||||
|
$ mkdir corpus && ( cd corpus && unzip ../../src/fuzzers/pop3-corpus.zip )
|
||||||
|
|
||||||
|
Now run the standalone fuzzer on the input corpus::
|
||||||
|
|
||||||
|
$ ./src/fuzzers/zeek-pop3-fuzzer corpus/*
|
||||||
|
|
||||||
|
Note that you can also configure this build for coverage reports to verify the
|
||||||
|
code coverage (see the CFLAGS/CXXFLAGS from the first "Initial Fuzzing"
|
||||||
|
section). There's also the following ASan option which may need to be used::
|
||||||
|
|
||||||
|
$ export ASAN_OPTIONS=detect_odr_violation=0
|
|
@ -16,6 +16,7 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
|
||||||
options.script_options_to_set.emplace_back("Site::local_nets={10.0.0.0/8}");
|
options.script_options_to_set.emplace_back("Site::local_nets={10.0.0.0/8}");
|
||||||
options.script_options_to_set.emplace_back("Log::default_writer=Log::WRITER_NONE");
|
options.script_options_to_set.emplace_back("Log::default_writer=Log::WRITER_NONE");
|
||||||
options.deterministic_mode = true;
|
options.deterministic_mode = true;
|
||||||
|
options.ignore_checksums = true;
|
||||||
options.abort_on_scripting_errors = true;
|
options.abort_on_scripting_errors = true;
|
||||||
|
|
||||||
if ( zeek::setup(*argc, *argv, &options).code )
|
if ( zeek::setup(*argc, *argv, &options).code )
|
BIN
src/fuzzers/pop3-corpus.zip
Normal file
BIN
src/fuzzers/pop3-corpus.zip
Normal file
Binary file not shown.
|
@ -9,7 +9,7 @@
|
||||||
#include "analyzer/protocol/tcp/TCP.h"
|
#include "analyzer/protocol/tcp/TCP.h"
|
||||||
|
|
||||||
#include "FuzzBuffer.h"
|
#include "FuzzBuffer.h"
|
||||||
#include "fuzz-setup.h"
|
#include "fuzzer-setup.h"
|
||||||
|
|
||||||
static constexpr auto ZEEK_FUZZ_ANALYZER = "pop3";
|
static constexpr auto ZEEK_FUZZ_ANALYZER = "pop3";
|
||||||
|
|
21
src/fuzzers/pop3.dict
Normal file
21
src/fuzzers/pop3.dict
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
"\x01PKT"
|
||||||
|
"OK"
|
||||||
|
"ERR"
|
||||||
|
"USER"
|
||||||
|
"PASS"
|
||||||
|
"APOP"
|
||||||
|
"AUTH"
|
||||||
|
"STAT"
|
||||||
|
"LIST"
|
||||||
|
"RETR"
|
||||||
|
"DELE"
|
||||||
|
"RSET"
|
||||||
|
"NOOP"
|
||||||
|
"LAST"
|
||||||
|
"QUIT"
|
||||||
|
"TOP"
|
||||||
|
"CAPA"
|
||||||
|
"UIDL"
|
||||||
|
"STLS"
|
||||||
|
"XSENDER"
|
||||||
|
"END"
|
47
src/fuzzers/standalone-driver.cc
Normal file
47
src/fuzzers/standalone-driver.cc
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
|
||||||
|
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv);
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto agg_start = high_resolution_clock::now();
|
||||||
|
auto num_inputs = argc - 1;
|
||||||
|
printf("Standalone fuzzer processing %d inputs\n", num_inputs);
|
||||||
|
|
||||||
|
LLVMFuzzerInitialize(&argc, &argv);
|
||||||
|
|
||||||
|
for ( auto i = 0; i < num_inputs; ++i )
|
||||||
|
{
|
||||||
|
auto input_file_name = argv[i + 1];
|
||||||
|
printf(" %s:", input_file_name);
|
||||||
|
|
||||||
|
auto f = fopen(input_file_name, "r");
|
||||||
|
assert(f);
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
auto input_length = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
auto input_buffer = std::make_unique<uint8_t[]>(input_length);
|
||||||
|
auto bytes_read = fread(input_buffer.get(), 1, input_length, f);
|
||||||
|
assert(bytes_read == input_length);
|
||||||
|
|
||||||
|
auto start = high_resolution_clock::now();
|
||||||
|
LLVMFuzzerTestOneInput(input_buffer.get(), input_length);
|
||||||
|
auto stop = high_resolution_clock::now();
|
||||||
|
auto dt = duration<double>(stop - start).count();
|
||||||
|
|
||||||
|
printf(" %6zu bytes, %f seconds\n", input_length, dt);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto agg_stop = high_resolution_clock::now();
|
||||||
|
auto agg_dt = duration<double>(agg_stop - agg_start).count();
|
||||||
|
printf("Processed %d inputs in %fs\n", num_inputs, agg_dt);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue