summaryrefslogtreecommitdiff
path: root/lib/fuzzer
diff options
context:
space:
mode:
Diffstat (limited to 'lib/fuzzer')
-rw-r--r--lib/fuzzer/CMakeLists.txt41
-rw-r--r--lib/fuzzer/FuzzerBuiltins.h7
-rw-r--r--lib/fuzzer/FuzzerBuiltinsMsvc.h9
-rw-r--r--lib/fuzzer/FuzzerCommand.h7
-rw-r--r--lib/fuzzer/FuzzerCorpus.h17
-rw-r--r--lib/fuzzer/FuzzerCrossOver.cpp7
-rw-r--r--lib/fuzzer/FuzzerDataFlowTrace.cpp7
-rw-r--r--lib/fuzzer/FuzzerDataFlowTrace.h7
-rw-r--r--lib/fuzzer/FuzzerDefs.h41
-rw-r--r--lib/fuzzer/FuzzerDictionary.h7
-rw-r--r--lib/fuzzer/FuzzerDriver.cpp123
-rw-r--r--lib/fuzzer/FuzzerExtFunctions.def11
-rw-r--r--lib/fuzzer/FuzzerExtFunctions.h7
-rw-r--r--lib/fuzzer/FuzzerExtFunctionsDlsym.cpp7
-rw-r--r--lib/fuzzer/FuzzerExtFunctionsWeak.cpp7
-rw-r--r--lib/fuzzer/FuzzerExtFunctionsWindows.cpp9
-rw-r--r--lib/fuzzer/FuzzerExtraCounters.cpp7
-rw-r--r--lib/fuzzer/FuzzerFlags.def37
-rw-r--r--lib/fuzzer/FuzzerFork.cpp360
-rw-r--r--lib/fuzzer/FuzzerFork.h24
-rw-r--r--lib/fuzzer/FuzzerIO.cpp44
-rw-r--r--lib/fuzzer/FuzzerIO.h29
-rw-r--r--lib/fuzzer/FuzzerIOPosix.cpp47
-rw-r--r--lib/fuzzer/FuzzerIOWindows.cpp94
-rw-r--r--lib/fuzzer/FuzzerInterface.h28
-rw-r--r--lib/fuzzer/FuzzerInternal.h23
-rw-r--r--lib/fuzzer/FuzzerLoop.cpp135
-rw-r--r--lib/fuzzer/FuzzerMain.cpp9
-rw-r--r--lib/fuzzer/FuzzerMerge.cpp223
-rw-r--r--lib/fuzzer/FuzzerMerge.h28
-rw-r--r--lib/fuzzer/FuzzerMutate.cpp30
-rw-r--r--lib/fuzzer/FuzzerMutate.h13
-rw-r--r--lib/fuzzer/FuzzerOptions.h16
-rw-r--r--lib/fuzzer/FuzzerRandom.h18
-rw-r--r--lib/fuzzer/FuzzerSHA1.cpp10
-rw-r--r--lib/fuzzer/FuzzerSHA1.h7
-rw-r--r--lib/fuzzer/FuzzerShmem.h69
-rw-r--r--lib/fuzzer/FuzzerShmemFuchsia.cpp38
-rw-r--r--lib/fuzzer/FuzzerShmemPosix.cpp108
-rw-r--r--lib/fuzzer/FuzzerShmemWindows.cpp64
-rw-r--r--lib/fuzzer/FuzzerTracePC.cpp334
-rw-r--r--lib/fuzzer/FuzzerTracePC.h102
-rw-r--r--lib/fuzzer/FuzzerUtil.cpp7
-rw-r--r--lib/fuzzer/FuzzerUtil.h23
-rw-r--r--lib/fuzzer/FuzzerUtilDarwin.cpp7
-rw-r--r--lib/fuzzer/FuzzerUtilFuchsia.cpp11
-rw-r--r--lib/fuzzer/FuzzerUtilLinux.cpp15
-rw-r--r--lib/fuzzer/FuzzerUtilPosix.cpp34
-rw-r--r--lib/fuzzer/FuzzerUtilWindows.cpp17
-rw-r--r--lib/fuzzer/FuzzerValueBitMap.h13
-rw-r--r--lib/fuzzer/afl/afl_driver.cpp250
-rw-r--r--lib/fuzzer/dataflow/DataFlow.cpp32
-rwxr-xr-xlib/fuzzer/scripts/collect_data_flow.py23
-rwxr-xr-xlib/fuzzer/scripts/merge_data_flow.py17
-rwxr-xr-xlib/fuzzer/scripts/unbalanced_allocs.py7
-rw-r--r--lib/fuzzer/standalone/StandaloneFuzzTargetMain.c8
-rw-r--r--lib/fuzzer/tests/CMakeLists.txt22
-rw-r--r--lib/fuzzer/tests/FuzzerUnittest.cpp68
58 files changed, 1530 insertions, 1235 deletions
diff --git a/lib/fuzzer/CMakeLists.txt b/lib/fuzzer/CMakeLists.txt
index caea9734f..852019dc1 100644
--- a/lib/fuzzer/CMakeLists.txt
+++ b/lib/fuzzer/CMakeLists.txt
@@ -6,6 +6,7 @@ set(LIBFUZZER_SOURCES
FuzzerExtFunctionsWeak.cpp
FuzzerExtFunctionsWindows.cpp
FuzzerExtraCounters.cpp
+ FuzzerFork.cpp
FuzzerIO.cpp
FuzzerIOPosix.cpp
FuzzerIOWindows.cpp
@@ -13,9 +14,6 @@ set(LIBFUZZER_SOURCES
FuzzerMerge.cpp
FuzzerMutate.cpp
FuzzerSHA1.cpp
- FuzzerShmemFuchsia.cpp
- FuzzerShmemPosix.cpp
- FuzzerShmemWindows.cpp
FuzzerTracePC.cpp
FuzzerUtil.cpp
FuzzerUtilDarwin.cpp
@@ -35,6 +33,7 @@ set(LIBFUZZER_HEADERS
FuzzerExtFunctions.def
FuzzerExtFunctions.h
FuzzerFlags.def
+ FuzzerFork.h
FuzzerIO.h
FuzzerInterface.h
FuzzerInternal.h
@@ -43,7 +42,6 @@ set(LIBFUZZER_HEADERS
FuzzerOptions.h
FuzzerRandom.h
FuzzerSHA1.h
- FuzzerShmem.h
FuzzerTracePC.h
FuzzerUtil.h
FuzzerValueBitMap.h)
@@ -57,7 +55,9 @@ CHECK_CXX_SOURCE_COMPILES("
set(LIBFUZZER_CFLAGS ${SANITIZER_COMMON_CFLAGS})
-if(OS_NAME MATCHES "Linux|Fuchsia" AND COMPILER_RT_LIBCXX_PATH)
+if(OS_NAME MATCHES "Linux|Fuchsia" AND
+ COMPILER_RT_LIBCXX_PATH AND
+ COMPILER_RT_LIBCXXABI_PATH)
list(APPEND LIBFUZZER_CFLAGS -nostdinc++ -D_LIBCPP_ABI_VERSION=Fuzzer)
# Remove -stdlib= which is unused when passing -nostdinc++.
string(REGEX REPLACE "-stdlib=[a-zA-Z+]*" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
@@ -71,12 +71,21 @@ if (CMAKE_CXX_FLAGS MATCHES "fsanitize-coverage")
list(APPEND LIBFUZZER_CFLAGS -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters)
endif()
-if(NOT HAS_THREAD_LOCAL)
- list(APPEND LIBFUZZER_CFLAGS -Dthread_local=__thread)
+if(MSVC)
+ # Silence warnings by turning off exceptions in MSVC headers and avoid an
+ # error by unecessarily defining thread_local when it isn't even used on
+ # Windows.
+ list(APPEND LIBFUZZER_CFLAGS -D_HAS_EXCEPTIONS=0)
+else()
+ if(NOT HAS_THREAD_LOCAL)
+ list(APPEND LIBFUZZER_CFLAGS -Dthread_local=__thread)
+ endif()
endif()
set(FUZZER_SUPPORTED_OS ${SANITIZER_COMMON_SUPPORTED_OS})
+add_compiler_rt_component(fuzzer)
+
add_compiler_rt_object_libraries(RTfuzzer
OS ${FUZZER_SUPPORTED_OS}
ARCHS ${FUZZER_SUPPORTED_ARCH}
@@ -108,12 +117,19 @@ add_compiler_rt_runtime(clang_rt.fuzzer_no_main
CFLAGS ${LIBFUZZER_CFLAGS}
PARENT_TARGET fuzzer)
-if(OS_NAME MATCHES "Linux|Fuchsia" AND COMPILER_RT_LIBCXX_PATH)
+if(OS_NAME MATCHES "Linux|Fuchsia" AND
+ COMPILER_RT_LIBCXX_PATH AND
+ COMPILER_RT_LIBCXXABI_PATH)
macro(partially_link_libcxx name dir arch)
+ if(${arch} MATCHES "i386")
+ set(EMULATION_ARGUMENT "-m" "elf_i386")
+ else()
+ set(EMULATION_ARGUMENT "")
+ endif()
set(cxx_${arch}_merge_dir "${CMAKE_CURRENT_BINARY_DIR}/cxx_${arch}_merge.dir")
file(MAKE_DIRECTORY ${cxx_${arch}_merge_dir})
add_custom_command(TARGET clang_rt.${name}-${arch} POST_BUILD
- COMMAND ${CMAKE_LINKER} --whole-archive "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>" --no-whole-archive ${dir}/lib/libc++.a -r -o ${name}.o
+ COMMAND ${CMAKE_LINKER} ${EMULATION_ARGUMENT} --whole-archive "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>" --no-whole-archive ${dir}/lib/libc++.a -r -o ${name}.o
COMMAND ${CMAKE_OBJCOPY} --localize-hidden ${name}.o
COMMAND ${CMAKE_COMMAND} -E remove "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>"
COMMAND ${CMAKE_AR} qcs "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>" ${name}.o
@@ -126,13 +142,8 @@ if(OS_NAME MATCHES "Linux|Fuchsia" AND COMPILER_RT_LIBCXX_PATH)
set(LIBCXX_${arch}_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_fuzzer_${arch})
add_custom_libcxx(libcxx_fuzzer_${arch} ${LIBCXX_${arch}_PREFIX}
CFLAGS ${TARGET_CFLAGS}
- -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS=1
- -fvisibility=hidden
CMAKE_ARGS -DCMAKE_CXX_COMPILER_WORKS=ON
- -DLIBCXX_ENABLE_EXCEPTIONS=OFF
- -DLIBCXX_ENABLE_SHARED=OFF
- -DLIBCXX_ABI_NAMESPACE=Fuzzer
- -DLIBCXX_CXX_ABI=none)
+ -DLIBCXX_ABI_NAMESPACE=Fuzzer)
target_compile_options(RTfuzzer.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1)
add_dependencies(RTfuzzer.${arch} libcxx_fuzzer_${arch}-build)
target_compile_options(RTfuzzer_main.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1)
diff --git a/lib/fuzzer/FuzzerBuiltins.h b/lib/fuzzer/FuzzerBuiltins.h
index a80938d9a..5f1ccef8a 100644
--- a/lib/fuzzer/FuzzerBuiltins.h
+++ b/lib/fuzzer/FuzzerBuiltins.h
@@ -1,9 +1,8 @@
//===- FuzzerBuiltins.h - Internal header for builtins ----------*- C++ -* ===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Wrapper functions and marcos around builtin functions.
diff --git a/lib/fuzzer/FuzzerBuiltinsMsvc.h b/lib/fuzzer/FuzzerBuiltinsMsvc.h
index 67dd57ff9..82709cfe7 100644
--- a/lib/fuzzer/FuzzerBuiltinsMsvc.h
+++ b/lib/fuzzer/FuzzerBuiltinsMsvc.h
@@ -1,9 +1,8 @@
//===- FuzzerBuiltinsMSVC.h - Internal header for builtins ------*- C++ -* ===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Wrapper functions and marcos that use intrinsics instead of builtin functions
@@ -25,7 +24,7 @@
// __builtin_return_address() cannot be compiled with MSVC. Use the equivalent
// from <intrin.h>
-#define GET_CALLER_PC() reinterpret_cast<uintptr_t>(_ReturnAddress())
+#define GET_CALLER_PC() _ReturnAddress()
namespace fuzzer {
diff --git a/lib/fuzzer/FuzzerCommand.h b/lib/fuzzer/FuzzerCommand.h
index 9d258a228..87308864a 100644
--- a/lib/fuzzer/FuzzerCommand.h
+++ b/lib/fuzzer/FuzzerCommand.h
@@ -1,9 +1,8 @@
//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// FuzzerCommand represents a command to run in a subprocess. It allows callers
diff --git a/lib/fuzzer/FuzzerCorpus.h b/lib/fuzzer/FuzzerCorpus.h
index f844c07c7..6a95ef3a8 100644
--- a/lib/fuzzer/FuzzerCorpus.h
+++ b/lib/fuzzer/FuzzerCorpus.h
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::InputCorpus
@@ -86,9 +85,10 @@ class InputCorpus {
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,
- bool HasFocusFunction, const Vector<uint32_t> &FeatureSet,
- const DataFlowTrace &DFT, const InputInfo *BaseII) {
+ InputInfo *AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile,
+ bool HasFocusFunction,
+ const Vector<uint32_t> &FeatureSet,
+ const DataFlowTrace &DFT, const InputInfo *BaseII) {
assert(!U.empty());
if (FeatureDebug)
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
@@ -114,6 +114,7 @@ class InputCorpus {
UpdateCorpusDistribution();
PrintCorpus();
// ValidateFeatureSet();
+ return &II;
}
// Debug-only
@@ -170,7 +171,7 @@ class InputCorpus {
InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)];
assert(!II.U.empty());
return II;
- };
+ }
// Returns an index of random unit from the corpus to mutate.
size_t ChooseUnitIdxToMutate(Random &Rand) {
diff --git a/lib/fuzzer/FuzzerCrossOver.cpp b/lib/fuzzer/FuzzerCrossOver.cpp
index 8b0fd7d52..83d9f8d47 100644
--- a/lib/fuzzer/FuzzerCrossOver.cpp
+++ b/lib/fuzzer/FuzzerCrossOver.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Cross over test inputs.
diff --git a/lib/fuzzer/FuzzerDataFlowTrace.cpp b/lib/fuzzer/FuzzerDataFlowTrace.cpp
index 764f3e49f..5ae7510e3 100644
--- a/lib/fuzzer/FuzzerDataFlowTrace.cpp
+++ b/lib/fuzzer/FuzzerDataFlowTrace.cpp
@@ -1,9 +1,8 @@
//===- FuzzerDataFlowTrace.cpp - DataFlowTrace ---*- C++ -* ===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::DataFlowTrace
diff --git a/lib/fuzzer/FuzzerDataFlowTrace.h b/lib/fuzzer/FuzzerDataFlowTrace.h
index ad4faeab7..9523a01e1 100644
--- a/lib/fuzzer/FuzzerDataFlowTrace.h
+++ b/lib/fuzzer/FuzzerDataFlowTrace.h
@@ -1,9 +1,8 @@
//===- FuzzerDataFlowTrace.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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::DataFlowTrace; reads and handles a data-flow trace.
diff --git a/lib/fuzzer/FuzzerDefs.h b/lib/fuzzer/FuzzerDefs.h
index c3dccbcd8..320b37d5f 100644
--- a/lib/fuzzer/FuzzerDefs.h
+++ b/lib/fuzzer/FuzzerDefs.h
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Basic definitions.
@@ -120,31 +119,39 @@
# define ALWAYS_INLINE
#endif // __clang__
-#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
-
-#if defined(__has_feature)
-# if __has_feature(address_sanitizer)
-# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS
-# elif __has_feature(memory_sanitizer)
-# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY
-# else
-# define ATTRIBUTE_NO_SANITIZE_ALL
-# endif
+#if LIBFUZZER_WINDOWS
+#define ATTRIBUTE_NO_SANITIZE_ADDRESS
#else
-# define ATTRIBUTE_NO_SANITIZE_ALL
+#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
#endif
#if LIBFUZZER_WINDOWS
+#define ATTRIBUTE_ALIGNED(X) __declspec(align(X))
#define ATTRIBUTE_INTERFACE __declspec(dllexport)
// This is used for __sancov_lowest_stack which is needed for
// -fsanitize-coverage=stack-depth. That feature is not yet available on
// Windows, so make the symbol static to avoid linking errors.
-#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
- __attribute__((tls_model("initial-exec"))) thread_local static
+#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC static
+#define ATTRIBUTE_NOINLINE __declspec(noinline)
#else
+#define ATTRIBUTE_ALIGNED(X) __attribute__((aligned(X)))
#define ATTRIBUTE_INTERFACE __attribute__((visibility("default")))
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local
+
+#define ATTRIBUTE_NOINLINE __attribute__((noinline))
+#endif
+
+#if defined(__has_feature)
+# if __has_feature(address_sanitizer)
+# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS
+# elif __has_feature(memory_sanitizer)
+# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY
+# else
+# define ATTRIBUTE_NO_SANITIZE_ALL
+# endif
+#else
+# define ATTRIBUTE_NO_SANITIZE_ALL
#endif
namespace fuzzer {
diff --git a/lib/fuzzer/FuzzerDictionary.h b/lib/fuzzer/FuzzerDictionary.h
index 0d9d91bcd..301c5d9af 100644
--- a/lib/fuzzer/FuzzerDictionary.h
+++ b/lib/fuzzer/FuzzerDictionary.h
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::Dictionary
diff --git a/lib/fuzzer/FuzzerDriver.cpp b/lib/fuzzer/FuzzerDriver.cpp
index ff2a639ac..b9c892747 100644
--- a/lib/fuzzer/FuzzerDriver.cpp
+++ b/lib/fuzzer/FuzzerDriver.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// FuzzerDriver and flag parsing.
@@ -11,12 +10,13 @@
#include "FuzzerCommand.h"
#include "FuzzerCorpus.h"
+#include "FuzzerFork.h"
#include "FuzzerIO.h"
#include "FuzzerInterface.h"
#include "FuzzerInternal.h"
+#include "FuzzerMerge.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
-#include "FuzzerShmem.h"
#include "FuzzerTracePC.h"
#include <algorithm>
#include <atomic>
@@ -26,10 +26,16 @@
#include <mutex>
#include <string>
#include <thread>
+#include <fstream>
// This function should be present in the libFuzzer so that the client
// binary can test for its existence.
+#if LIBFUZZER_MSVC
+extern "C" void __libfuzzer_is_present() {}
+#pragma comment(linker, "/include:__libfuzzer_is_present")
+#else
extern "C" __attribute__((used)) void __libfuzzer_is_present() {}
+#endif // LIBFUZZER_MSVC
namespace fuzzer {
@@ -316,10 +322,8 @@ int CleanseCrashInput(const Vector<std::string> &Args,
assert(Cmd.hasArgument(InputFilePath));
Cmd.removeArgument(InputFilePath);
- auto LogFilePath = DirPlusFile(
- TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
- auto TmpFilePath = DirPlusFile(
- TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".repro");
+ auto LogFilePath = TempPath(".txt");
+ auto TmpFilePath = TempPath(".repro");
Cmd.addArgument(TmpFilePath);
Cmd.setOutputFile(LogFilePath);
Cmd.combineOutAndErr();
@@ -379,8 +383,7 @@ int MinimizeCrashInput(const Vector<std::string> &Args,
BaseCmd.addFlag("max_total_time", "600");
}
- auto LogFilePath = DirPlusFile(
- TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
+ auto LogFilePath = TempPath(".txt");
BaseCmd.setOutputFile(LogFilePath);
BaseCmd.combineOutAndErr();
@@ -464,6 +467,34 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
return 0;
}
+void Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args,
+ const Vector<std::string> &Corpora, const char *CFPathOrNull) {
+ if (Corpora.size() < 2) {
+ Printf("INFO: Merge requires two or more corpus dirs\n");
+ exit(0);
+ }
+
+ Vector<SizedFile> OldCorpus, NewCorpus;
+ GetSizedFilesFromDir(Corpora[0], &OldCorpus);
+ for (size_t i = 1; i < Corpora.size(); i++)
+ GetSizedFilesFromDir(Corpora[i], &NewCorpus);
+ std::sort(OldCorpus.begin(), OldCorpus.end());
+ std::sort(NewCorpus.begin(), NewCorpus.end());
+
+ std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath(".txt");
+ Vector<std::string> NewFiles;
+ Set<uint32_t> NewFeatures, NewCov;
+ CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures,
+ {}, &NewCov, CFPath, true);
+ for (auto &Path : NewFiles)
+ F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen));
+ // We are done, delete the control file if it was a temporary one.
+ if (!Flags.merge_control_file)
+ RemoveFile(CFPath);
+
+ exit(0);
+}
+
int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict,
UnitVector& Corpus) {
Printf("Started dictionary minimization (up to %d tests)\n",
@@ -573,6 +604,9 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Options.UnitTimeoutSec = Flags.timeout;
Options.ErrorExitCode = Flags.error_exitcode;
Options.TimeoutExitCode = Flags.timeout_exitcode;
+ Options.IgnoreTimeouts = Flags.ignore_timeouts;
+ Options.IgnoreOOMs = Flags.ignore_ooms;
+ Options.IgnoreCrashes = Flags.ignore_crashes;
Options.MaxTotalTimeSec = Flags.max_total_time;
Options.DoCrossOver = Flags.cross_over;
Options.MutateDepth = Flags.mutate_depth;
@@ -617,7 +651,6 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
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)
@@ -626,6 +659,9 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Options.FocusFunction = Flags.focus_function;
if (Flags.data_flow_trace)
Options.DataFlowTrace = Flags.data_flow_trace;
+ if (Flags.features_dir)
+ Options.FeaturesDir = Flags.features_dir;
+ Options.LazyCounters = Flags.lazy_counters;
unsigned Seed = Flags.seed;
// Initialize Seed.
@@ -669,34 +705,6 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
if (Flags.cleanse_crash)
return CleanseCrashInput(Args, Options);
-#if 0 // deprecated, to be removed.
- if (auto Name = Flags.run_equivalence_server) {
- SMR.Destroy(Name);
- if (!SMR.Create(Name)) {
- Printf("ERROR: can't create shared memory region\n");
- return 1;
- }
- Printf("INFO: EQUIVALENCE SERVER UP\n");
- while (true) {
- SMR.WaitClient();
- size_t Size = SMR.ReadByteArraySize();
- SMR.WriteByteArray(nullptr, 0);
- const Unit tmp(SMR.GetByteArray(), SMR.GetByteArray() + Size);
- F->ExecuteCallback(tmp.data(), tmp.size());
- SMR.PostServer();
- }
- return 0;
- }
-
- if (auto Name = Flags.use_equivalence_server) {
- if (!SMR.Open(Name)) {
- Printf("ERROR: can't open shared memory region\n");
- return 1;
- }
- Printf("INFO: EQUIVALENCE CLIENT UP\n");
- }
-#endif
-
if (DoPlainRun) {
Options.SaveArtifacts = false;
int Runs = std::max(1, Flags.runs);
@@ -719,13 +727,11 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
exit(0);
}
- if (Flags.merge) {
- F->CrashResistantMerge(Args, *Inputs,
- Flags.load_coverage_summary,
- Flags.save_coverage_summary,
- Flags.merge_control_file);
- exit(0);
- }
+ if (Flags.fork)
+ FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork);
+
+ if (Flags.merge)
+ Merge(F, Options, Args, *Inputs, Flags.merge_control_file);
if (Flags.merge_inner) {
const size_t kDefaultMaxMergeLen = 1 << 20;
@@ -757,7 +763,28 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
exit(0);
}
- F->Loop(*Inputs);
+ // Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file
+ Vector<std::string> ExtraSeedFiles;
+ if (Flags.seed_inputs) {
+ std::string SeedInputs;
+ if (Flags.seed_inputs[0] == '@')
+ SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list.
+ else
+ SeedInputs = Flags.seed_inputs; // seed_inputs contains the list.
+ if (SeedInputs.empty()) {
+ Printf("seed_inputs is empty or @file does not exist.\n");
+ exit(1);
+ }
+ // Parse SeedInputs.
+ size_t comma_pos = 0;
+ while ((comma_pos = SeedInputs.find_last_of(',')) != std::string::npos) {
+ ExtraSeedFiles.push_back(SeedInputs.substr(comma_pos + 1));
+ SeedInputs = SeedInputs.substr(0, comma_pos);
+ }
+ ExtraSeedFiles.push_back(SeedInputs);
+ }
+
+ F->Loop(*Inputs, ExtraSeedFiles);
if (Flags.verbosity)
Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(),
diff --git a/lib/fuzzer/FuzzerExtFunctions.def b/lib/fuzzer/FuzzerExtFunctions.def
index 8bfffdde5..288a59ce3 100644
--- a/lib/fuzzer/FuzzerExtFunctions.def
+++ b/lib/fuzzer/FuzzerExtFunctions.def
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This defines the external function pointers that
@@ -29,7 +28,7 @@ EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
EXT_FUNC(__lsan_enable, void, (), false);
EXT_FUNC(__lsan_disable, void, (), false);
EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false);
-EXT_FUNC(__sanitizer_acquire_crash_state, bool, (), true);
+EXT_FUNC(__sanitizer_acquire_crash_state, int, (), true);
EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int,
(void (*malloc_hook)(const volatile void *, size_t),
void (*free_hook)(const volatile void *)),
@@ -44,8 +43,6 @@ EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int,
size_t module_path_len,void **pc_offset), false);
EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true);
EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false);
-EXT_FUNC(__sanitizer_dump_coverage, void, (const uintptr_t *, uintptr_t),
- false);
EXT_FUNC(__msan_scoped_disable_interceptor_checks, void, (), false);
EXT_FUNC(__msan_scoped_enable_interceptor_checks, void, (), false);
EXT_FUNC(__msan_unpoison, void, (const volatile void *, size_t size), false);
diff --git a/lib/fuzzer/FuzzerExtFunctions.h b/lib/fuzzer/FuzzerExtFunctions.h
index 2672a3854..c88aac4e6 100644
--- a/lib/fuzzer/FuzzerExtFunctions.h
+++ b/lib/fuzzer/FuzzerExtFunctions.h
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Defines an interface to (possibly optional) functions.
diff --git a/lib/fuzzer/FuzzerExtFunctionsDlsym.cpp b/lib/fuzzer/FuzzerExtFunctionsDlsym.cpp
index 06bddd5de..dcd713459 100644
--- a/lib/fuzzer/FuzzerExtFunctionsDlsym.cpp
+++ b/lib/fuzzer/FuzzerExtFunctionsDlsym.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Implementation for operating systems that support dlsym(). We only use it on
diff --git a/lib/fuzzer/FuzzerExtFunctionsWeak.cpp b/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
index 6a6ef4932..ea5b87bd5 100644
--- a/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
+++ b/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Implementation for Linux. This relies on the linker's support for weak
diff --git a/lib/fuzzer/FuzzerExtFunctionsWindows.cpp b/lib/fuzzer/FuzzerExtFunctionsWindows.cpp
index b01871439..55efe8f80 100644
--- a/lib/fuzzer/FuzzerExtFunctionsWindows.cpp
+++ b/lib/fuzzer/FuzzerExtFunctionsWindows.cpp
@@ -1,9 +1,8 @@
//=== FuzzerExtWindows.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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Implementation of FuzzerExtFunctions for Windows. Uses alternatename when
@@ -50,7 +49,7 @@ extern "C" {
Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
exit(1); \
} \
- EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG;
+ EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG
#include "FuzzerExtFunctions.def"
diff --git a/lib/fuzzer/FuzzerExtraCounters.cpp b/lib/fuzzer/FuzzerExtraCounters.cpp
index c99cd89be..3f38f4fb7 100644
--- a/lib/fuzzer/FuzzerExtraCounters.cpp
+++ b/lib/fuzzer/FuzzerExtraCounters.cpp
@@ -1,9 +1,8 @@
//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Extra coverage counters defined by user code.
diff --git a/lib/fuzzer/FuzzerFlags.def b/lib/fuzzer/FuzzerFlags.def
index 91281c979..b4ec5f298 100644
--- a/lib/fuzzer/FuzzerFlags.def
+++ b/lib/fuzzer/FuzzerFlags.def
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the
@@ -21,6 +20,9 @@ FUZZER_FLAG_INT(len_control, 100, "Try generating small inputs first, "
"then try larger inputs over time. Specifies the rate at which the length "
"limit is increased (smaller == faster). If 0, immediately try inputs with "
"size up to max_len.")
+FUZZER_FLAG_STRING(seed_inputs, "A comma-separated list of input files "
+ "to use as an additional seed corpus. Alternatively, an \"@\" followed by "
+ "the name of a file containing the comma-seperated list.")
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.")
@@ -35,11 +37,16 @@ FUZZER_FLAG_INT(
"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 "
+FUZZER_FLAG_INT(timeout_exitcode, 70, "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(fork, 0, "Experimental mode where fuzzing happens "
+ "in a subprocess")
+FUZZER_FLAG_INT(ignore_timeouts, 1, "Ignore timeouts in fork mode")
+FUZZER_FLAG_INT(ignore_ooms, 1, "Ignore OOMs in fork mode")
+FUZZER_FLAG_INT(ignore_crashes, 0, "Ignore crashes in fork mode")
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.")
@@ -49,13 +56,6 @@ FUZZER_FLAG_STRING(merge_control_file,
"If a merge process gets killed it tries to leave this file "
"in a state suitable for resuming the merge. "
"By default a temporary file will be used.")
-FUZZER_FLAG_STRING(save_coverage_summary, "Experimental:"
- " save coverage summary to a given file."
- " Used with -merge=1")
-FUZZER_FLAG_STRING(load_coverage_summary, "Experimental:"
- " load coverage summary from a given file."
- " Treat this coverage as belonging to the first corpus. "
- " Used with -merge=1")
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."
@@ -68,6 +68,10 @@ FUZZER_FLAG_INT(cleanse_crash, 0, "If 1, tries to cleanse the provided"
" Use with -exact_artifact_path to specify the output."
)
FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag")
+FUZZER_FLAG_STRING(features_dir, "internal flag. Used to dump feature sets on disk."
+ "Every time a new input is added to the corpus, a corresponding file in the features_dir"
+ " is created containing the unique features of that input."
+ " Features are stored in binary format.")
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
FUZZER_FLAG_INT(use_memmem, 1,
"Use hints from intercepting memmem, strstr, etc")
@@ -107,9 +111,7 @@ 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 as text"
" at exit.")
-FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated."
- " If 1, dump coverage information as a"
- " .sancov file at exit.")
+FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated.")
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.")
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
@@ -120,6 +122,9 @@ FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.")
FUZZER_FLAG_INT(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.")
FUZZER_FLAG_INT(handle_usr1, 1, "If 1, try to intercept SIGUSR1.")
FUZZER_FLAG_INT(handle_usr2, 1, "If 1, try to intercept SIGUSR2.")
+FUZZER_FLAG_INT(lazy_counters, 0, "If 1, a performance optimization is"
+ "enabled for the 8bit inline counters. "
+ "Requires that libFuzzer successfully installs its SEGV handler")
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. stderr of asan.")
@@ -148,8 +153,6 @@ FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed "
FUZZER_FLAG_STRING(focus_function, "Experimental. "
"Fuzzing will focus on inputs that trigger calls to this function")
-FUZZER_DEPRECATED_FLAG(run_equivalence_server)
-FUZZER_DEPRECATED_FLAG(use_equivalence_server)
FUZZER_FLAG_INT(analyze_dict, 0, "Experimental")
FUZZER_DEPRECATED_FLAG(use_clang_coverage)
FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace")
diff --git a/lib/fuzzer/FuzzerFork.cpp b/lib/fuzzer/FuzzerFork.cpp
new file mode 100644
index 000000000..dd16ec1e2
--- /dev/null
+++ b/lib/fuzzer/FuzzerFork.cpp
@@ -0,0 +1,360 @@
+//===- FuzzerFork.cpp - run fuzzing in separate subprocesses --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// Spawn and orchestrate separate fuzzing processes.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerCommand.h"
+#include "FuzzerFork.h"
+#include "FuzzerIO.h"
+#include "FuzzerInternal.h"
+#include "FuzzerMerge.h"
+#include "FuzzerSHA1.h"
+#include "FuzzerTracePC.h"
+#include "FuzzerUtil.h"
+
+#include <atomic>
+#include <chrono>
+#include <fstream>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <sstream>
+#include <thread>
+
+namespace fuzzer {
+
+struct Stats {
+ size_t number_of_executed_units = 0;
+ size_t peak_rss_mb = 0;
+ size_t average_exec_per_sec = 0;
+};
+
+static Stats ParseFinalStatsFromLog(const std::string &LogPath) {
+ std::ifstream In(LogPath);
+ std::string Line;
+ Stats Res;
+ struct {
+ const char *Name;
+ size_t *Var;
+ } NameVarPairs[] = {
+ {"stat::number_of_executed_units:", &Res.number_of_executed_units},
+ {"stat::peak_rss_mb:", &Res.peak_rss_mb},
+ {"stat::average_exec_per_sec:", &Res.average_exec_per_sec},
+ {nullptr, nullptr},
+ };
+ while (std::getline(In, Line, '\n')) {
+ if (Line.find("stat::") != 0) continue;
+ std::istringstream ISS(Line);
+ std::string Name;
+ size_t Val;
+ ISS >> Name >> Val;
+ for (size_t i = 0; NameVarPairs[i].Name; i++)
+ if (Name == NameVarPairs[i].Name)
+ *NameVarPairs[i].Var = Val;
+ }
+ return Res;
+}
+
+struct FuzzJob {
+ // Inputs.
+ Command Cmd;
+ std::string CorpusDir;
+ std::string FeaturesDir;
+ std::string LogPath;
+ std::string SeedListPath;
+ std::string CFPath;
+
+ // Fuzzing Outputs.
+ int ExitCode;
+
+ ~FuzzJob() {
+ RemoveFile(CFPath);
+ RemoveFile(LogPath);
+ RemoveFile(SeedListPath);
+ RmDirRecursive(CorpusDir);
+ RmDirRecursive(FeaturesDir);
+ }
+};
+
+struct GlobalEnv {
+ Vector<std::string> Args;
+ Vector<std::string> CorpusDirs;
+ std::string MainCorpusDir;
+ std::string TempDir;
+ Set<uint32_t> Features, Cov;
+ Vector<std::string> Files;
+ Random *Rand;
+ std::chrono::system_clock::time_point ProcessStartTime;
+ int Verbosity = 0;
+
+ size_t NumTimeouts = 0;
+ size_t NumOOMs = 0;
+ size_t NumCrashes = 0;
+
+
+ size_t NumRuns = 0;
+
+ size_t secondsSinceProcessStartUp() const {
+ return std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now() - ProcessStartTime)
+ .count();
+ }
+
+ FuzzJob *CreateNewJob(size_t JobId) {
+ Command Cmd(Args);
+ Cmd.removeFlag("fork");
+ Cmd.removeFlag("runs");
+ for (auto &C : CorpusDirs) // Remove all corpora from the args.
+ Cmd.removeArgument(C);
+ Cmd.addFlag("reload", "0"); // working in an isolated dir, no reload.
+ Cmd.addFlag("print_final_stats", "1");
+ Cmd.addFlag("print_funcs", "0"); // no need to spend time symbolizing.
+ Cmd.addFlag("max_total_time", std::to_string(std::min((size_t)300, JobId)));
+
+ auto Job = new FuzzJob;
+ std::string Seeds;
+ if (size_t CorpusSubsetSize =
+ std::min(Files.size(), (size_t)sqrt(Files.size() + 2)))
+ for (size_t i = 0; i < CorpusSubsetSize; i++)
+ Seeds += (Seeds.empty() ? "" : ",") +
+ Files[Rand->SkewTowardsLast(Files.size())];
+ if (!Seeds.empty()) {
+ Job->SeedListPath = std::to_string(JobId) + ".seeds";
+ WriteToFile(Seeds, Job->SeedListPath);
+ Cmd.addFlag("seed_inputs", "@" + Job->SeedListPath);
+ }
+ Job->LogPath = DirPlusFile(TempDir, std::to_string(JobId) + ".log");
+ Job->CorpusDir = DirPlusFile(TempDir, "C" + std::to_string(JobId));
+ Job->FeaturesDir = DirPlusFile(TempDir, "F" + std::to_string(JobId));
+ Job->CFPath = DirPlusFile(TempDir, std::to_string(JobId) + ".merge");
+
+
+ Cmd.addArgument(Job->CorpusDir);
+ Cmd.addFlag("features_dir", Job->FeaturesDir);
+
+ for (auto &D : {Job->CorpusDir, Job->FeaturesDir}) {
+ RmDirRecursive(D);
+ MkDir(D);
+ }
+
+ Cmd.setOutputFile(Job->LogPath);
+ Cmd.combineOutAndErr();
+
+ Job->Cmd = Cmd;
+
+ if (Verbosity >= 2)
+ Printf("Job %zd/%p Created: %s\n", JobId, Job,
+ Job->Cmd.toString().c_str());
+ // Start from very short runs and gradually increase them.
+ return Job;
+ }
+
+ void RunOneMergeJob(FuzzJob *Job) {
+ auto Stats = ParseFinalStatsFromLog(Job->LogPath);
+ NumRuns += Stats.number_of_executed_units;
+
+ Vector<SizedFile> TempFiles, MergeCandidates;
+ // Read all newly created inputs and their feature sets.
+ // Choose only those inputs that have new features.
+ GetSizedFilesFromDir(Job->CorpusDir, &TempFiles);
+ std::sort(TempFiles.begin(), TempFiles.end());
+ for (auto &F : TempFiles) {
+ auto FeatureFile = F.File;
+ FeatureFile.replace(0, Job->CorpusDir.size(), Job->FeaturesDir);
+ auto FeatureBytes = FileToVector(FeatureFile, 0, false);
+ assert((FeatureBytes.size() % sizeof(uint32_t)) == 0);
+ Vector<uint32_t> NewFeatures(FeatureBytes.size() / sizeof(uint32_t));
+ memcpy(NewFeatures.data(), FeatureBytes.data(), FeatureBytes.size());
+ for (auto Ft : NewFeatures) {
+ if (!Features.count(Ft)) {
+ MergeCandidates.push_back(F);
+ break;
+ }
+ }
+ }
+ if (MergeCandidates.empty()) return;
+
+ Vector<std::string> FilesToAdd;
+ Set<uint32_t> NewFeatures, NewCov;
+ CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features,
+ &NewFeatures, Cov, &NewCov, Job->CFPath, false);
+ for (auto &Path : FilesToAdd) {
+ auto U = FileToVector(Path);
+ auto NewPath = DirPlusFile(MainCorpusDir, Hash(U));
+ WriteToFile(U, NewPath);
+ Files.push_back(NewPath);
+ }
+ Features.insert(NewFeatures.begin(), NewFeatures.end());
+ Cov.insert(NewCov.begin(), NewCov.end());
+ for (auto Idx : NewCov)
+ if (auto *TE = TPC.PCTableEntryByIdx(Idx))
+ if (TPC.PcIsFuncEntry(TE))
+ PrintPC(" NEW_FUNC: %p %F %L\n", "",
+ TPC.GetNextInstructionPc(TE->PC));
+
+ if (!FilesToAdd.empty() || Job->ExitCode != 0)
+ Printf("#%zd: cov: %zd ft: %zd corp: %zd exec/s %zd "
+ "oom/timeout/crash: %zd/%zd/%zd time: %zds\n", NumRuns,
+ Cov.size(), Features.size(), Files.size(),
+ Stats.average_exec_per_sec,
+ NumOOMs, NumTimeouts, NumCrashes, secondsSinceProcessStartUp());
+ }
+};
+
+struct JobQueue {
+ std::queue<FuzzJob *> Qu;
+ std::mutex Mu;
+
+ void Push(FuzzJob *Job) {
+ std::lock_guard<std::mutex> Lock(Mu);
+ Qu.push(Job);
+ }
+ FuzzJob *Pop() {
+ std::lock_guard<std::mutex> Lock(Mu);
+ if (Qu.empty()) return nullptr;
+ auto Job = Qu.front();
+ Qu.pop();
+ return Job;
+ }
+};
+
+void WorkerThread(std::atomic<bool> *Stop, JobQueue *FuzzQ, JobQueue *MergeQ) {
+ while (!Stop->load()) {
+ auto Job = FuzzQ->Pop();
+ // Printf("WorkerThread: job %p\n", Job);
+ if (!Job) {
+ SleepSeconds(1);
+ continue;
+ }
+ Job->ExitCode = ExecuteCommand(Job->Cmd);
+ MergeQ->Push(Job);
+ }
+}
+
+// This is just a skeleton of an experimental -fork=1 feature.
+void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
+ const Vector<std::string> &Args,
+ const Vector<std::string> &CorpusDirs, int NumJobs) {
+ Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs);
+
+ GlobalEnv Env;
+ Env.Args = Args;
+ Env.CorpusDirs = CorpusDirs;
+ Env.Rand = &Rand;
+ Env.Verbosity = Options.Verbosity;
+ Env.ProcessStartTime = std::chrono::system_clock::now();
+
+ Vector<SizedFile> SeedFiles;
+ for (auto &Dir : CorpusDirs)
+ GetSizedFilesFromDir(Dir, &SeedFiles);
+ std::sort(SeedFiles.begin(), SeedFiles.end());
+ Env.TempDir = TempPath(".dir");
+ RmDirRecursive(Env.TempDir); // in case there is a leftover from old runs.
+ MkDir(Env.TempDir);
+
+
+ if (CorpusDirs.empty())
+ MkDir(Env.MainCorpusDir = DirPlusFile(Env.TempDir, "C"));
+ else
+ Env.MainCorpusDir = CorpusDirs[0];
+
+ auto CFPath = DirPlusFile(Env.TempDir, "merge.txt");
+ CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features,
+ {}, &Env.Cov,
+ CFPath, false);
+ RemoveFile(CFPath);
+ Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs,
+ Env.Files.size(), Env.TempDir.c_str());
+
+ int ExitCode = 0;
+
+ JobQueue FuzzQ, MergeQ;
+ std::atomic<bool> Stop(false);
+
+ size_t JobId = 1;
+ Vector<std::thread> Threads;
+ for (int t = 0; t < NumJobs; t++) {
+ Threads.push_back(std::thread(WorkerThread, &Stop, &FuzzQ, &MergeQ));
+ FuzzQ.Push(Env.CreateNewJob(JobId++));
+ }
+
+ while (true) {
+ std::unique_ptr<FuzzJob> Job(MergeQ.Pop());
+ if (!Job) {
+ if (Stop)
+ break;
+ SleepSeconds(1);
+ continue;
+ }
+ ExitCode = Job->ExitCode;
+ if (ExitCode == Options.InterruptExitCode) {
+ Printf("==%lu== libFuzzer: a child was interrupted; exiting\n", GetPid());
+ Stop = true;
+ break;
+ }
+ Fuzzer::MaybeExitGracefully();
+
+ Env.RunOneMergeJob(Job.get());
+
+ // Continue if our crash is one of the ignorred ones.
+ if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode)
+ Env.NumTimeouts++;
+ else if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode)
+ Env.NumOOMs++;
+ else if (ExitCode != 0) {
+ Env.NumCrashes++;
+ if (Options.IgnoreCrashes) {
+ std::ifstream In(Job->LogPath);
+ std::string Line;
+ while (std::getline(In, Line, '\n'))
+ if (Line.find("ERROR:") != Line.npos ||
+ Line.find("runtime error:") != Line.npos)
+ Printf("%s\n", Line.c_str());
+ } else {
+ // And exit if we don't ignore this crash.
+ Printf("INFO: log from the inner process:\n%s",
+ FileToString(Job->LogPath).c_str());
+ Stop = true;
+ }
+ }
+
+ // Stop if we are over the time budget.
+ // This is not precise, since other threads are still running
+ // and we will wait while joining them.
+ // We also don't stop instantly: other jobs need to finish.
+ if (Options.MaxTotalTimeSec > 0 && !Stop &&
+ Env.secondsSinceProcessStartUp() >= (size_t)Options.MaxTotalTimeSec) {
+ Printf("INFO: fuzzed for %zd seconds, wrapping up soon\n",
+ Env.secondsSinceProcessStartUp());
+ Stop = true;
+ }
+ if (!Stop && Env.NumRuns >= Options.MaxNumberOfRuns) {
+ Printf("INFO: fuzzed for %zd iterations, wrapping up soon\n",
+ Env.NumRuns);
+ Stop = true;
+ }
+
+ if (!Stop)
+ FuzzQ.Push(Env.CreateNewJob(JobId++));
+ }
+ Stop = true;
+
+ for (auto &T : Threads)
+ T.join();
+
+ // The workers have terminated. Don't try to remove the directory before they
+ // terminate to avoid a race condition preventing cleanup on Windows.
+ RmDirRecursive(Env.TempDir);
+
+ // Use the exit code from the last child process.
+ Printf("INFO: exiting: %d time: %zds\n", ExitCode,
+ Env.secondsSinceProcessStartUp());
+ exit(ExitCode);
+}
+
+} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerFork.h b/lib/fuzzer/FuzzerFork.h
new file mode 100644
index 000000000..b29a43e13
--- /dev/null
+++ b/lib/fuzzer/FuzzerFork.h
@@ -0,0 +1,24 @@
+//===- FuzzerFork.h - run fuzzing in sub-processes --------------*- C++ -* ===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_FORK_H
+#define LLVM_FUZZER_FORK_H
+
+#include "FuzzerDefs.h"
+#include "FuzzerOptions.h"
+#include "FuzzerRandom.h"
+
+#include <string>
+
+namespace fuzzer {
+void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
+ const Vector<std::string> &Args,
+ const Vector<std::string> &CorpusDirs, int NumJobs);
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_FORK_H
diff --git a/lib/fuzzer/FuzzerIO.cpp b/lib/fuzzer/FuzzerIO.cpp
index c4c31e824..7e5ba30a2 100644
--- a/lib/fuzzer/FuzzerIO.cpp
+++ b/lib/fuzzer/FuzzerIO.cpp
@@ -1,17 +1,17 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// IO functions.
//===----------------------------------------------------------------------===//
-#include "FuzzerIO.h"
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
+#include "FuzzerIO.h"
+#include "FuzzerUtil.h"
#include <algorithm>
#include <cstdarg>
#include <fstream>
@@ -61,10 +61,19 @@ void CopyFileToErr(const std::string &Path) {
}
void WriteToFile(const Unit &U, const std::string &Path) {
+ WriteToFile(U.data(), U.size(), Path);
+}
+
+void WriteToFile(const std::string &Data, const std::string &Path) {
+ WriteToFile(reinterpret_cast<const uint8_t *>(Data.c_str()), Data.size(),
+ Path);
+}
+
+void WriteToFile(const uint8_t *Data, size_t Size, 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");
+ FILE *Out = fopen(Path.c_str(), "wb");
if (!Out) return;
- fwrite(U.data(), sizeof(U[0]), U.size(), Out);
+ fwrite(Data, sizeof(Data[0]), Size, Out);
fclose(Out);
}
@@ -126,4 +135,25 @@ void Printf(const char *Fmt, ...) {
fflush(OutputFile);
}
+void VPrintf(bool Verbose, const char *Fmt, ...) {
+ if (!Verbose) return;
+ va_list ap;
+ va_start(ap, Fmt);
+ vfprintf(OutputFile, Fmt, ap);
+ va_end(ap);
+ fflush(OutputFile);
+}
+
+void RmDirRecursive(const std::string &Dir) {
+ IterateDirRecursive(
+ Dir, [](const std::string &Path) {},
+ [](const std::string &Path) { RmDir(Path); },
+ [](const std::string &Path) { RemoveFile(Path); });
+}
+
+std::string TempPath(const char *Extension) {
+ return DirPlusFile(TmpDir(),
+ "libFuzzerTemp." + std::to_string(GetPid()) + Extension);
+}
+
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerIO.h b/lib/fuzzer/FuzzerIO.h
index b4a68190e..fe0d7b451 100644
--- a/lib/fuzzer/FuzzerIO.h
+++ b/lib/fuzzer/FuzzerIO.h
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// IO interface.
@@ -25,6 +24,9 @@ std::string FileToString(const std::string &Path);
void CopyFileToErr(const std::string &Path);
+void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path);
+// Write Data.c_str() to the file without terminating null character.
+void WriteToFile(const std::string &Data, const std::string &Path);
void WriteToFile(const Unit &U, const std::string &Path);
void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V,
@@ -40,6 +42,8 @@ std::string DirName(const std::string &FileName);
// Returns path to a TmpDir.
std::string TmpDir();
+std::string TempPath(const char *Extension);
+
bool IsInterestingCoverageFile(const std::string &FileName);
void DupAndCloseStderr();
@@ -47,6 +51,7 @@ void DupAndCloseStderr();
void CloseStdout();
void Printf(const char *Fmt, ...);
+void VPrintf(bool Verbose, const char *Fmt, ...);
// Print using raw syscalls, useful when printing at early init stages.
void RawPrint(const char *Str);
@@ -58,6 +63,16 @@ size_t FileSize(const std::string &Path);
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
Vector<std::string> *V, bool TopDir);
+void RmDirRecursive(const std::string &Dir);
+
+// Iterate files and dirs inside Dir, recursively.
+// Call DirPreCallback/DirPostCallback on dirs before/after
+// calling FileCallback on files.
+void IterateDirRecursive(const std::string &Dir,
+ void (*DirPreCallback)(const std::string &Dir),
+ void (*DirPostCallback)(const std::string &Dir),
+ void (*FileCallback)(const std::string &Dir));
+
struct SizedFile {
std::string File;
size_t Size;
@@ -77,11 +92,17 @@ int CloseFile(int Fd);
int DuplicateFile(int Fd);
void RemoveFile(const std::string &Path);
+void RenameFile(const std::string &OldPath, const std::string &NewPath);
void DiscardOutput(int Fd);
intptr_t GetHandleFromFd(int fd);
+void MkDir(const std::string &Path);
+void RmDir(const std::string &Path);
+
+const std::string &getDevNull();
+
} // namespace fuzzer
#endif // LLVM_FUZZER_IO_H
diff --git a/lib/fuzzer/FuzzerIOPosix.cpp b/lib/fuzzer/FuzzerIOPosix.cpp
index 401b4cbbf..cfd69bbc8 100644
--- a/lib/fuzzer/FuzzerIOPosix.cpp
+++ b/lib/fuzzer/FuzzerIOPosix.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// IO functions implementation using Posix API.
@@ -79,6 +78,28 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
*Epoch = E;
}
+
+void IterateDirRecursive(const std::string &Dir,
+ void (*DirPreCallback)(const std::string &Dir),
+ void (*DirPostCallback)(const std::string &Dir),
+ void (*FileCallback)(const std::string &Dir)) {
+ DirPreCallback(Dir);
+ DIR *D = opendir(Dir.c_str());
+ if (!D) return;
+ while (auto E = readdir(D)) {
+ std::string Path = DirPlusFile(Dir, E->d_name);
+ if (E->d_type == DT_REG || E->d_type == DT_LNK ||
+ (E->d_type == DT_UNKNOWN && IsFile(Path)))
+ FileCallback(Path);
+ else if ((E->d_type == DT_DIR ||
+ (E->d_type == DT_UNKNOWN && IsDirectory(Path))) &&
+ *E->d_name != '.')
+ IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback);
+ }
+ closedir(D);
+ DirPostCallback(Dir);
+}
+
char GetSeparator() {
return '/';
}
@@ -99,6 +120,10 @@ void RemoveFile(const std::string &Path) {
unlink(Path.c_str());
}
+void RenameFile(const std::string &OldPath, const std::string &NewPath) {
+ rename(OldPath.c_str(), NewPath.c_str());
+}
+
void DiscardOutput(int Fd) {
FILE* Temp = fopen("/dev/null", "w");
if (!Temp)
@@ -137,11 +162,23 @@ bool IsInterestingCoverageFile(const std::string &FileName) {
return true;
}
-
void RawPrint(const char *Str) {
write(2, Str, strlen(Str));
}
+void MkDir(const std::string &Path) {
+ mkdir(Path.c_str(), 0700);
+}
+
+void RmDir(const std::string &Path) {
+ rmdir(Path.c_str());
+}
+
+const std::string &getDevNull() {
+ static const std::string devNull = "/dev/null";
+ return devNull;
+}
+
} // namespace fuzzer
#endif // LIBFUZZER_POSIX
diff --git a/lib/fuzzer/FuzzerIOWindows.cpp b/lib/fuzzer/FuzzerIOWindows.cpp
index 75dcaf72a..510afebef 100644
--- a/lib/fuzzer/FuzzerIOWindows.cpp
+++ b/lib/fuzzer/FuzzerIOWindows.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// IO functions implementation for Windows.
@@ -72,6 +71,11 @@ bool IsFile(const std::string &Path) {
return IsFile(Path, Att);
}
+static bool IsDir(DWORD FileAttrs) {
+ if (FileAttrs == INVALID_FILE_ATTRIBUTES) return false;
+ return FileAttrs & FILE_ATTRIBUTE_DIRECTORY;
+}
+
std::string Basename(const std::string &Path) {
size_t Pos = Path.find_last_of("/\\");
if (Pos == std::string::npos) return Path;
@@ -82,8 +86,10 @@ std::string Basename(const std::string &Path) {
size_t FileSize(const std::string &Path) {
WIN32_FILE_ATTRIBUTE_DATA attr;
if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) {
- Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
- Path.c_str(), GetLastError());
+ DWORD LastError = GetLastError();
+ if (LastError != ERROR_FILE_NOT_FOUND)
+ Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
+ Path.c_str(), LastError);
return 0;
}
ULARGE_INTEGER size;
@@ -141,6 +147,58 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
*Epoch = E;
}
+
+void IterateDirRecursive(const std::string &Dir,
+ void (*DirPreCallback)(const std::string &Dir),
+ void (*DirPostCallback)(const std::string &Dir),
+ void (*FileCallback)(const std::string &Dir)) {
+ // TODO(metzman): Implement ListFilesInDirRecursive via this function.
+ DirPreCallback(Dir);
+
+ DWORD DirAttrs = GetFileAttributesA(Dir.c_str());
+ if (!IsDir(DirAttrs)) return;
+
+ std::string TargetDir(Dir);
+ assert(!TargetDir.empty());
+ if (TargetDir.back() != '\\') TargetDir.push_back('\\');
+ TargetDir.push_back('*');
+
+ WIN32_FIND_DATAA FindInfo;
+ // Find the directory's first file.
+ HANDLE FindHandle = FindFirstFileA(TargetDir.c_str(), &FindInfo);
+ if (FindHandle == INVALID_HANDLE_VALUE) {
+ DWORD LastError = GetLastError();
+ if (LastError != ERROR_FILE_NOT_FOUND) {
+ // If the directory isn't empty, then something abnormal is going on.
+ Printf("FindFirstFileA failed for %s (Error code: %lu).\n", Dir.c_str(),
+ LastError);
+ }
+ return;
+ }
+
+ do {
+ std::string Path = DirPlusFile(Dir, FindInfo.cFileName);
+ DWORD PathAttrs = FindInfo.dwFileAttributes;
+ if (IsDir(PathAttrs)) {
+ // Is Path the current directory (".") or the parent ("..")?
+ if (strcmp(FindInfo.cFileName, ".") == 0 ||
+ strcmp(FindInfo.cFileName, "..") == 0)
+ continue;
+ IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback);
+ } else if (PathAttrs != INVALID_FILE_ATTRIBUTES) {
+ FileCallback(Path);
+ }
+ } while (FindNextFileA(FindHandle, &FindInfo));
+
+ DWORD LastError = GetLastError();
+ if (LastError != ERROR_NO_MORE_FILES)
+ Printf("FindNextFileA failed for %s (Error code: %lu).\n", Dir.c_str(),
+ LastError);
+
+ FindClose(FindHandle);
+ DirPostCallback(Dir);
+}
+
char GetSeparator() {
return '\\';
}
@@ -161,6 +219,10 @@ void RemoveFile(const std::string &Path) {
_unlink(Path.c_str());
}
+void RenameFile(const std::string &OldPath, const std::string &NewPath) {
+ rename(OldPath.c_str(), NewPath.c_str());
+}
+
void DiscardOutput(int Fd) {
FILE* Temp = fopen("nul", "w");
if (!Temp)
@@ -334,8 +396,24 @@ bool IsInterestingCoverageFile(const std::string &FileName) {
}
void RawPrint(const char *Str) {
- // Not tested, may or may not work. Fix if needed.
- Printf("%s", Str);
+ _write(2, Str, strlen(Str));
+}
+
+void MkDir(const std::string &Path) {
+ if (CreateDirectoryA(Path.c_str(), nullptr)) return;
+ Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(),
+ GetLastError());
+}
+
+void RmDir(const std::string &Path) {
+ if (RemoveDirectoryA(Path.c_str())) return;
+ Printf("RemoveDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(),
+ GetLastError());
+}
+
+const std::string &getDevNull() {
+ static const std::string devNull = "NUL";
+ return devNull;
}
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerInterface.h b/lib/fuzzer/FuzzerInterface.h
index 0f7effb2a..4f62822ea 100644
--- a/lib/fuzzer/FuzzerInterface.h
+++ b/lib/fuzzer/FuzzerInterface.h
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Define the interface between libFuzzer and the library being tested.
@@ -26,25 +25,32 @@
extern "C" {
#endif // __cplusplus
+// Define FUZZER_INTERFACE_VISIBILITY to set default visibility in a way that
+// doesn't break MSVC.
+#if defined(_WIN32)
+#define FUZZER_INTERFACE_VISIBILITY __declspec(dllexport)
+#else
+#define FUZZER_INTERFACE_VISIBILITY __attribute__((visibility("default")))
+#endif
+
// 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.
-__attribute__((visibility("default"))) int
+FUZZER_INTERFACE_VISIBILITY 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.
-__attribute__((visibility("default"))) int LLVMFuzzerInitialize(int *argc,
- char ***argv);
+FUZZER_INTERFACE_VISIBILITY 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.
-__attribute__((visibility("default"))) size_t
+FUZZER_INTERFACE_VISIBILITY size_t
LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
unsigned int Seed);
@@ -52,7 +58,7 @@ LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
// 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.
-__attribute__((visibility("default"))) size_t
+FUZZER_INTERFACE_VISIBILITY 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);
@@ -61,9 +67,11 @@ LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
// libFuzzer-provided function to be used inside LLVMFuzzerCustomMutator.
// Mutates raw data in [Data, Data+Size) inplace.
// Returns the new size, which is not greater than MaxSize.
-__attribute__((visibility("default"))) size_t
+FUZZER_INTERFACE_VISIBILITY size_t
LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+#undef FUZZER_INTERFACE_VISIBILITY
+
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
diff --git a/lib/fuzzer/FuzzerInternal.h b/lib/fuzzer/FuzzerInternal.h
index a7fdc89cb..f20dae014 100644
--- a/lib/fuzzer/FuzzerInternal.h
+++ b/lib/fuzzer/FuzzerInternal.h
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Define the main class fuzzer::Fuzzer and most functions.
@@ -36,8 +35,10 @@ public:
Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
FuzzingOptions Options);
~Fuzzer();
- void Loop(const Vector<std::string> &CorpusDirs);
- void ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs);
+ void Loop(const Vector<std::string> &CorpusDirs,
+ const Vector<std::string> &ExtraSeedFiles);
+ void ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs,
+ const Vector<std::string> &ExtraSeedFiles);
void MinimizeCrashLoop(const Unit &U);
void RereadOutputCorpus(size_t MaxSize);
@@ -72,11 +73,6 @@ public:
// Merge Corpora[1:] into Corpora[0].
void Merge(const Vector<std::string> &Corpora);
- void CrashResistantMerge(const Vector<std::string> &Args,
- const Vector<std::string> &Corpora,
- const char *CoverageSummaryInputPathOrNull,
- const char *CoverageSummaryOutputPathOrNull,
- const char *MergeControlFilePathOrNull);
void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
MutationDispatcher &GetMD() { return MD; }
void PrintFinalStats();
@@ -90,20 +86,19 @@ public:
bool DuringInitialCorpusExecution);
void HandleMalloc(size_t Size);
- void AnnounceOutput(const uint8_t *Data, size_t Size);
+ static void MaybeExitGracefully();
+ std::string WriteToOutputCorpus(const Unit &U);
private:
void AlarmCallback();
void CrashCallback();
void ExitCallback();
- void MaybeExitGracefully();
void CrashOnOverwrittenData();
void InterruptCallback();
void MutateAndTestOne();
void PurgeAllocator();
void ReportNewCoverage(InputInfo *II, const Unit &U);
void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t 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, const char *Text);
diff --git a/lib/fuzzer/FuzzerLoop.cpp b/lib/fuzzer/FuzzerLoop.cpp
index a32a30723..fd5b226a1 100644
--- a/lib/fuzzer/FuzzerLoop.cpp
+++ b/lib/fuzzer/FuzzerLoop.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Fuzzer's main loop.
@@ -14,7 +13,6 @@
#include "FuzzerInternal.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
-#include "FuzzerShmem.h"
#include "FuzzerTracePC.h"
#include <algorithm>
#include <cstring>
@@ -41,8 +39,6 @@ static const size_t kMaxUnitSizeToPrint = 256;
thread_local bool Fuzzer::IsMyThread;
-SharedMemoryRegion SMR;
-
bool RunningUserCallback = false;
// Only one Fuzzer per process.
@@ -135,7 +131,7 @@ void Fuzzer::HandleMalloc(size_t Size) {
DumpCurrentUnit("oom-");
Printf("SUMMARY: libFuzzer: out-of-memory\n");
PrintFinalStats();
- _Exit(Options.ErrorExitCode); // Stop right now.
+ _Exit(Options.OOMExitCode); // Stop right now.
}
Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
@@ -157,7 +153,7 @@ Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)
EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);
MaxInputLen = MaxMutationLen = Options.MaxLen;
- TmpMaxMutationLen = Max(size_t(4), Corpus.MaxInputSize());
+ TmpMaxMutationLen = 0; // Will be set once we load the corpus.
AllocateCurrentUnitData();
CurrentUnitSize = 0;
memset(BaseSha1, 0, sizeof(BaseSha1));
@@ -231,8 +227,9 @@ void Fuzzer::StaticFileSizeExceedCallback() {
}
void Fuzzer::CrashCallback() {
- if (EF->__sanitizer_acquire_crash_state)
- EF->__sanitizer_acquire_crash_state();
+ if (EF->__sanitizer_acquire_crash_state &&
+ !EF->__sanitizer_acquire_crash_state())
+ return;
Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
PrintStackTrace();
Printf("NOTE: libFuzzer has rudimentary signal handlers.\n"
@@ -259,16 +256,20 @@ void Fuzzer::ExitCallback() {
}
void Fuzzer::MaybeExitGracefully() {
- if (!GracefulExitRequested) return;
+ if (!F->GracefulExitRequested) return;
Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid());
- PrintFinalStats();
+ RmDirRecursive(TempPath(".dir"));
+ F->PrintFinalStats();
_Exit(0);
}
void Fuzzer::InterruptCallback() {
Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());
PrintFinalStats();
- _Exit(0); // Stop right now, don't perform any at-exit actions.
+ ScopedDisableMsanInterceptorChecks S; // RmDirRecursive may call opendir().
+ RmDirRecursive(TempPath(".dir"));
+ // Stop right now, don't perform any at-exit actions.
+ _Exit(Options.InterruptExitCode);
}
NO_SANITIZE_MEMORY
@@ -317,7 +318,7 @@ void Fuzzer::RssLimitCallback() {
DumpCurrentUnit("oom-");
Printf("SUMMARY: libFuzzer: out-of-memory\n");
PrintFinalStats();
- _Exit(Options.ErrorExitCode); // Stop right now.
+ _Exit(Options.OOMExitCode); // Stop right now.
}
void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
@@ -355,8 +356,6 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
void Fuzzer::PrintFinalStats() {
if (Options.PrintCoverage)
TPC.PrintCoverage();
- if (Options.DumpCoverage)
- TPC.DumpCoverage();
if (Options.PrintCorpusStats)
Corpus.PrintStats();
if (!Options.PrintFinalStats)
@@ -388,10 +387,10 @@ void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {
void Fuzzer::CheckExitOnSrcPosOrItem() {
if (!Options.ExitOnSrcPos.empty()) {
static auto *PCsSet = new Set<uintptr_t>;
- auto HandlePC = [&](uintptr_t PC) {
- if (!PCsSet->insert(PC).second)
+ auto HandlePC = [&](const TracePC::PCTableEntry *TE) {
+ if (!PCsSet->insert(TE->PC).second)
return;
- std::string Descr = DescribePC("%F %L", PC + 1);
+ std::string Descr = DescribePC("%F %L", TE->PC + 1);
if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {
Printf("INFO: found line matching '%s', exiting.\n",
Options.ExitOnSrcPos.c_str());
@@ -447,6 +446,23 @@ void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {
}
}
+static void WriteFeatureSetToFile(const std::string &FeaturesDir,
+ const std::string &FileName,
+ const Vector<uint32_t> &FeatureSet) {
+ if (FeaturesDir.empty() || FeatureSet.empty()) return;
+ WriteToFile(reinterpret_cast<const uint8_t *>(FeatureSet.data()),
+ FeatureSet.size() * sizeof(FeatureSet[0]),
+ DirPlusFile(FeaturesDir, FileName));
+}
+
+static void RenameFeatureSetFile(const std::string &FeaturesDir,
+ const std::string &OldFile,
+ const std::string &NewFile) {
+ if (FeaturesDir.empty()) return;
+ RenameFile(DirPlusFile(FeaturesDir, OldFile),
+ DirPlusFile(FeaturesDir, NewFile));
+}
+
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
InputInfo *II, bool *FoundUniqFeatures) {
if (!Size)
@@ -471,15 +487,21 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;
if (NumNewFeatures) {
TPC.UpdateObservedPCs();
- Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
- TPC.ObservedFocusFunction(), UniqFeatureSetTmp, DFT, II);
+ auto NewII = Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures,
+ MayDeleteFile, TPC.ObservedFocusFunction(),
+ UniqFeatureSetTmp, DFT, II);
+ WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1),
+ NewII->UniqFeatureSet);
return true;
}
if (II && FoundUniqFeaturesOfII &&
II->DataFlowTraceForFocusFunction.empty() &&
FoundUniqFeaturesOfII == II->UniqFeatureSet.size() &&
II->U.size() > Size) {
+ auto OldFeaturesFile = Sha1ToString(II->Sha1);
Corpus.Replace(II, {Data, Data + Size});
+ RenameFeatureSetFile(Options.FeaturesDir, OldFeaturesFile,
+ Sha1ToString(II->Sha1));
return true;
}
return false;
@@ -513,8 +535,6 @@ void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
TPC.RecordInitialStack();
TotalNumberOfRuns++;
assert(InFuzzingThread());
- if (SMR.IsClient())
- SMR.WriteByteArray(Data, Size);
// 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];
@@ -543,15 +563,16 @@ void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
delete[] DataCopy;
}
-void Fuzzer::WriteToOutputCorpus(const Unit &U) {
+std::string Fuzzer::WriteToOutputCorpus(const Unit &U) {
if (Options.OnlyASCII)
assert(IsASCII(U));
if (Options.OutputCorpus.empty())
- return;
+ return "";
std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));
WriteToFile(U, Path);
if (Options.Verbosity >= 2)
Printf("Written %zd bytes to %s\n", U.size(), Path.c_str());
+ return Path;
}
void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
@@ -637,6 +658,8 @@ void Fuzzer::MutateAndTestOne() {
MD.StartMutationSequence();
auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());
+ if (Options.DoCrossOver)
+ MD.SetCrossOverWith(&Corpus.ChooseUnitToMutate(MD.GetRand()).U);
const auto &U = II.U;
memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1));
assert(CurrentUnitData);
@@ -659,7 +682,9 @@ void Fuzzer::MutateAndTestOne() {
Size <= CurrentMaxMutationLen)
NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size,
II.DataFlowTraceForFocusFunction);
- else
+
+ // If MutateWithMask either failed or wasn't called, call default Mutate.
+ if (!NewSize)
NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);
assert(NewSize > 0 && "Mutator returned empty unit");
assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit");
@@ -695,7 +720,9 @@ void Fuzzer::PurgeAllocator() {
LastAllocatorPurgeAttemptTime = system_clock::now();
}
-void Fuzzer::ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs) {
+void Fuzzer::ReadAndExecuteSeedCorpora(
+ const Vector<std::string> &CorpusDirs,
+ const Vector<std::string> &ExtraSeedFiles) {
const size_t kMaxSaneLen = 1 << 20;
const size_t kMinDefaultLen = 4096;
Vector<SizedFile> SizedFiles;
@@ -709,6 +736,11 @@ void Fuzzer::ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs) {
Dir.c_str());
LastNumFiles = SizedFiles.size();
}
+ // Add files from -seed_inputs.
+ for (auto &File : ExtraSeedFiles)
+ if (auto Size = FileSize(File))
+ SizedFiles.push_back({File, Size});
+
for (auto &File : SizedFiles) {
MaxSize = Max(File.Size, MaxSize);
MinSize = Min(File.Size, MinSize);
@@ -722,6 +754,10 @@ void Fuzzer::ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs) {
uint8_t dummy = 0;
ExecuteCallback(&dummy, 0);
+ // Protect lazy counters here, after the once-init code has been executed.
+ if (Options.LazyCounters)
+ TPC.ProtectLazyCounters();
+
if (SizedFiles.empty()) {
Printf("INFO: A corpus is not provided, starting from an empty corpus\n");
Unit U({'\n'}); // Valid ASCII input.
@@ -764,14 +800,17 @@ void Fuzzer::ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs) {
}
}
-void Fuzzer::Loop(const Vector<std::string> &CorpusDirs) {
- ReadAndExecuteSeedCorpora(CorpusDirs);
+void Fuzzer::Loop(const Vector<std::string> &CorpusDirs,
+ const Vector<std::string> &ExtraSeedFiles) {
+ ReadAndExecuteSeedCorpora(CorpusDirs, ExtraSeedFiles);
DFT.Clear(); // No need for DFT any more.
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs);
system_clock::time_point LastCorpusReload = system_clock::now();
- if (Options.DoCrossOver)
- MD.SetCorpus(&Corpus);
+
+ TmpMaxMutationLen =
+ Min(MaxMutationLen, Max(size_t(4), Corpus.MaxInputSize()));
+
while (true) {
auto Now = system_clock::now();
if (duration_cast<seconds>(Now - LastCorpusReload).count() >=
@@ -824,44 +863,14 @@ void Fuzzer::MinimizeCrashLoop(const Unit &U) {
}
}
-void Fuzzer::AnnounceOutput(const uint8_t *Data, size_t Size) {
- if (SMR.IsServer()) {
- SMR.WriteByteArray(Data, Size);
- } else if (SMR.IsClient()) {
- SMR.PostClient();
- SMR.WaitServer();
- size_t OtherSize = SMR.ReadByteArraySize();
- uint8_t *OtherData = SMR.GetByteArray();
- if (Size != OtherSize || memcmp(Data, OtherData, Size) != 0) {
- size_t i = 0;
- for (i = 0; i < Min(Size, OtherSize); i++)
- if (Data[i] != OtherData[i])
- break;
- Printf("==%lu== ERROR: libFuzzer: equivalence-mismatch. Sizes: %zd %zd; "
- "offset %zd\n",
- GetPid(), Size, OtherSize, i);
- DumpCurrentUnit("mismatch-");
- Printf("SUMMARY: libFuzzer: equivalence-mismatch\n");
- PrintFinalStats();
- _Exit(Options.ErrorExitCode);
- }
- }
-}
-
} // namespace fuzzer
extern "C" {
-__attribute__((visibility("default"))) size_t
+ATTRIBUTE_INTERFACE size_t
LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
assert(fuzzer::F);
return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
}
-// Experimental
-__attribute__((visibility("default"))) void
-LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) {
- assert(fuzzer::F);
- fuzzer::F->AnnounceOutput(Data, Size);
-}
} // extern "C"
diff --git a/lib/fuzzer/FuzzerMain.cpp b/lib/fuzzer/FuzzerMain.cpp
index f2c8e9c7b..771a34aed 100644
--- a/lib/fuzzer/FuzzerMain.cpp
+++ b/lib/fuzzer/FuzzerMain.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// main() and flags.
@@ -16,6 +15,6 @@ extern "C" {
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
} // extern "C"
-__attribute__((visibility("default"))) int main(int argc, char **argv) {
+ATTRIBUTE_INTERFACE int main(int argc, char **argv) {
return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput);
}
diff --git a/lib/fuzzer/FuzzerMerge.cpp b/lib/fuzzer/FuzzerMerge.cpp
index 5f3052a39..dace45ece 100644
--- a/lib/fuzzer/FuzzerMerge.cpp
+++ b/lib/fuzzer/FuzzerMerge.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Merging corpora.
@@ -43,10 +42,12 @@ void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) {
// 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.
+// FT 0 1 4 6 8 # FileID COV1 COV2 ...
+// COV 0 7 8 9 # FileID COV1 COV1
+// STARTED 1 456 # If FT is missing, the input crashed while processing.
// STARTED 2 567
-// DONE 2 8 9
+// FT 2 8 9
+// COV 2 11 12
bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
LastFailure.clear();
std::string Line;
@@ -71,11 +72,12 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
if (!std::getline(IS, Files[i].Name, '\n'))
return false;
- // Parse STARTED and DONE lines.
+ // Parse STARTED, FT, and COV lines.
size_t ExpectedStartMarker = 0;
const size_t kInvalidStartMarker = -1;
size_t LastSeenStartMarker = kInvalidStartMarker;
Vector<uint32_t> TmpFeatures;
+ Set<uint32_t> PCs;
while (std::getline(IS, Line, '\n')) {
std::istringstream ISS1(Line);
std::string Marker;
@@ -90,19 +92,25 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
LastSeenStartMarker = ExpectedStartMarker;
assert(ExpectedStartMarker < Files.size());
ExpectedStartMarker++;
- } else if (Marker == "DONE") {
- // DONE FILE_ID COV1 COV2 COV3 ...
+ } else if (Marker == "FT") {
+ // FT FILE_ID COV1 COV2 COV3 ...
size_t CurrentFileIdx = N;
if (CurrentFileIdx != LastSeenStartMarker)
return false;
LastSeenStartMarker = kInvalidStartMarker;
if (ParseCoverage) {
TmpFeatures.clear(); // use a vector from outer scope to avoid resizes.
- while (ISS1 >> std::hex >> N)
+ while (ISS1 >> N)
TmpFeatures.push_back(N);
std::sort(TmpFeatures.begin(), TmpFeatures.end());
Files[CurrentFileIdx].Features = TmpFeatures;
}
+ } else if (Marker == "COV") {
+ size_t CurrentFileIdx = N;
+ if (ParseCoverage)
+ while (ISS1 >> N)
+ if (PCs.insert(N).second)
+ Files[CurrentFileIdx].Cov.push_back(N);
} else {
return false;
}
@@ -121,21 +129,21 @@ size_t Merger::ApproximateMemoryConsumption() const {
return Res;
}
-// Decides which files need to be merged (add thost to NewFiles).
+// Decides which files need to be merged (add those to NewFiles).
// Returns the number of new features added.
size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
+ Set<uint32_t> *NewFeatures,
+ const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov,
Vector<std::string> *NewFiles) {
NewFiles->clear();
assert(NumFilesInFirstCorpus <= Files.size());
- Set<uint32_t> AllFeatures(InitialFeatures);
+ Set<uint32_t> AllFeatures = InitialFeatures;
// 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;
@@ -161,22 +169,20 @@ size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
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)
+ bool FoundNewFeatures = false;
+ for (auto Fe: Cur) {
+ if (AllFeatures.insert(Fe).second) {
+ FoundNewFeatures = true;
+ NewFeatures->insert(Fe);
+ }
+ }
+ if (FoundNewFeatures)
NewFiles->push_back(Files[i].Name);
+ for (auto Cov : Files[i].Cov)
+ if (InitialCov.find(Cov) == InitialCov.end())
+ NewCov->insert(Cov);
}
- return AllFeatures.size() - InitialNumFeatures;
-}
-
-void Merger::PrintSummary(std::ostream &OS) {
- for (auto &File : Files) {
- OS << std::hex;
- OS << File.Name << " size: " << File.Size << " features: ";
- for (auto Feature : File.Features)
- OS << " " << Feature;
- OS << "\n";
- }
+ return NewFeatures->size();
}
Set<uint32_t> Merger::AllFeatures() const {
@@ -186,25 +192,6 @@ Set<uint32_t> Merger::AllFeatures() const {
return S;
}
-Set<uint32_t> Merger::ParseSummary(std::istream &IS) {
- std::string Line, Tmp;
- Set<uint32_t> Res;
- while (std::getline(IS, Line, '\n')) {
- size_t N;
- std::istringstream ISS1(Line);
- ISS1 >> Tmp; // Name
- ISS1 >> Tmp; // size:
- assert(Tmp == "size:" && "Corrupt summary file");
- ISS1 >> std::hex;
- ISS1 >> N; // File Size
- ISS1 >> Tmp; // features:
- assert(Tmp == "features:" && "Corrupt summary file");
- while (ISS1 >> std::hex >> N)
- Res.insert(N);
- }
- return Res;
-}
-
// 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());
@@ -223,8 +210,9 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app);
Set<size_t> AllFeatures;
+ Set<const TracePC::PCTableEntry *> AllPCs;
for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
- MaybeExitGracefully();
+ Fuzzer::MaybeExitGracefully();
auto U = FileToVector(M.Files[i].Name);
if (U.size() > MaxInputLen) {
U.resize(MaxInputLen);
@@ -232,7 +220,7 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
}
std::ostringstream StartedLine;
// Write the pre-run marker.
- OF << "STARTED " << std::dec << i << " " << U.size() << "\n";
+ OF << "STARTED " << i << " " << U.size() << "\n";
OF.flush(); // Flush is important since Command::Execute may crash.
// Run.
TPC.ResetMaps();
@@ -247,26 +235,36 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
if (AllFeatures.insert(Feature).second)
UniqFeatures.insert(Feature);
});
+ TPC.UpdateObservedPCs();
// Show stats.
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)))
PrintStats("pulse ");
// Write the post-run marker and the coverage.
- OF << "DONE " << i;
+ OF << "FT " << i;
for (size_t F : UniqFeatures)
- OF << " " << std::hex << F;
+ OF << " " << F;
+ OF << "\n";
+ OF << "COV " << i;
+ TPC.ForEachObservedPC([&](const TracePC::PCTableEntry *TE) {
+ if (AllPCs.insert(TE).second)
+ OF << " " << TPC.PCTableEntryIdx(TE);
+ });
OF << "\n";
OF.flush();
}
+ PrintStats("DONE ");
}
static void WriteNewControlFile(const std::string &CFPath,
- const Vector<SizedFile> &AllFiles,
- size_t NumFilesInFirstCorpus) {
+ const Vector<SizedFile> &OldCorpus,
+ const Vector<SizedFile> &NewCorpus) {
RemoveFile(CFPath);
std::ofstream ControlFile(CFPath);
- ControlFile << AllFiles.size() << "\n";
- ControlFile << NumFilesInFirstCorpus << "\n";
- for (auto &SF: AllFiles)
+ ControlFile << (OldCorpus.size() + NewCorpus.size()) << "\n";
+ ControlFile << OldCorpus.size() << "\n";
+ for (auto &SF: OldCorpus)
+ ControlFile << SF.File << "\n";
+ for (auto &SF: NewCorpus)
ControlFile << SF.File << "\n";
if (!ControlFile) {
Printf("MERGE-OUTER: failed to write to the control file: %s\n",
@@ -275,116 +273,89 @@ static void WriteNewControlFile(const std::string &CFPath,
}
}
-// Outer process. Does not call the target code and thus sohuld not fail.
-void Fuzzer::CrashResistantMerge(const Vector<std::string> &Args,
- const Vector<std::string> &Corpora,
- const char *CoverageSummaryInputPathOrNull,
- const char *CoverageSummaryOutputPathOrNull,
- const char *MergeControlFilePathOrNull) {
- if (Corpora.size() <= 1) {
- Printf("Merge requires two or more corpus dirs\n");
- return;
- }
- auto CFPath =
- MergeControlFilePathOrNull
- ? MergeControlFilePathOrNull
- : DirPlusFile(TmpDir(),
- "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
-
+// Outer process. Does not call the target code and thus should not fail.
+void CrashResistantMerge(const Vector<std::string> &Args,
+ const Vector<SizedFile> &OldCorpus,
+ const Vector<SizedFile> &NewCorpus,
+ Vector<std::string> *NewFiles,
+ const Set<uint32_t> &InitialFeatures,
+ Set<uint32_t> *NewFeatures,
+ const Set<uint32_t> &InitialCov,
+ Set<uint32_t> *NewCov,
+ const std::string &CFPath,
+ bool V /*Verbose*/) {
+ if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge.
size_t NumAttempts = 0;
- if (MergeControlFilePathOrNull && FileSize(MergeControlFilePathOrNull)) {
- Printf("MERGE-OUTER: non-empty control file provided: '%s'\n",
- MergeControlFilePathOrNull);
+ if (FileSize(CFPath)) {
+ VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n",
+ CFPath.c_str());
Merger M;
- std::ifstream IF(MergeControlFilePathOrNull);
+ std::ifstream IF(CFPath);
if (M.Parse(IF, /*ParseCoverage=*/false)) {
- Printf("MERGE-OUTER: control file ok, %zd files total,"
+ VPrintf(V, "MERGE-OUTER: control file ok, %zd files total,"
" first not processed file %zd\n",
M.Files.size(), M.FirstNotProcessedFile);
if (!M.LastFailure.empty())
- Printf("MERGE-OUTER: '%s' will be skipped as unlucky "
+ VPrintf(V, "MERGE-OUTER: '%s' will be skipped as unlucky "
"(merge has stumbled on it the last time)\n",
M.LastFailure.c_str());
if (M.FirstNotProcessedFile >= M.Files.size()) {
- Printf("MERGE-OUTER: nothing to do, merge has been completed before\n");
+ VPrintf(
+ V, "MERGE-OUTER: nothing to do, merge has been completed before\n");
exit(0);
}
NumAttempts = M.Files.size() - M.FirstNotProcessedFile;
} else {
- Printf("MERGE-OUTER: bad control file, will overwrite it\n");
+ VPrintf(V, "MERGE-OUTER: bad control file, will overwrite it\n");
}
}
if (!NumAttempts) {
// The supplied control file is empty or bad, create a fresh one.
- Vector<SizedFile> AllFiles;
- GetSizedFilesFromDir(Corpora[0], &AllFiles);
- size_t NumFilesInFirstCorpus = AllFiles.size();
- std::sort(AllFiles.begin(), AllFiles.end());
- for (size_t i = 1; i < Corpora.size(); i++)
- GetSizedFilesFromDir(Corpora[i], &AllFiles);
- std::sort(AllFiles.begin() + NumFilesInFirstCorpus, AllFiles.end());
- Printf("MERGE-OUTER: %zd files, %zd in the initial corpus\n",
- AllFiles.size(), NumFilesInFirstCorpus);
- WriteNewControlFile(CFPath, AllFiles, NumFilesInFirstCorpus);
- NumAttempts = AllFiles.size();
+ NumAttempts = OldCorpus.size() + NewCorpus.size();
+ VPrintf(V, "MERGE-OUTER: %zd files, %zd in the initial corpus\n",
+ NumAttempts, OldCorpus.size());
+ WriteNewControlFile(CFPath, OldCorpus, NewCorpus);
}
// Execute the inner process until it passes.
// Every inner process should execute at least one input.
Command BaseCmd(Args);
BaseCmd.removeFlag("merge");
- bool Success = false;
+ BaseCmd.removeFlag("fork");
for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) {
- MaybeExitGracefully();
- Printf("MERGE-OUTER: attempt %zd\n", Attempt);
+ Fuzzer::MaybeExitGracefully();
+ VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt);
Command Cmd(BaseCmd);
Cmd.addFlag("merge_control_file", CFPath);
Cmd.addFlag("merge_inner", "1");
+ if (!V) {
+ Cmd.setOutputFile(getDevNull());
+ Cmd.combineOutAndErr();
+ }
auto ExitCode = ExecuteCommand(Cmd);
if (!ExitCode) {
- Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt);
- Success = true;
+ VPrintf(V, "MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt);
break;
}
}
- if (!Success) {
- Printf("MERGE-OUTER: zero succesfull attempts, exiting\n");
- exit(1);
- }
// Read the control file and do the merge.
Merger M;
std::ifstream IF(CFPath);
IF.seekg(0, IF.end);
- Printf("MERGE-OUTER: the control file has %zd bytes\n", (size_t)IF.tellg());
+ VPrintf(V, "MERGE-OUTER: the control file has %zd bytes\n",
+ (size_t)IF.tellg());
IF.seekg(0, IF.beg);
M.ParseOrExit(IF, true);
IF.close();
- Printf("MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n",
- M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb());
- if (CoverageSummaryOutputPathOrNull) {
- Printf("MERGE-OUTER: writing coverage summary for %zd files to %s\n",
- M.Files.size(), CoverageSummaryOutputPathOrNull);
- std::ofstream SummaryOut(CoverageSummaryOutputPathOrNull);
- M.PrintSummary(SummaryOut);
- }
- Vector<std::string> NewFiles;
- Set<uint32_t> InitialFeatures;
- if (CoverageSummaryInputPathOrNull) {
- std::ifstream SummaryIn(CoverageSummaryInputPathOrNull);
- InitialFeatures = M.ParseSummary(SummaryIn);
- Printf("MERGE-OUTER: coverage summary loaded from %s, %zd features found\n",
- CoverageSummaryInputPathOrNull, InitialFeatures.size());
- }
- size_t NumNewFeatures = M.Merge(InitialFeatures, &NewFiles);
- Printf("MERGE-OUTER: %zd new files with %zd new features added\n",
- NewFiles.size(), NumNewFeatures);
- for (auto &F: NewFiles)
- WriteToOutputCorpus(FileToVector(F, MaxInputLen));
- // We are done, delete the control file if it was a temporary one.
- if (!MergeControlFilePathOrNull)
- RemoveFile(CFPath);
+ VPrintf(V,
+ "MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n",
+ M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb());
+ M.Merge(InitialFeatures, NewFeatures, InitialCov, NewCov, NewFiles);
+ VPrintf(V, "MERGE-OUTER: %zd new files with %zd new features added; "
+ "%zd new coverage edges\n",
+ NewFiles->size(), NewFeatures->size(), NewCov->size());
}
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerMerge.h b/lib/fuzzer/FuzzerMerge.h
index e54885a1e..c14dd589e 100644
--- a/lib/fuzzer/FuzzerMerge.h
+++ b/lib/fuzzer/FuzzerMerge.h
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Merging Corpora.
@@ -52,7 +51,7 @@ namespace fuzzer {
struct MergeFileInfo {
std::string Name;
size_t Size = 0;
- Vector<uint32_t> Features;
+ Vector<uint32_t> Features, Cov;
};
struct Merger {
@@ -64,17 +63,24 @@ struct Merger {
bool Parse(std::istream &IS, bool ParseCoverage);
bool Parse(const std::string &Str, bool ParseCoverage);
void ParseOrExit(std::istream &IS, bool ParseCoverage);
- void PrintSummary(std::ostream &OS);
- Set<uint32_t> ParseSummary(std::istream &IS);
- size_t Merge(const Set<uint32_t> &InitialFeatures,
+ size_t Merge(const Set<uint32_t> &InitialFeatures, Set<uint32_t> *NewFeatures,
+ const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov,
Vector<std::string> *NewFiles);
- size_t Merge(Vector<std::string> *NewFiles) {
- return Merge(Set<uint32_t>{}, NewFiles);
- }
size_t ApproximateMemoryConsumption() const;
Set<uint32_t> AllFeatures() const;
};
+void CrashResistantMerge(const Vector<std::string> &Args,
+ const Vector<SizedFile> &OldCorpus,
+ const Vector<SizedFile> &NewCorpus,
+ Vector<std::string> *NewFiles,
+ const Set<uint32_t> &InitialFeatures,
+ Set<uint32_t> *NewFeatures,
+ const Set<uint32_t> &InitialCov,
+ Set<uint32_t> *NewCov,
+ const std::string &CFPath,
+ bool Verbose);
+
} // namespace fuzzer
#endif // LLVM_FUZZER_MERGE_H
diff --git a/lib/fuzzer/FuzzerMutate.cpp b/lib/fuzzer/FuzzerMutate.cpp
index 142b2b0b0..29541eac5 100644
--- a/lib/fuzzer/FuzzerMutate.cpp
+++ b/lib/fuzzer/FuzzerMutate.cpp
@@ -1,20 +1,19 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Mutate a test input.
//===----------------------------------------------------------------------===//
-#include "FuzzerMutate.h"
-#include "FuzzerCorpus.h"
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
+#include "FuzzerMutate.h"
#include "FuzzerOptions.h"
+#include "FuzzerTracePC.h"
namespace fuzzer {
@@ -73,10 +72,10 @@ size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size,
size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size,
size_t MaxSize) {
- if (!Corpus || Corpus->size() < 2 || Size == 0)
+ if (Size == 0)
return 0;
- size_t Idx = Rand(Corpus->size());
- const Unit &Other = (*Corpus)[Idx];
+ if (!CrossOverWith) return 0;
+ const Unit &Other = *CrossOverWith;
if (Other.empty())
return 0;
CustomCrossOverInPlaceHere.resize(MaxSize);
@@ -422,9 +421,9 @@ size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data,
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 (Size == 0) return 0;
+ if (!CrossOverWith) return 0;
+ const Unit &O = *CrossOverWith;
if (O.empty()) return 0;
MutateInPlaceHere.resize(MaxSize);
auto &U = MutateInPlaceHere;
@@ -530,7 +529,7 @@ size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size,
size_t MaxSize,
const Vector<uint8_t> &Mask) {
- assert(Size <= Mask.size());
+ size_t MaskedSize = std::min(Size, Mask.size());
// * Copy the worthy bytes into a temporary array T
// * Mutate T
// * Copy T back.
@@ -539,16 +538,17 @@ size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size,
if (T.size() < Size)
T.resize(Size);
size_t OneBits = 0;
- for (size_t I = 0; I < Size; I++)
+ for (size_t I = 0; I < MaskedSize; I++)
if (Mask[I])
T[OneBits++] = Data[I];
+ if (!OneBits) return 0;
assert(!T.empty());
size_t NewSize = Mutate(T.data(), OneBits, OneBits);
assert(NewSize <= OneBits);
(void)NewSize;
// Even if NewSize < OneBits we still use all OneBits bytes.
- for (size_t I = 0, J = 0; I < Size; I++)
+ for (size_t I = 0, J = 0; I < MaskedSize; I++)
if (Mask[I])
Data[I] = T[J++];
return Size;
diff --git a/lib/fuzzer/FuzzerMutate.h b/lib/fuzzer/FuzzerMutate.h
index a51c7fb44..6cbce8027 100644
--- a/lib/fuzzer/FuzzerMutate.h
+++ b/lib/fuzzer/FuzzerMutate.h
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::MutationDispatcher
@@ -64,7 +63,7 @@ public:
/// 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.
+ /// CrossOver Data with CrossOverWith.
size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
/// Applies one of the configured mutations.
@@ -89,7 +88,7 @@ public:
void PrintRecommendedDictionary();
- void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; }
+ void SetCrossOverWith(const Unit *U) { CrossOverWith = U; }
Random &GetRand() { return Rand; }
@@ -140,7 +139,7 @@ public:
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
size_t CmpDictionaryEntriesDequeIdx = 0;
- const InputCorpus *Corpus = nullptr;
+ const Unit *CrossOverWith = nullptr;
Vector<uint8_t> MutateInPlaceHere;
Vector<uint8_t> MutateWithMaskTemp;
// CustomCrossOver needs its own buffer as a custom implementation may call
diff --git a/lib/fuzzer/FuzzerOptions.h b/lib/fuzzer/FuzzerOptions.h
index ab90df82a..d48439daa 100644
--- a/lib/fuzzer/FuzzerOptions.h
+++ b/lib/fuzzer/FuzzerOptions.h
@@ -1,8 +1,7 @@
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::FuzzingOptions
@@ -20,8 +19,13 @@ struct FuzzingOptions {
size_t MaxLen = 0;
size_t LenControl = 1000;
int UnitTimeoutSec = 300;
- int TimeoutExitCode = 77;
+ int TimeoutExitCode = 70;
+ int OOMExitCode = 71;
+ int InterruptExitCode = 72;
int ErrorExitCode = 77;
+ bool IgnoreTimeouts = true;
+ bool IgnoreOOMs = true;
+ bool IgnoreCrashes = false;
int MaxTotalTimeSec = 0;
int RssLimitMb = 0;
int MallocLimitMb = 0;
@@ -47,6 +51,7 @@ struct FuzzingOptions {
std::string ExitOnItem;
std::string FocusFunction;
std::string DataFlowTrace;
+ std::string FeaturesDir;
bool SaveArtifacts = true;
bool PrintNEW = true; // Print a status line when new units are found;
bool PrintNewCovPcs = false;
@@ -68,6 +73,7 @@ struct FuzzingOptions {
bool HandleXfsz = false;
bool HandleUsr1 = false;
bool HandleUsr2 = false;
+ bool LazyCounters = false;
};
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerRandom.h b/lib/fuzzer/FuzzerRandom.h
index 8a1aa3ef5..659283eee 100644
--- a/lib/fuzzer/FuzzerRandom.h
+++ b/lib/fuzzer/FuzzerRandom.h
@@ -1,9 +1,8 @@
//===- FuzzerRandom.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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::Random
@@ -15,12 +14,17 @@
#include <random>
namespace fuzzer {
-class Random : public std::mt19937 {
+class Random : public std::minstd_rand {
public:
- Random(unsigned int seed) : std::mt19937(seed) {}
- result_type operator()() { return this->std::mt19937::operator()(); }
+ Random(unsigned int seed) : std::minstd_rand(seed) {}
+ result_type operator()() { return this->std::minstd_rand::operator()(); }
size_t Rand() { return this->operator()(); }
size_t RandBool() { return Rand() % 2; }
+ size_t SkewTowardsLast(size_t n) {
+ size_t T = this->operator()(n * n);
+ size_t Res = sqrt(T);
+ return Res;
+ }
size_t operator()(size_t n) { return n ? Rand() % n : 0; }
intptr_t operator()(intptr_t From, intptr_t To) {
assert(From < To);
diff --git a/lib/fuzzer/FuzzerSHA1.cpp b/lib/fuzzer/FuzzerSHA1.cpp
index d2f8e811b..43e5e78cd 100644
--- a/lib/fuzzer/FuzzerSHA1.cpp
+++ b/lib/fuzzer/FuzzerSHA1.cpp
@@ -1,9 +1,8 @@
//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This code is taken from public domain
@@ -32,7 +31,8 @@ namespace { // Added for LibFuzzer
#ifdef __BIG_ENDIAN__
# define SHA_BIG_ENDIAN
-#elif defined __LITTLE_ENDIAN__
+// Windows is always little endian and MSVC doesn't have <endian.h>
+#elif defined __LITTLE_ENDIAN__ || LIBFUZZER_WINDOWS
/* override */
#elif defined __BYTE_ORDER
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
diff --git a/lib/fuzzer/FuzzerSHA1.h b/lib/fuzzer/FuzzerSHA1.h
index 3b5e6e807..05cbacda8 100644
--- a/lib/fuzzer/FuzzerSHA1.h
+++ b/lib/fuzzer/FuzzerSHA1.h
@@ -1,9 +1,8 @@
//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// SHA1 utils.
diff --git a/lib/fuzzer/FuzzerShmem.h b/lib/fuzzer/FuzzerShmem.h
deleted file mode 100644
index 53568e0ac..000000000
--- a/lib/fuzzer/FuzzerShmem.h
+++ /dev/null
@@ -1,69 +0,0 @@
-//===- FuzzerShmem.h - shared memory interface ------------------*- C++ -* ===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// SharedMemoryRegion
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_FUZZER_SHMEM_H
-#define LLVM_FUZZER_SHMEM_H
-
-#include <algorithm>
-#include <cstring>
-#include <string>
-
-#include "FuzzerDefs.h"
-
-namespace fuzzer {
-
-class SharedMemoryRegion {
- public:
- bool Create(const char *Name);
- bool Open(const char *Name);
- bool Destroy(const char *Name);
- uint8_t *GetData() { return Data; }
- void PostServer() {Post(0);}
- void WaitServer() {Wait(0);}
- void PostClient() {Post(1);}
- void WaitClient() {Wait(1);}
-
- size_t WriteByteArray(const uint8_t *Bytes, size_t N) {
- assert(N <= kShmemSize - sizeof(N));
- memcpy(GetData(), &N, sizeof(N));
- memcpy(GetData() + sizeof(N), Bytes, N);
- assert(N == ReadByteArraySize());
- return N;
- }
- size_t ReadByteArraySize() {
- size_t Res;
- memcpy(&Res, GetData(), sizeof(Res));
- return Res;
- }
- uint8_t *GetByteArray() { return GetData() + sizeof(size_t); }
-
- bool IsServer() const { return Data && IAmServer; }
- bool IsClient() const { return Data && !IAmServer; }
-
-private:
-
- static const size_t kShmemSize = 1 << 22;
- bool IAmServer;
- std::string Path(const char *Name);
- std::string SemName(const char *Name, int Idx);
- void Post(int Idx);
- void Wait(int Idx);
-
- bool Map(int fd);
- uint8_t *Data = nullptr;
- void *Semaphore[2];
-};
-
-extern SharedMemoryRegion SMR;
-
-} // namespace fuzzer
-
-#endif // LLVM_FUZZER_SHMEM_H
diff --git a/lib/fuzzer/FuzzerShmemFuchsia.cpp b/lib/fuzzer/FuzzerShmemFuchsia.cpp
deleted file mode 100644
index e9ce50c2a..000000000
--- a/lib/fuzzer/FuzzerShmemFuchsia.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-//===- FuzzerShmemPosix.cpp - Posix shared memory ---------------*- C++ -* ===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// SharedMemoryRegion. For Fuchsia, this is just stubs as equivalence servers
-// are not currently supported.
-//===----------------------------------------------------------------------===//
-#include "FuzzerDefs.h"
-
-#if LIBFUZZER_FUCHSIA
-
-#include "FuzzerShmem.h"
-
-namespace fuzzer {
-
-bool SharedMemoryRegion::Create(const char *Name) {
- return false;
-}
-
-bool SharedMemoryRegion::Open(const char *Name) {
- return false;
-}
-
-bool SharedMemoryRegion::Destroy(const char *Name) {
- return false;
-}
-
-void SharedMemoryRegion::Post(int Idx) {}
-
-void SharedMemoryRegion::Wait(int Idx) {}
-
-} // namespace fuzzer
-
-#endif // LIBFUZZER_FUCHSIA
diff --git a/lib/fuzzer/FuzzerShmemPosix.cpp b/lib/fuzzer/FuzzerShmemPosix.cpp
deleted file mode 100644
index 41a93f610..000000000
--- a/lib/fuzzer/FuzzerShmemPosix.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-//===- FuzzerShmemPosix.cpp - Posix shared memory ---------------*- C++ -* ===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// SharedMemoryRegion
-//===----------------------------------------------------------------------===//
-#include "FuzzerDefs.h"
-#if LIBFUZZER_POSIX
-
-#include "FuzzerIO.h"
-#include "FuzzerShmem.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <semaphore.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-namespace fuzzer {
-
-std::string SharedMemoryRegion::Path(const char *Name) {
- return DirPlusFile(TmpDir(), Name);
-}
-
-std::string SharedMemoryRegion::SemName(const char *Name, int Idx) {
- std::string Res(Name);
- // When passing a name without a leading <slash> character to
- // sem_open, the behaviour is unspecified in POSIX. Add a leading
- // <slash> character for the name if there is no such one.
- if (!Res.empty() && Res[0] != '/')
- Res.insert(Res.begin(), '/');
- return Res + (char)('0' + Idx);
-}
-
-bool SharedMemoryRegion::Map(int fd) {
- Data =
- (uint8_t *)mmap(0, kShmemSize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
- if (Data == (uint8_t*)-1)
- return false;
- return true;
-}
-
-bool SharedMemoryRegion::Create(const char *Name) {
- int fd = open(Path(Name).c_str(), O_CREAT | O_RDWR, 0777);
- if (fd < 0) return false;
- if (ftruncate(fd, kShmemSize) < 0) return false;
- if (!Map(fd))
- return false;
- for (int i = 0; i < 2; i++) {
- sem_unlink(SemName(Name, i).c_str());
- Semaphore[i] = sem_open(SemName(Name, i).c_str(), O_CREAT, 0644, 0);
- if (Semaphore[i] == SEM_FAILED)
- return false;
- }
- IAmServer = true;
- return true;
-}
-
-bool SharedMemoryRegion::Open(const char *Name) {
- int fd = open(Path(Name).c_str(), O_RDWR);
- if (fd < 0) return false;
- struct stat stat_res;
- if (0 != fstat(fd, &stat_res))
- return false;
- assert(stat_res.st_size == kShmemSize);
- if (!Map(fd))
- return false;
- for (int i = 0; i < 2; i++) {
- Semaphore[i] = sem_open(SemName(Name, i).c_str(), 0);
- if (Semaphore[i] == SEM_FAILED)
- return false;
- }
- IAmServer = false;
- return true;
-}
-
-bool SharedMemoryRegion::Destroy(const char *Name) {
- return 0 == unlink(Path(Name).c_str());
-}
-
-void SharedMemoryRegion::Post(int Idx) {
- assert(Idx == 0 || Idx == 1);
- sem_post((sem_t*)Semaphore[Idx]);
-}
-
-void SharedMemoryRegion::Wait(int Idx) {
- assert(Idx == 0 || Idx == 1);
- for (int i = 0; i < 10 && sem_wait((sem_t*)Semaphore[Idx]); i++) {
- // sem_wait may fail if interrupted by a signal.
- sleep(i);
- if (i)
- Printf("%s: sem_wait[%d] failed %s\n", i < 9 ? "WARNING" : "ERROR", i,
- strerror(errno));
- if (i == 9) abort();
- }
-}
-
-} // namespace fuzzer
-
-#endif // LIBFUZZER_POSIX
diff --git a/lib/fuzzer/FuzzerShmemWindows.cpp b/lib/fuzzer/FuzzerShmemWindows.cpp
deleted file mode 100644
index d330ebf4f..000000000
--- a/lib/fuzzer/FuzzerShmemWindows.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-//===- FuzzerShmemWindows.cpp - Posix shared memory -------------*- C++ -* ===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// SharedMemoryRegion
-//===----------------------------------------------------------------------===//
-#include "FuzzerDefs.h"
-#if LIBFUZZER_WINDOWS
-
-#include "FuzzerIO.h"
-#include "FuzzerShmem.h"
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-namespace fuzzer {
-
-std::string SharedMemoryRegion::Path(const char *Name) {
- return DirPlusFile(TmpDir(), Name);
-}
-
-std::string SharedMemoryRegion::SemName(const char *Name, int Idx) {
- std::string Res(Name);
- return Res + (char)('0' + Idx);
-}
-
-bool SharedMemoryRegion::Map(int fd) {
- assert(0 && "UNIMPLEMENTED");
- return false;
-}
-
-bool SharedMemoryRegion::Create(const char *Name) {
- assert(0 && "UNIMPLEMENTED");
- return false;
-}
-
-bool SharedMemoryRegion::Open(const char *Name) {
- assert(0 && "UNIMPLEMENTED");
- return false;
-}
-
-bool SharedMemoryRegion::Destroy(const char *Name) {
- assert(0 && "UNIMPLEMENTED");
- return false;
-}
-
-void SharedMemoryRegion::Post(int Idx) {
- assert(0 && "UNIMPLEMENTED");
-}
-
-void SharedMemoryRegion::Wait(int Idx) {
- Semaphore[1] = nullptr;
- assert(0 && "UNIMPLEMENTED");
-}
-
-} // namespace fuzzer
-
-#endif // LIBFUZZER_WINDOWS
diff --git a/lib/fuzzer/FuzzerTracePC.cpp b/lib/fuzzer/FuzzerTracePC.cpp
index 80b33105b..a2d3b7e7f 100644
--- a/lib/fuzzer/FuzzerTracePC.cpp
+++ b/lib/fuzzer/FuzzerTracePC.cpp
@@ -1,9 +1,8 @@
//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Trace PCs.
@@ -24,15 +23,6 @@
#include "FuzzerValueBitMap.h"
#include <set>
-// The coverage counters and PCs.
-// These are declared as global variables named "__sancov_*" to simplify
-// experiments with inlined instrumentation.
-alignas(64) ATTRIBUTE_INTERFACE
-uint8_t __sancov_trace_pc_guard_8bit_counters[fuzzer::TracePC::kNumPCs];
-
-ATTRIBUTE_INTERFACE
-uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs];
-
// Used by -fsanitize-coverage=stack-depth to track stack depth
ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack;
@@ -40,33 +30,80 @@ namespace fuzzer {
TracePC TPC;
-uint8_t *TracePC::Counters() const {
- return __sancov_trace_pc_guard_8bit_counters;
-}
-
-uintptr_t *TracePC::PCs() const {
- return __sancov_trace_pc_pcs;
-}
-
size_t TracePC::GetTotalPCCoverage() {
- if (ObservedPCs.size())
- return ObservedPCs.size();
- size_t Res = 0;
- for (size_t i = 1, N = GetNumPCs(); i < N; i++)
- if (PCs()[i])
- Res++;
- return Res;
+ return ObservedPCs.size();
}
void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) {
if (Start == Stop) return;
- if (NumModulesWithInline8bitCounters &&
- ModuleCounters[NumModulesWithInline8bitCounters-1].Start == Start) return;
- assert(NumModulesWithInline8bitCounters <
- sizeof(ModuleCounters) / sizeof(ModuleCounters[0]));
- ModuleCounters[NumModulesWithInline8bitCounters++] = {Start, Stop};
- NumInline8bitCounters += Stop - Start;
+ if (NumModules &&
+ Modules[NumModules - 1].Start() == Start)
+ return;
+ assert(NumModules <
+ sizeof(Modules) / sizeof(Modules[0]));
+ auto &M = Modules[NumModules++];
+ uint8_t *AlignedStart = RoundUpByPage(Start);
+ uint8_t *AlignedStop = RoundDownByPage(Stop);
+ size_t NumFullPages = AlignedStop > AlignedStart ?
+ (AlignedStop - AlignedStart) / PageSize() : 0;
+ bool NeedFirst = Start < AlignedStart || !NumFullPages;
+ bool NeedLast = Stop > AlignedStop && AlignedStop >= AlignedStart;
+ M.NumRegions = NumFullPages + NeedFirst + NeedLast;;
+ assert(M.NumRegions > 0);
+ M.Regions = new Module::Region[M.NumRegions];
+ assert(M.Regions);
+ size_t R = 0;
+ if (NeedFirst)
+ M.Regions[R++] = {Start, std::min(Stop, AlignedStart), true, false};
+ for (uint8_t *P = AlignedStart; P < AlignedStop; P += PageSize())
+ M.Regions[R++] = {P, P + PageSize(), true, true};
+ if (NeedLast)
+ M.Regions[R++] = {AlignedStop, Stop, true, false};
+ assert(R == M.NumRegions);
+ assert(M.Size() == (size_t)(Stop - Start));
+ assert(M.Stop() == Stop);
+ assert(M.Start() == Start);
+ NumInline8bitCounters += M.Size();
+}
+
+// Mark all full page counter regions as PROT_NONE and set Enabled=false.
+// The first time the instrumented code hits such a protected/disabled
+// counter region we should catch a SEGV and call UnprotectLazyCounters,
+// which will mark the page as PROT_READ|PROT_WRITE and set Enabled=true.
+//
+// Whenever other functions iterate over the counters they should ignore
+// regions with Enabled=false.
+void TracePC::ProtectLazyCounters() {
+ size_t NumPagesProtected = 0;
+ IterateCounterRegions([&](Module::Region &R) {
+ if (!R.OneFullPage) return;
+ if (Mprotect(R.Start, R.Stop - R.Start, false)) {
+ R.Enabled = false;
+ NumPagesProtected++;
+ }
+ });
+ if (NumPagesProtected)
+ Printf("INFO: %zd pages of counters where protected;"
+ " libFuzzer's SEGV handler must be installed\n",
+ NumPagesProtected);
+}
+
+bool TracePC::UnprotectLazyCounters(void *CounterPtr) {
+ // Printf("UnprotectLazyCounters: %p\n", CounterPtr);
+ if (!CounterPtr)
+ return false;
+ bool Done = false;
+ uint8_t *Addr = reinterpret_cast<uint8_t *>(CounterPtr);
+ IterateCounterRegions([&](Module::Region &R) {
+ if (!R.OneFullPage || R.Enabled || Done) return;
+ if (Addr >= R.Start && Addr < R.Stop)
+ if (Mprotect(R.Start, R.Stop - R.Start, true)) {
+ R.Enabled = true;
+ Done = true;
+ }
+ });
+ return Done;
}
void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) {
@@ -78,38 +115,13 @@ void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) {
NumPCsInPCTables += E - B;
}
-void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) {
- if (Start == Stop || *Start) return;
- assert(NumModules < sizeof(Modules) / sizeof(Modules[0]));
- for (uint32_t *P = Start; P < Stop; P++) {
- NumGuards++;
- if (NumGuards == kNumPCs) {
- RawPrint(
- "WARNING: The binary has too many instrumented PCs.\n"
- " You may want to reduce the size of the binary\n"
- " for more efficient fuzzing and precise coverage data\n");
- }
- *P = NumGuards % kNumPCs;
- }
- Modules[NumModules].Start = Start;
- Modules[NumModules].Stop = Stop;
- NumModules++;
-}
-
void TracePC::PrintModuleInfo() {
- if (NumGuards) {
- Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards);
- for (size_t i = 0; i < NumModules; i++)
- Printf("%zd [%p, %p), ", Modules[i].Stop - Modules[i].Start,
- Modules[i].Start, Modules[i].Stop);
- Printf("\n");
- }
- if (NumModulesWithInline8bitCounters) {
+ if (NumModules) {
Printf("INFO: Loaded %zd modules (%zd inline 8-bit counters): ",
- NumModulesWithInline8bitCounters, NumInline8bitCounters);
- for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++)
- Printf("%zd [%p, %p), ", ModuleCounters[i].Stop - ModuleCounters[i].Start,
- ModuleCounters[i].Start, ModuleCounters[i].Stop);
+ NumModules, NumInline8bitCounters);
+ for (size_t i = 0; i < NumModules; i++)
+ Printf("%zd [%p, %p), ", Modules[i].Size(), Modules[i].Start(),
+ Modules[i].Stop());
Printf("\n");
}
if (NumPCTables) {
@@ -121,8 +133,7 @@ void TracePC::PrintModuleInfo() {
}
Printf("\n");
- if ((NumGuards && NumGuards != NumPCsInPCTables) ||
- (NumInline8bitCounters && NumInline8bitCounters != NumPCsInPCTables)) {
+ if (NumInline8bitCounters && NumInline8bitCounters != NumPCsInPCTables) {
Printf("ERROR: The size of coverage PC tables does not match the\n"
"number of instrumented PCs. This might be a compiler bug,\n"
"please contact the libFuzzer developers.\n"
@@ -163,7 +174,7 @@ inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
/// \return the address of the next instruction.
/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cc`
-inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) {
+ALWAYS_INLINE uintptr_t TracePC::GetNextInstructionPc(uintptr_t PC) {
#if defined(__mips__)
return PC + 8;
#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \
@@ -176,41 +187,34 @@ inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) {
void TracePC::UpdateObservedPCs() {
Vector<uintptr_t> CoveredFuncs;
- auto ObservePC = [&](uintptr_t PC) {
- if (ObservedPCs.insert(PC).second && DoPrintNewPCs) {
- PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", GetNextInstructionPc(PC));
+ auto ObservePC = [&](const PCTableEntry *TE) {
+ if (ObservedPCs.insert(TE).second && DoPrintNewPCs) {
+ PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p",
+ GetNextInstructionPc(TE->PC));
Printf("\n");
}
};
- auto Observe = [&](const PCTableEntry &TE) {
- if (TE.PCFlags & 1)
- if (++ObservedFuncs[TE.PC] == 1 && NumPrintNewFuncs)
- CoveredFuncs.push_back(TE.PC);
- ObservePC(TE.PC);
+ auto Observe = [&](const PCTableEntry *TE) {
+ if (PcIsFuncEntry(TE))
+ if (++ObservedFuncs[TE->PC] == 1 && NumPrintNewFuncs)
+ CoveredFuncs.push_back(TE->PC);
+ ObservePC(TE);
};
if (NumPCsInPCTables) {
if (NumInline8bitCounters == NumPCsInPCTables) {
- for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
- uint8_t *Beg = ModuleCounters[i].Start;
- size_t Size = ModuleCounters[i].Stop - Beg;
- assert(Size ==
- (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
- for (size_t j = 0; j < Size; j++)
- if (Beg[j])
- Observe(ModulePCTable[i].Start[j]);
- }
- } else if (NumGuards == NumPCsInPCTables) {
- size_t GuardIdx = 1;
for (size_t i = 0; i < NumModules; i++) {
- uint32_t *Beg = Modules[i].Start;
- size_t Size = Modules[i].Stop - Beg;
- assert(Size ==
+ auto &M = Modules[i];
+ assert(M.Size() ==
(size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
- for (size_t j = 0; j < Size; j++, GuardIdx++)
- if (Counters()[GuardIdx])
- Observe(ModulePCTable[i].Start[j]);
+ for (size_t r = 0; r < M.NumRegions; r++) {
+ auto &R = M.Regions[r];
+ if (!R.Enabled) continue;
+ for (uint8_t *P = R.Start; P < R.Stop; P++)
+ if (*P)
+ Observe(&ModulePCTable[i].Start[M.Idx(P)]);
+ }
}
}
}
@@ -223,6 +227,27 @@ void TracePC::UpdateObservedPCs() {
}
}
+uintptr_t TracePC::PCTableEntryIdx(const PCTableEntry *TE) {
+ size_t TotalTEs = 0;
+ for (size_t i = 0; i < NumPCTables; i++) {
+ auto &M = ModulePCTable[i];
+ if (TE >= M.Start && TE < M.Stop)
+ return TotalTEs + TE - M.Start;
+ TotalTEs += M.Stop - M.Start;
+ }
+ assert(0);
+ return 0;
+}
+
+const TracePC::PCTableEntry *TracePC::PCTableEntryByIdx(uintptr_t Idx) {
+ for (size_t i = 0; i < NumPCTables; i++) {
+ auto &M = ModulePCTable[i];
+ size_t Size = M.Stop - M.Start;
+ if (Idx < Size) return &M.Start[Idx];
+ Idx -= Size;
+ }
+ return nullptr;
+}
static std::string GetModuleName(uintptr_t PC) {
char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
@@ -242,47 +267,38 @@ void TracePC::IterateCoveredFunctions(CallBack CB) {
auto ModuleName = GetModuleName(M.Start->PC);
for (auto NextFE = M.Start; NextFE < M.Stop; ) {
auto FE = NextFE;
- assert((FE->PCFlags & 1) && "Not a function entry point");
+ assert(PcIsFuncEntry(FE) && "Not a function entry point");
do {
NextFE++;
- } while (NextFE < M.Stop && !(NextFE->PCFlags & 1));
- if (ObservedFuncs.count(FE->PC))
- CB(FE, NextFE, ObservedFuncs[FE->PC]);
+ } while (NextFE < M.Stop && !(PcIsFuncEntry(NextFE)));
+ CB(FE, NextFE, ObservedFuncs[FE->PC]);
}
}
}
void TracePC::SetFocusFunction(const std::string &FuncName) {
// This function should be called once.
- assert(FocusFunction.first > NumModulesWithInline8bitCounters);
+ assert(!FocusFunctionCounterPtr);
if (FuncName.empty())
return;
- for (size_t M = 0; M < NumModulesWithInline8bitCounters; M++) {
+ for (size_t M = 0; M < NumModules; M++) {
auto &PCTE = ModulePCTable[M];
size_t N = PCTE.Stop - PCTE.Start;
for (size_t I = 0; I < N; I++) {
- if (!(PCTE.Start[I].PCFlags & 1)) continue; // not a function entry.
+ if (!(PcIsFuncEntry(&PCTE.Start[I]))) continue; // not a function entry.
auto Name = DescribePC("%F", GetNextInstructionPc(PCTE.Start[I].PC));
if (Name[0] == 'i' && Name[1] == 'n' && Name[2] == ' ')
Name = Name.substr(3, std::string::npos);
if (FuncName != Name) continue;
Printf("INFO: Focus function is set to '%s'\n", Name.c_str());
- FocusFunction = {M, I};
+ FocusFunctionCounterPtr = Modules[M].Start() + I;
return;
}
}
}
bool TracePC::ObservedFocusFunction() {
- size_t I = FocusFunction.first;
- size_t J = FocusFunction.second;
- if (I >= NumModulesWithInline8bitCounters)
- return false;
- auto &MC = ModuleCounters[I];
- size_t Size = MC.Stop - MC.Start;
- if (J >= Size)
- return false;
- return MC.Start[J] != 0;
+ return FocusFunctionCounterPtr && *FocusFunctionCounterPtr;
}
void TracePC::PrintCoverage() {
@@ -306,32 +322,24 @@ void TracePC::PrintCoverage() {
if (FunctionStr.find("in ") == 0)
FunctionStr = FunctionStr.substr(3);
std::string LineStr = DescribePC("%l", VisualizePC);
- size_t Line = std::stoul(LineStr);
size_t NumEdges = Last - First;
Vector<uintptr_t> UncoveredPCs;
for (auto TE = First; TE < Last; TE++)
- if (!ObservedPCs.count(TE->PC))
+ if (!ObservedPCs.count(TE))
UncoveredPCs.push_back(TE->PC);
- Printf("COVERED_FUNC: hits: %zd", Counter);
+ Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter);
Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges);
- Printf(" %s %s:%zd\n", FunctionStr.c_str(), FileStr.c_str(), Line);
- for (auto PC: UncoveredPCs)
- Printf(" UNCOVERED_PC: %s\n",
- DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str());
+ Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(),
+ LineStr.c_str());
+ if (Counter)
+ for (auto PC : UncoveredPCs)
+ Printf(" UNCOVERED_PC: %s\n",
+ DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str());
};
IterateCoveredFunctions(CoveredFunctionCallback);
}
-void TracePC::DumpCoverage() {
- if (EF->__sanitizer_dump_coverage) {
- Vector<uintptr_t> PCsCopy(GetNumPCs());
- for (size_t i = 0; i < GetNumPCs(); i++)
- PCsCopy[i] = PCs()[i] ? GetPreviousInstructionPc(PCs()[i]) : 0;
- EF->__sanitizer_dump_coverage(PCsCopy.data(), PCsCopy.size());
- }
-}
-
// Value profile.
// We keep track of various values that affect control flow.
// These values are inserted into a bit-set-based hash map.
@@ -400,11 +408,10 @@ static size_t InternalStrnlen2(const char *S1, const char *S2) {
}
void TracePC::ClearInlineCounters() {
- for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
- uint8_t *Beg = ModuleCounters[i].Start;
- size_t Size = ModuleCounters[i].Stop - Beg;
- memset(Beg, 0, Size);
- }
+ IterateCounterRegions([](const Module::Region &R){
+ if (R.Enabled)
+ memset(R.Start, 0, R.Stop - R.Start);
+ });
}
ATTRIBUTE_NO_SANITIZE_ALL
@@ -417,16 +424,25 @@ uintptr_t TracePC::GetMaxStackOffset() const {
return InitialStack - __sancov_lowest_stack; // Stack grows down
}
+void WarnAboutDeprecatedInstrumentation(const char *flag) {
+ // Use RawPrint because Printf cannot be used on Windows before OutputFile is
+ // initialized.
+ RawPrint(flag);
+ RawPrint(
+ " is no longer supported by libFuzzer.\n"
+ "Please either migrate to a compiler that supports -fsanitize=fuzzer\n"
+ "or use an older version of libFuzzer\n");
+ exit(1);
+}
+
} // namespace fuzzer
extern "C" {
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
- uint32_t Idx = *Guard;
- __sancov_trace_pc_pcs[Idx] = PC;
- __sancov_trace_pc_guard_8bit_counters[Idx]++;
+ fuzzer::WarnAboutDeprecatedInstrumentation(
+ "-fsanitize-coverage=trace-pc-guard");
}
// Best-effort support for -fsanitize-coverage=trace-pc, which is available
@@ -434,15 +450,13 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
void __sanitizer_cov_trace_pc() {
- uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
- uintptr_t Idx = PC & (((uintptr_t)1 << fuzzer::TracePC::kTracePcBits) - 1);
- __sancov_trace_pc_pcs[Idx] = PC;
- __sancov_trace_pc_guard_8bit_counters[Idx]++;
+ fuzzer::WarnAboutDeprecatedInstrumentation("-fsanitize-coverage=trace-pc");
}
ATTRIBUTE_INTERFACE
void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
- fuzzer::TPC.HandleInit(Start, Stop);
+ fuzzer::WarnAboutDeprecatedInstrumentation(
+ "-fsanitize-coverage=trace-pc-guard");
}
ATTRIBUTE_INTERFACE
@@ -537,24 +551,44 @@ void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
uint64_t N = Cases[0];
uint64_t ValSizeInBits = Cases[1];
uint64_t *Vals = Cases + 2;
- // Skip the most common and the most boring case.
- if (Vals[N - 1] < 256 && Val < 256)
+ // Skip the most common and the most boring case: all switch values are small.
+ // We may want to skip this at compile-time, but it will make the
+ // instrumentation less general.
+ if (Vals[N - 1] < 256)
+ return;
+ // Also skip small inputs values, they won't give good signal.
+ if (Val < 256)
return;
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
size_t i;
- uint64_t Token = 0;
+ uint64_t Smaller = 0;
+ uint64_t Larger = ~(uint64_t)0;
+ // Find two switch values such that Smaller < Val < Larger.
+ // Use 0 and 0xfff..f as the defaults.
for (i = 0; i < N; i++) {
- Token = Val ^ Vals[i];
- if (Val < Vals[i])
+ if (Val < Vals[i]) {
+ Larger = Vals[i];
break;
+ }
+ if (Val > Vals[i]) Smaller = Vals[i];
}
- if (ValSizeInBits == 16)
- fuzzer::TPC.HandleCmp(PC + i, static_cast<uint16_t>(Token), (uint16_t)(0));
- else if (ValSizeInBits == 32)
- fuzzer::TPC.HandleCmp(PC + i, static_cast<uint32_t>(Token), (uint32_t)(0));
- else
- fuzzer::TPC.HandleCmp(PC + i, Token, (uint64_t)(0));
+ // Apply HandleCmp to {Val,Smaller} and {Val, Larger},
+ // use i as the PC modifier for HandleCmp.
+ if (ValSizeInBits == 16) {
+ fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast<uint16_t>(Val),
+ (uint16_t)(Smaller));
+ fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast<uint16_t>(Val),
+ (uint16_t)(Larger));
+ } else if (ValSizeInBits == 32) {
+ fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast<uint32_t>(Val),
+ (uint32_t)(Smaller));
+ fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast<uint32_t>(Val),
+ (uint32_t)(Larger));
+ } else {
+ fuzzer::TPC.HandleCmp(PC + 2*i, Val, Smaller);
+ fuzzer::TPC.HandleCmp(PC + 2*i + 1, Val, Larger);
+ }
}
ATTRIBUTE_INTERFACE
diff --git a/lib/fuzzer/FuzzerTracePC.h b/lib/fuzzer/FuzzerTracePC.h
index 46d6c2488..4f5ebeb04 100644
--- a/lib/fuzzer/FuzzerTracePC.h
+++ b/lib/fuzzer/FuzzerTracePC.h
@@ -1,9 +1,8 @@
//===- FuzzerTracePC.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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// fuzzer::TracePC
@@ -70,11 +69,6 @@ struct MemMemTable {
class TracePC {
public:
- static const size_t kNumPCs = 1 << 21;
- // How many bits of PC are used from __sanitizer_cov_trace_pc.
- static const size_t kTracePcBits = 18;
-
- void HandleInit(uint32_t *Start, uint32_t *Stop);
void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop);
void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop);
void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee);
@@ -89,8 +83,6 @@ class TracePC {
void ResetMaps() {
ValueProfileMap.Reset();
- if (NumModules)
- memset(Counters(), 0, GetNumPCs());
ClearExtraCounters();
ClearInlineCounters();
}
@@ -103,7 +95,6 @@ class TracePC {
void PrintModuleInfo();
void PrintCoverage();
- void DumpCoverage();
template<class CallBack>
void IterateCoveredFunctions(CallBack CB);
@@ -116,14 +107,6 @@ class TracePC {
TableOfRecentCompares<Word, 32> TORCW;
MemMemTable<1024> MMT;
- size_t GetNumPCs() const {
- return NumGuards == 0 ? (1 << kTracePcBits) : Min(kNumPCs, NumGuards + 1);
- }
- uintptr_t GetPC(size_t Idx) {
- assert(Idx < GetNumPCs());
- return PCs()[Idx];
- }
-
void RecordInitialStack();
uintptr_t GetMaxStackOffset() const;
@@ -136,39 +119,63 @@ class TracePC {
void SetFocusFunction(const std::string &FuncName);
bool ObservedFocusFunction();
+ void ProtectLazyCounters();
+ bool UnprotectLazyCounters(void *CounterPtr);
+
+ struct PCTableEntry {
+ uintptr_t PC, PCFlags;
+ };
+
+ uintptr_t PCTableEntryIdx(const PCTableEntry *TE);
+ const PCTableEntry *PCTableEntryByIdx(uintptr_t Idx);
+ static uintptr_t GetNextInstructionPc(uintptr_t PC);
+ bool PcIsFuncEntry(const PCTableEntry *TE) { return TE->PCFlags & 1; }
+
private:
bool UseCounters = false;
uint32_t UseValueProfileMask = false;
bool DoPrintNewPCs = false;
size_t NumPrintNewFuncs = 0;
+ // Module represents the array of 8-bit counters split into regions
+ // such that every region, except maybe the first and the last one, is one
+ // full page.
struct Module {
- uint32_t *Start, *Stop;
+ struct Region {
+ uint8_t *Start, *Stop;
+ bool Enabled;
+ bool OneFullPage;
+ };
+ Region *Regions;
+ size_t NumRegions;
+ uint8_t *Start() { return Regions[0].Start; }
+ uint8_t *Stop() { return Regions[NumRegions - 1].Stop; }
+ size_t Size() { return Stop() - Start(); }
+ size_t Idx(uint8_t *P) {
+ assert(P >= Start() && P < Stop());
+ return P - Start();
+ }
};
Module Modules[4096];
size_t NumModules; // linker-initialized.
- size_t NumGuards; // linker-initialized.
-
- struct { uint8_t *Start, *Stop; } ModuleCounters[4096];
- size_t NumModulesWithInline8bitCounters; // linker-initialized.
size_t NumInline8bitCounters;
- struct PCTableEntry {
- uintptr_t PC, PCFlags;
- };
+ template <class Callback>
+ void IterateCounterRegions(Callback CB) {
+ for (size_t m = 0; m < NumModules; m++)
+ for (size_t r = 0; r < Modules[m].NumRegions; r++)
+ CB(Modules[m].Regions[r]);
+ }
struct { const PCTableEntry *Start, *Stop; } ModulePCTable[4096];
size_t NumPCTables;
size_t NumPCsInPCTables;
- uint8_t *Counters() const;
- uintptr_t *PCs() const;
-
- Set<uintptr_t> ObservedPCs;
+ Set<const PCTableEntry*> ObservedPCs;
std::unordered_map<uintptr_t, uintptr_t> ObservedFuncs; // PC => Counter.
- std::pair<size_t, size_t> FocusFunction = {-1, -1}; // Module and PC IDs.
+ uint8_t *FocusFunctionCounterPtr = nullptr;
ValueBitMap ValueProfileMap;
uintptr_t InitialStack;
@@ -177,7 +184,7 @@ private:
template <class Callback>
// void Callback(size_t FirstFeature, size_t Idx, uint8_t Value);
ATTRIBUTE_NO_SANITIZE_ALL
-void ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End,
+size_t ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End,
size_t FirstFeature, Callback Handle8bitCounter) {
typedef uintptr_t LargeType;
const size_t Step = sizeof(LargeType) / sizeof(uint8_t);
@@ -199,6 +206,7 @@ void ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End,
for (; P < End; P++)
if (uint8_t V = *P)
Handle8bitCounter(FirstFeature, P - Begin, V);
+ return End - Begin;
}
// Given a non-zero Counter returns a number in the range [0,7].
@@ -229,10 +237,8 @@ unsigned CounterToFeature(T Counter) {
template <class Callback> // void Callback(size_t Feature)
ATTRIBUTE_NO_SANITIZE_ADDRESS
-__attribute__((noinline))
+ATTRIBUTE_NOINLINE
void TracePC::CollectFeatures(Callback HandleFeature) const {
- uint8_t *Counters = this->Counters();
- size_t N = GetNumPCs();
auto Handle8bitCounter = [&](size_t FirstFeature,
size_t Idx, uint8_t Counter) {
if (UseCounters)
@@ -243,22 +249,18 @@ void TracePC::CollectFeatures(Callback HandleFeature) const {
size_t FirstFeature = 0;
- if (!NumInline8bitCounters) {
- ForEachNonZeroByte(Counters, Counters + N, FirstFeature, Handle8bitCounter);
- FirstFeature += N * 8;
- }
-
- if (NumInline8bitCounters) {
- for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
- ForEachNonZeroByte(ModuleCounters[i].Start, ModuleCounters[i].Stop,
- FirstFeature, Handle8bitCounter);
- FirstFeature += 8 * (ModuleCounters[i].Stop - ModuleCounters[i].Start);
+ for (size_t i = 0; i < NumModules; i++) {
+ for (size_t r = 0; r < Modules[i].NumRegions; r++) {
+ if (!Modules[i].Regions[r].Enabled) continue;
+ FirstFeature += 8 * ForEachNonZeroByte(Modules[i].Regions[r].Start,
+ Modules[i].Regions[r].Stop,
+ FirstFeature, Handle8bitCounter);
}
}
- ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), FirstFeature,
- Handle8bitCounter);
- FirstFeature += (ExtraCountersEnd() - ExtraCountersBegin()) * 8;
+ FirstFeature +=
+ 8 * ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(),
+ FirstFeature, Handle8bitCounter);
if (UseValueProfileMask) {
ValueProfileMap.ForEach([&](size_t Idx) {
diff --git a/lib/fuzzer/FuzzerUtil.cpp b/lib/fuzzer/FuzzerUtil.cpp
index 6286f9a71..7aa84a1fa 100644
--- a/lib/fuzzer/FuzzerUtil.cpp
+++ b/lib/fuzzer/FuzzerUtil.cpp
@@ -1,9 +1,8 @@
//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils.
diff --git a/lib/fuzzer/FuzzerUtil.h b/lib/fuzzer/FuzzerUtil.h
index d2f1d5de4..0a127911d 100644
--- a/lib/fuzzer/FuzzerUtil.h
+++ b/lib/fuzzer/FuzzerUtil.h
@@ -1,9 +1,8 @@
//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Util functions.
@@ -53,6 +52,8 @@ void SetSignalHandler(const FuzzingOptions& Options);
void SleepSeconds(int Seconds);
+bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite);
+
unsigned long GetPid();
size_t GetPeakRSSMb();
@@ -88,6 +89,20 @@ size_t SimpleFastHash(const uint8_t *Data, size_t Size);
inline uint32_t Log(uint32_t X) { return 32 - Clz(X) - 1; }
+inline size_t PageSize() { return 4096; }
+inline uint8_t *RoundUpByPage(uint8_t *P) {
+ uintptr_t X = reinterpret_cast<uintptr_t>(P);
+ size_t Mask = PageSize() - 1;
+ X = (X + Mask) & ~Mask;
+ return reinterpret_cast<uint8_t *>(X);
+}
+inline uint8_t *RoundDownByPage(uint8_t *P) {
+ uintptr_t X = reinterpret_cast<uintptr_t>(P);
+ size_t Mask = PageSize() - 1;
+ X = X & ~Mask;
+ return reinterpret_cast<uint8_t *>(X);
+}
+
} // namespace fuzzer
#endif // LLVM_FUZZER_UTIL_H
diff --git a/lib/fuzzer/FuzzerUtilDarwin.cpp b/lib/fuzzer/FuzzerUtilDarwin.cpp
index 4bfbc11a5..171db2357 100644
--- a/lib/fuzzer/FuzzerUtilDarwin.cpp
+++ b/lib/fuzzer/FuzzerUtilDarwin.cpp
@@ -1,9 +1,8 @@
//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils for Darwin.
diff --git a/lib/fuzzer/FuzzerUtilFuchsia.cpp b/lib/fuzzer/FuzzerUtilFuchsia.cpp
index cd48fefef..7b5c8f647 100644
--- a/lib/fuzzer/FuzzerUtilFuchsia.cpp
+++ b/lib/fuzzer/FuzzerUtilFuchsia.cpp
@@ -1,9 +1,8 @@
//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils implementation using Fuchsia/Zircon APIs.
@@ -288,6 +287,10 @@ void CrashHandler(zx_handle_t *Event) {
} // namespace
+bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite) {
+ return false; // UNIMPLEMENTED
+}
+
// Platform specific functions.
void SetSignalHandler(const FuzzingOptions &Options) {
// Set up alarm handler if needed.
diff --git a/lib/fuzzer/FuzzerUtilLinux.cpp b/lib/fuzzer/FuzzerUtilLinux.cpp
index c103fd230..d5a15d19f 100644
--- a/lib/fuzzer/FuzzerUtilLinux.cpp
+++ b/lib/fuzzer/FuzzerUtilLinux.cpp
@@ -1,9 +1,8 @@
//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils for Linux.
@@ -14,12 +13,18 @@
#include "FuzzerCommand.h"
#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
namespace fuzzer {
int ExecuteCommand(const Command &Cmd) {
std::string CmdLine = Cmd.toString();
- return system(CmdLine.c_str());
+ int exit_code = system(CmdLine.c_str());
+ if (WIFEXITED(exit_code))
+ return WEXITSTATUS(exit_code);
+ return exit_code;
}
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerUtilPosix.cpp b/lib/fuzzer/FuzzerUtilPosix.cpp
index bc64d3293..110785d87 100644
--- a/lib/fuzzer/FuzzerUtilPosix.cpp
+++ b/lib/fuzzer/FuzzerUtilPosix.cpp
@@ -1,9 +1,8 @@
//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils implementation using Posix API.
@@ -12,6 +11,7 @@
#if LIBFUZZER_POSIX
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
+#include "FuzzerTracePC.h"
#include <cassert>
#include <chrono>
#include <cstring>
@@ -19,6 +19,7 @@
#include <iomanip>
#include <signal.h>
#include <stdio.h>
+#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
@@ -32,6 +33,16 @@ static void AlarmHandler(int, siginfo_t *, void *) {
Fuzzer::StaticAlarmCallback();
}
+static void (*upstream_segv_handler)(int, siginfo_t *, void *);
+
+static void SegvHandler(int sig, siginfo_t *si, void *ucontext) {
+ assert(si->si_signo == SIGSEGV);
+ if (TPC.UnprotectLazyCounters(si->si_addr)) return;
+ if (upstream_segv_handler)
+ return upstream_segv_handler(sig, si, ucontext);
+ Fuzzer::StaticCrashSignalCallback();
+}
+
static void CrashHandler(int, siginfo_t *, void *) {
Fuzzer::StaticCrashSignalCallback();
}
@@ -56,8 +67,11 @@ static void SetSigaction(int signum,
exit(1);
}
if (sigact.sa_flags & SA_SIGINFO) {
- if (sigact.sa_sigaction)
- return;
+ if (sigact.sa_sigaction) {
+ if (signum != SIGSEGV)
+ return;
+ upstream_segv_handler = sigact.sa_sigaction;
+ }
} else {
if (sigact.sa_handler != SIG_DFL && sigact.sa_handler != SIG_IGN &&
sigact.sa_handler != SIG_ERR)
@@ -65,6 +79,7 @@ static void SetSigaction(int signum,
}
sigact = {};
+ sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = callback;
if (sigaction(signum, &sigact, 0)) {
Printf("libFuzzer: sigaction failed with %d\n", errno);
@@ -83,6 +98,11 @@ void SetTimer(int Seconds) {
SetSigaction(SIGALRM, AlarmHandler);
}
+bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite) {
+ return 0 == mprotect(Ptr, Size,
+ AllowReadWrite ? (PROT_READ | PROT_WRITE) : PROT_NONE);
+}
+
void SetSignalHandler(const FuzzingOptions& Options) {
if (Options.UnitTimeoutSec > 0)
SetTimer(Options.UnitTimeoutSec / 2 + 1);
@@ -91,7 +111,7 @@ void SetSignalHandler(const FuzzingOptions& Options) {
if (Options.HandleTerm)
SetSigaction(SIGTERM, InterruptHandler);
if (Options.HandleSegv)
- SetSigaction(SIGSEGV, CrashHandler);
+ SetSigaction(SIGSEGV, SegvHandler);
if (Options.HandleBus)
SetSigaction(SIGBUS, CrashHandler);
if (Options.HandleAbrt)
diff --git a/lib/fuzzer/FuzzerUtilWindows.cpp b/lib/fuzzer/FuzzerUtilWindows.cpp
index 393b4768b..074e1eb42 100644
--- a/lib/fuzzer/FuzzerUtilWindows.cpp
+++ b/lib/fuzzer/FuzzerUtilWindows.cpp
@@ -1,9 +1,8 @@
//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Misc utils implementation for Windows.
@@ -86,11 +85,11 @@ void CALLBACK AlarmHandler(PVOID, BOOLEAN) {
class TimerQ {
HANDLE TimerQueue;
public:
- TimerQ() : TimerQueue(NULL) {};
+ TimerQ() : TimerQueue(NULL) {}
~TimerQ() {
if (TimerQueue)
DeleteTimerQueueEx(TimerQueue, NULL);
- };
+ }
void SetTimer(int Seconds) {
if (!TimerQueue) {
TimerQueue = CreateTimerQueue();
@@ -105,13 +104,17 @@ class TimerQ {
Printf("libFuzzer: CreateTimerQueueTimer failed.\n");
exit(1);
}
- };
+ }
};
static TimerQ Timer;
static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); }
+bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite) {
+ return false; // UNIMPLEMENTED
+}
+
void SetSignalHandler(const FuzzingOptions& Options) {
HandlerOpt = &Options;
diff --git a/lib/fuzzer/FuzzerValueBitMap.h b/lib/fuzzer/FuzzerValueBitMap.h
index 13d7cbd95..bc039f1df 100644
--- a/lib/fuzzer/FuzzerValueBitMap.h
+++ b/lib/fuzzer/FuzzerValueBitMap.h
@@ -1,9 +1,8 @@
//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// ValueBitMap.
@@ -35,7 +34,7 @@ struct ValueBitMap {
uintptr_t WordIdx = Idx / kBitsInWord;
uintptr_t BitIdx = Idx % kBitsInWord;
uintptr_t Old = Map[WordIdx];
- uintptr_t New = Old | (1UL << BitIdx);
+ uintptr_t New = Old | (1ULL << BitIdx);
Map[WordIdx] = New;
return New != Old;
}
@@ -49,7 +48,7 @@ struct ValueBitMap {
assert(Idx < kMapSizeInBits);
uintptr_t WordIdx = Idx / kBitsInWord;
uintptr_t BitIdx = Idx % kBitsInWord;
- return Map[WordIdx] & (1UL << BitIdx);
+ return Map[WordIdx] & (1ULL << BitIdx);
}
size_t SizeInBits() const { return kMapSizeInBits; }
@@ -65,7 +64,7 @@ struct ValueBitMap {
}
private:
- uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512)));
+ ATTRIBUTE_ALIGNED(512) uintptr_t Map[kMapSizeInWords];
};
} // namespace fuzzer
diff --git a/lib/fuzzer/afl/afl_driver.cpp b/lib/fuzzer/afl/afl_driver.cpp
index 5a10c0d27..f21dfc58f 100644
--- a/lib/fuzzer/afl/afl_driver.cpp
+++ b/lib/fuzzer/afl/afl_driver.cpp
@@ -1,9 +1,8 @@
//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//===----------------------------------------------------------------------===//
/* This file allows to fuzz libFuzzer-style target functions
@@ -32,32 +31,22 @@ clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o
rm -rf IN OUT; mkdir IN OUT; echo z > IN/z;
$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out
################################################################################
-Environment Variables:
-There are a few environment variables that can be set to use features that
-afl-fuzz doesn't have.
-
AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file
specified. If the file does not exist, it is created. This is useful for getting
-stack traces (when using ASAN for example) or original error messages on hard to
-reproduce bugs.
+stack traces (when using ASAN for example) or original error messages on hard
+to reproduce bugs. Note that any content written to stderr will be written to
+this file instead of stderr's usual location.
-AFL_DRIVER_EXTRA_STATS_FILENAME: Setting this causes afl_driver to write extra
-statistics to the file specified. Currently these are peak_rss_mb
-(the peak amount of virtual memory used in MB) and slowest_unit_time_secs. If
-the file does not exist it is created. If the file does exist then
-afl_driver assumes it was restarted by afl-fuzz and will try to read old
-statistics from the file. If that fails then the process will quit.
+AFL_DRIVER_CLOSE_FD_MASK: Similar to libFuzzer's -close_fd_mask behavior option.
+If 1, close stdout at startup. If 2 close stderr; if 3 close both.
*/
#include <assert.h>
#include <errno.h>
-#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/resource.h>
-#include <sys/time.h>
#include <unistd.h>
#include <fstream>
@@ -99,17 +88,6 @@ statistics from the file. If that fails then the process will quit.
#error "Support for your platform has not been implemented"
#endif
-// Used to avoid repeating error checking boilerplate. If cond is false, a
-// fatal error has occurred in the program. In this event print error_message
-// to stderr and abort(). Otherwise do nothing. Note that setting
-// AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended
-// to the file as well, if the error occurs after the duplication is performed.
-#define CHECK_ERROR(cond, error_message) \
- if (!(cond)) { \
- fprintf(stderr, "%s\n", (error_message)); \
- abort(); \
- }
-
// libFuzzer interface is thin, so we don't include any libFuzzer headers.
extern "C" {
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
@@ -123,24 +101,24 @@ static volatile char suppress_warning2 = AFL_PERSISTENT[0];
// Notify AFL about deferred forkserver.
static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
-extern "C" void __afl_manual_init();
+extern "C" void __afl_manual_init();
static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0];
// Input buffer.
static const size_t kMaxAflInputSize = 1 << 20;
static uint8_t AflInputBuf[kMaxAflInputSize];
-// Variables we need for writing to the extra stats file.
-static FILE *extra_stats_file = NULL;
-static uint32_t previous_peak_rss = 0;
-static time_t slowest_unit_time_secs = 0;
-static const int kNumExtraStats = 2;
-static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n"
- "slowest_unit_time_sec : %u\n";
+// Use this optionally defined function to output sanitizer messages even if
+// user asks to close stderr.
+__attribute__((weak)) extern "C" void __sanitizer_set_report_fd(void *);
+
+// Keep track of where stderr content is being written to, so that
+// dup_and_close_stderr can use the correct one.
+static FILE *output_file = stderr;
// Experimental feature to use afl_driver without AFL's deferred mode.
// Needs to run before __afl_auto_init.
-__attribute__((constructor(0))) void __decide_deferred_forkserver(void) {
+__attribute__((constructor(0))) static void __decide_deferred_forkserver(void) {
if (getenv("AFL_DRIVER_DONT_DEFER")) {
if (unsetenv("__AFL_DEFER_FORKSRV")) {
perror("Failed to unset __AFL_DEFER_FORKSRV");
@@ -149,126 +127,15 @@ __attribute__((constructor(0))) void __decide_deferred_forkserver(void) {
}
}
-// Copied from FuzzerUtil.cpp.
-size_t GetPeakRSSMb() {
- struct rusage usage;
- if (getrusage(RUSAGE_SELF, &usage))
- return 0;
- if (LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD ||
- LIBFUZZER_OPENBSD) {
- // ru_maxrss is in KiB
- return usage.ru_maxrss >> 10;
- } else if (LIBFUZZER_APPLE) {
- // ru_maxrss is in bytes
- return usage.ru_maxrss >> 20;
- }
- assert(0 && "GetPeakRSSMb() is not implemented for your platform");
- return 0;
-}
-
-// Based on SetSigaction in FuzzerUtil.cpp
-static void SetSigaction(int signum,
- void (*callback)(int, siginfo_t *, void *)) {
- struct sigaction sigact;
- memset(&sigact, 0, sizeof(sigact));
- sigact.sa_sigaction = callback;
- if (sigaction(signum, &sigact, 0)) {
- fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno);
- exit(1);
- }
-}
-
-// Write extra stats to the file specified by the user. If none is specified
-// this function will never be called.
-static void write_extra_stats() {
- uint32_t peak_rss = GetPeakRSSMb();
-
- if (peak_rss < previous_peak_rss)
- peak_rss = previous_peak_rss;
-
- int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString,
- peak_rss, slowest_unit_time_secs);
-
- CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file");
-
- CHECK_ERROR(fclose(extra_stats_file) == 0,
- "Failed to close extra_stats_file");
-}
-
-// Call write_extra_stats before we exit.
-static void crash_handler(int, siginfo_t *, void *) {
- // Make sure we don't try calling write_extra_stats again if we crashed while
- // trying to call it.
- static bool first_crash = true;
- CHECK_ERROR(first_crash,
- "Crashed in crash signal handler. This is a bug in the fuzzer.");
-
- first_crash = false;
- write_extra_stats();
-}
-
-// If the user has specified an extra_stats_file through the environment
-// variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up
-// to write stats to it on exit. If no file is specified, do nothing. Otherwise
-// install signal and exit handlers to write to the file when the process exits.
-// Then if the file doesn't exist create it and set extra stats to 0. But if it
-// does exist then read the initial values of the extra stats from the file
-// and check that the file is writable.
-static void maybe_initialize_extra_stats() {
- // If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do.
- char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME");
- if (!extra_stats_filename)
- return;
-
- // Open the file and find the previous peak_rss_mb value.
- // This is necessary because the fuzzing process is restarted after N
- // iterations are completed. So we may need to get this value from a previous
- // process to be accurate.
- extra_stats_file = fopen(extra_stats_filename, "r");
-
- // If extra_stats_file already exists: read old stats from it.
- if (extra_stats_file) {
- int matches = fscanf(extra_stats_file, kExtraStatsFormatString,
- &previous_peak_rss, &slowest_unit_time_secs);
-
- // Make sure we have read a real extra stats file and that we have used it
- // to set slowest_unit_time_secs and previous_peak_rss.
- CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt");
-
- CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file");
-
- // Now open the file for writing.
- extra_stats_file = fopen(extra_stats_filename, "w");
- CHECK_ERROR(extra_stats_file,
- "Failed to open extra stats file for writing");
- } else {
- // Looks like this is the first time in a fuzzing job this is being called.
- extra_stats_file = fopen(extra_stats_filename, "w+");
- CHECK_ERROR(extra_stats_file, "failed to create extra stats file");
- }
-
- // Make sure that crash_handler gets called on any kind of fatal error.
- int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE, SIGINT,
- SIGTERM};
-
- const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]);
-
- for (size_t idx = 0; idx < num_signals; idx++)
- SetSigaction(crash_signals[idx], crash_handler);
-
- // Make sure it gets called on other kinds of exits.
- atexit(write_extra_stats);
-}
-
// If the user asks us to duplicate stderr, then do it.
static void maybe_duplicate_stderr() {
- char* stderr_duplicate_filename =
+ char *stderr_duplicate_filename =
getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
if (!stderr_duplicate_filename)
return;
- FILE* stderr_duplicate_stream =
+ FILE *stderr_duplicate_stream =
freopen(stderr_duplicate_filename, "a+", stderr);
if (!stderr_duplicate_stream) {
@@ -277,6 +144,54 @@ static void maybe_duplicate_stderr() {
"Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
abort();
}
+ output_file = stderr_duplicate_stream;
+}
+
+// Most of these I/O functions were inspired by/copied from libFuzzer's code.
+static void discard_output(int fd) {
+ FILE *temp = fopen("/dev/null", "w");
+ if (!temp)
+ abort();
+ dup2(fileno(temp), fd);
+ fclose(temp);
+}
+
+static void close_stdout() { discard_output(STDOUT_FILENO); }
+
+// Prevent the targeted code from writing to "stderr" but allow sanitizers and
+// this driver to do so.
+static void dup_and_close_stderr() {
+ int output_fileno = fileno(output_file);
+ int output_fd = dup(output_fileno);
+ if (output_fd <= 0)
+ abort();
+ FILE *new_output_file = fdopen(output_fd, "w");
+ if (!new_output_file)
+ abort();
+ if (!__sanitizer_set_report_fd)
+ return;
+ __sanitizer_set_report_fd(reinterpret_cast<void *>(output_fd));
+ discard_output(output_fileno);
+}
+
+static void Printf(const char *Fmt, ...) {
+ va_list ap;
+ va_start(ap, Fmt);
+ vfprintf(output_file, Fmt, ap);
+ va_end(ap);
+ fflush(output_file);
+}
+
+// Close stdout and/or stderr if user asks for it.
+static void maybe_close_fd_mask() {
+ char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK");
+ if (!fd_mask_str)
+ return;
+ int fd_mask = atoi(fd_mask_str);
+ if (fd_mask & 2)
+ dup_and_close_stderr();
+ if (fd_mask & 1)
+ close_stdout();
}
// Define LLVMFuzzerMutate to avoid link failures for targets that use it
@@ -287,7 +202,7 @@ extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
}
// Execute any files provided as parameters.
-int ExecuteFilesOnyByOne(int argc, char **argv) {
+static int ExecuteFilesOnyByOne(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
std::ifstream in(argv[i], std::ios::binary);
in.seekg(0, in.end);
@@ -306,7 +221,7 @@ int ExecuteFilesOnyByOne(int argc, char **argv) {
}
int main(int argc, char **argv) {
- fprintf(stderr,
+ Printf(
"======================= INFO =========================\n"
"This binary is built for AFL-fuzz.\n"
"To run the target function on individual input(s) execute this:\n"
@@ -319,13 +234,13 @@ int main(int argc, char **argv) {
"re-spawning the process (default: 1000)\n"
"======================================================\n",
argv[0], argv[0], argv[0]);
+
+ maybe_duplicate_stderr();
+ maybe_close_fd_mask();
if (LLVMFuzzerInitialize)
LLVMFuzzerInitialize(&argc, &argv);
// Do any other expensive one-time initialization here.
- maybe_duplicate_stderr();
- maybe_initialize_extra_stats();
-
if (!getenv("AFL_DRIVER_DONT_DEFER"))
__afl_manual_init();
@@ -333,8 +248,7 @@ int main(int argc, char **argv) {
if (argc == 2 && argv[1][0] == '-')
N = atoi(argv[1] + 1);
else if(argc == 2 && (N = atoi(argv[1])) > 0)
- fprintf(stderr, "WARNING: using the deprecated call style `%s %d`\n",
- argv[0], N);
+ Printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N);
else if (argc > 1)
return ExecuteFilesOnyByOne(argc, argv);
@@ -345,7 +259,6 @@ int main(int argc, char **argv) {
uint8_t dummy_input[1] = {0};
LLVMFuzzerTestOneInput(dummy_input, 1);
- time_t unit_time_secs;
int num_runs = 0;
while (__afl_persistent_loop(N)) {
ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize);
@@ -354,25 +267,10 @@ int main(int argc, char **argv) {
// overflows. Don't use unique_ptr/etc to avoid extra dependencies.
uint8_t *copy = new uint8_t[n_read];
memcpy(copy, AflInputBuf, n_read);
-
- struct timeval unit_start_time;
- CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0,
- "Calling gettimeofday failed");
-
num_runs++;
LLVMFuzzerTestOneInput(copy, n_read);
-
- struct timeval unit_stop_time;
- CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0,
- "Calling gettimeofday failed");
-
- // Update slowest_unit_time_secs if we see a new max.
- unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec;
- if (slowest_unit_time_secs < unit_time_secs)
- slowest_unit_time_secs = unit_time_secs;
-
delete[] copy;
}
}
- fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs);
+ Printf("%s: successfully executed %d input(s)\n", argv[0], num_runs);
}
diff --git a/lib/fuzzer/dataflow/DataFlow.cpp b/lib/fuzzer/dataflow/DataFlow.cpp
index a79c796ac..187a8e52c 100644
--- a/lib/fuzzer/dataflow/DataFlow.cpp
+++ b/lib/fuzzer/dataflow/DataFlow.cpp
@@ -1,9 +1,8 @@
/*===- DataFlow.cpp - a standalone DataFlow tracer -------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// An experimental data-flow tracer for fuzz targets.
@@ -64,6 +63,9 @@ __attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv);
} // extern "C"
static size_t InputLen;
+static size_t InputLabelBeg;
+static size_t InputLabelEnd;
+static size_t InputSizeLabel;
static size_t NumFuncs;
static const uintptr_t *FuncsBeg;
static __thread size_t CurrentFunc;
@@ -96,8 +98,10 @@ void SetBytesForLabel(dfsan_label L, char *Bytes) {
return;
LabelSeen[L] = true;
assert(L);
- if (L <= InputLen + 1) {
- Bytes[L - 1] = '1';
+ if (L < InputSizeLabel) {
+ Bytes[L + InputLabelBeg - 1] = '1';
+ } else if (L == InputSizeLabel) {
+ Bytes[InputLen] = '1';
} else {
auto *DLI = dfsan_get_label_info(L);
SetBytesForLabel(DLI->l1, Bytes);
@@ -125,9 +129,9 @@ int main(int argc, char **argv) {
if (argc == 1)
return PrintFunctions();
assert(argc == 4 || argc == 5);
- size_t Beg = atoi(argv[1]);
- size_t End = atoi(argv[2]);
- assert(Beg < End);
+ InputLabelBeg = atoi(argv[1]);
+ InputLabelEnd = atoi(argv[2]);
+ assert(InputLabelBeg < InputLabelEnd);
const char *Input = argv[3];
fprintf(stderr, "INFO: reading '%s'\n", Input);
@@ -144,14 +148,16 @@ int main(int argc, char **argv) {
fprintf(stderr, "INFO: running '%s'\n", Input);
for (size_t I = 1; I <= InputLen; I++) {
- dfsan_label L = dfsan_create_label("", nullptr);
- assert(L == I);
size_t Idx = I - 1;
- if (Idx >= Beg && Idx < End)
+ if (Idx >= InputLabelBeg && Idx < InputLabelEnd) {
+ dfsan_label L = dfsan_create_label("", nullptr);
+ assert(L == I - InputLabelBeg);
dfsan_set_label(L, Buf + Idx, 1);
+ }
}
dfsan_label SizeL = dfsan_create_label("", nullptr);
- assert(SizeL == InputLen + 1);
+ InputSizeLabel = SizeL;
+ assert(InputSizeLabel == InputLabelEnd - InputLabelBeg + 1);
dfsan_set_label(SizeL, &InputLen, sizeof(InputLen));
LLVMFuzzerTestOneInput(Buf, InputLen);
diff --git a/lib/fuzzer/scripts/collect_data_flow.py b/lib/fuzzer/scripts/collect_data_flow.py
index 3edff66bb..bd601eb63 100755
--- a/lib/fuzzer/scripts/collect_data_flow.py
+++ b/lib/fuzzer/scripts/collect_data_flow.py
@@ -1,10 +1,9 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#===- lib/fuzzer/scripts/collect_data_flow.py ------------------------------===#
#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
# Runs the data-flow tracer several times on the same input in order to collect
@@ -40,7 +39,9 @@ def collect_dataflow_for_corpus(self, exe, corpus_dir, output_dir):
for root, dirs, files in os.walk(corpus_dir):
for f in files:
path = os.path.join(root, f)
- sha1 = hashlib.sha1(open(path).read()).hexdigest()
+ with open(path, 'rb') as fh:
+ data = fh.read()
+ sha1 = hashlib.sha1(data).hexdigest()
output = os.path.join(output_dir, sha1)
subprocess.call([self, exe, path, output])
functions_txt = open(os.path.join(output_dir, "functions.txt"), "w")
@@ -56,19 +57,19 @@ def main(argv):
q = [[0, size]]
tmpdir = tempfile.mkdtemp(prefix="libfuzzer-tmp-")
atexit.register(cleanup, tmpdir)
- print "tmpdir: ", tmpdir
+ print("tmpdir: ", tmpdir)
outputs = []
while len(q):
r = q.pop()
- print "******* Trying: ", r
+ print("******* Trying: ", r)
tmpfile = os.path.join(tmpdir, str(r[0]) + "-" + str(r[1]))
ret = subprocess.call([exe, str(r[0]), str(r[1]), inp, tmpfile])
if ret and r[1] - r[0] >= 2:
- q.append([r[0], (r[1] + r[0]) / 2])
- q.append([(r[1] + r[0]) / 2, r[1]])
+ q.append([r[0], (r[1] + r[0]) // 2])
+ q.append([(r[1] + r[0]) // 2, r[1]])
else:
outputs.append(tmpfile)
- print "******* Success: ", r
+ print("******* Success: ", r)
f = sys.stdout
if len(argv) >= 4:
f = open(argv[3], "w")
diff --git a/lib/fuzzer/scripts/merge_data_flow.py b/lib/fuzzer/scripts/merge_data_flow.py
index d2f5081e7..b442b89b7 100755
--- a/lib/fuzzer/scripts/merge_data_flow.py
+++ b/lib/fuzzer/scripts/merge_data_flow.py
@@ -1,10 +1,9 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#===- lib/fuzzer/scripts/merge_data_flow.py ------------------------------===#
#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
# Merge several data flow traces into one.
@@ -19,7 +18,7 @@ def Merge(a, b):
res = array('b')
for i in range(0, len(a)):
res.append(ord('1' if a[i] == '1' or b[i] == '1' else '0'))
- return res.tostring()
+ return res.tostring().decode('utf-8')
def main(argv):
D = {}
@@ -30,7 +29,11 @@ def main(argv):
else:
D[F] = BV;
for F in D.keys():
- print("%s %s" % (F, D[F]))
+ if isinstance(D[F], str):
+ value = D[F]
+ else:
+ value = D[F].decode('utf-8')
+ print("%s %s" % (F, value))
if __name__ == '__main__':
main(sys.argv)
diff --git a/lib/fuzzer/scripts/unbalanced_allocs.py b/lib/fuzzer/scripts/unbalanced_allocs.py
index 74478ad55..579e481a2 100755
--- a/lib/fuzzer/scripts/unbalanced_allocs.py
+++ b/lib/fuzzer/scripts/unbalanced_allocs.py
@@ -1,10 +1,9 @@
#!/usr/bin/env python
#===- lib/fuzzer/scripts/unbalanced_allocs.py ------------------------------===#
#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
#
diff --git a/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c b/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c
index 0d76ea49e..efe512cfe 100644
--- a/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c
+++ b/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c
@@ -1,9 +1,8 @@
/*===- StandaloneFuzzTargetMain.c - standalone main() for fuzz targets. ---===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This main() function can be linked to a fuzz target (i.e. a library
@@ -33,6 +32,7 @@ int main(int argc, char **argv) {
fseek(f, 0, SEEK_SET);
unsigned char *buf = (unsigned char*)malloc(len);
size_t n_read = fread(buf, 1, len, f);
+ fclose(f);
assert(n_read == len);
LLVMFuzzerTestOneInput(buf, len);
free(buf);
diff --git a/lib/fuzzer/tests/CMakeLists.txt b/lib/fuzzer/tests/CMakeLists.txt
index 6abb72def..f338d9993 100644
--- a/lib/fuzzer/tests/CMakeLists.txt
+++ b/lib/fuzzer/tests/CMakeLists.txt
@@ -17,16 +17,21 @@ set_target_properties(FuzzerUnitTests PROPERTIES FOLDER "Compiler-RT Tests")
set(LIBFUZZER_UNITTEST_LINK_FLAGS ${COMPILER_RT_UNITTEST_LINK_FLAGS})
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS --driver-mode=g++)
-if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
- list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lc++ -lpthread)
-elseif(NOT WIN32)
- list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++ -lpthread)
+if(NOT WIN32)
+ list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lpthread)
endif()
-if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND COMPILER_RT_LIBCXX_PATH)
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND
+ COMPILER_RT_LIBCXX_PATH AND
+ COMPILER_RT_LIBCXXABI_PATH)
list(APPEND LIBFUZZER_UNITTEST_CFLAGS -nostdinc++)
endif()
+if ("-fvisibility=hidden" IN_LIST LIBFUZZER_CFLAGS)
+ # Match visibility settings.
+ list(APPEND LIBFUZZER_UNITTEST_CFLAGS "-fvisibility=hidden")
+endif()
+
if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH)
# libFuzzer unit tests are only run on the host machine.
set(arch ${COMPILER_RT_DEFAULT_TARGET_ARCH})
@@ -45,8 +50,11 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH)
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
FOLDER "Compiler-RT Runtime tests")
- if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND COMPILER_RT_LIBCXX_PATH)
- set(LIBFUZZER_TEST_RUNTIME_DEPS libcxx_fuzzer_${arch}-build)
+ if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND
+ COMPILER_RT_LIBCXX_PATH AND
+ COMPILER_RT_LIBCXXABI_PATH)
+ file(GLOB libfuzzer_headers ../*.h)
+ set(LIBFUZZER_TEST_RUNTIME_DEPS libcxx_fuzzer_${arch}-build ${libfuzzer_headers})
set(LIBFUZZER_TEST_RUNTIME_CFLAGS -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1)
set(LIBFUZZER_TEST_RUNTIME_LINK_FLAGS ${LIBCXX_${arch}_PREFIX}/lib/libc++.a)
endif()
diff --git a/lib/fuzzer/tests/FuzzerUnittest.cpp b/lib/fuzzer/tests/FuzzerUnittest.cpp
index 7cdd44582..d9bdfc1ef 100644
--- a/lib/fuzzer/tests/FuzzerUnittest.cpp
+++ b/lib/fuzzer/tests/FuzzerUnittest.cpp
@@ -1,5 +1,6 @@
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// Avoid ODR violations (LibFuzzer is built without ASan and this test is built
// with ASan) involving C++ standard library types when using libcxx.
@@ -618,7 +619,7 @@ TEST(Merge, Bad) {
"2\n2\nA\n",
"2\n2\nA\nB\nC\n",
"0\n0\n",
- "1\n1\nA\nDONE 0",
+ "1\n1\nA\nFT 0",
"1\n1\nA\nSTARTED 1",
};
Merger M;
@@ -643,11 +644,9 @@ static void Merge(const std::string &Input,
size_t NumNewFeatures) {
Merger M;
Vector<std::string> NewFiles;
+ Set<uint32_t> NewFeatures, NewCov;
EXPECT_TRUE(M.Parse(Input, true));
- std::stringstream SS;
- M.PrintSummary(SS);
- EXPECT_EQ(NumNewFeatures, M.Merge(&NewFiles));
- EXPECT_EQ(M.AllFeatures(), M.ParseSummary(SS));
+ EXPECT_EQ(NumNewFeatures, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles));
EQ(NewFiles, Result);
}
@@ -671,9 +670,9 @@ TEST(Merge, Good) {
EXPECT_TRUE(M.Parse("3\n1\nAA\nBB\nC\n"
"STARTED 0 1000\n"
- "DONE 0 1 2 3\n"
+ "FT 0 1 2 3\n"
"STARTED 1 1001\n"
- "DONE 1 4 5 6 \n"
+ "FT 1 4 5 6 \n"
"STARTED 2 1002\n"
"", true));
EXPECT_EQ(M.Files.size(), 3U);
@@ -691,11 +690,12 @@ TEST(Merge, Good) {
Vector<std::string> NewFiles;
+ Set<uint32_t> NewFeatures, NewCov;
EXPECT_TRUE(M.Parse("3\n2\nAA\nBB\nC\n"
- "STARTED 0 1000\nDONE 0 1 2 3\n"
- "STARTED 1 1001\nDONE 1 4 5 6 \n"
- "STARTED 2 1002\nDONE 2 6 1 3 \n"
+ "STARTED 0 1000\nFT 0 1 2 3\n"
+ "STARTED 1 1001\nFT 1 4 5 6 \n"
+ "STARTED 2 1002\nFT 2 6 1 3 \n"
"", true));
EXPECT_EQ(M.Files.size(), 3U);
EXPECT_EQ(M.NumFilesInFirstCorpus, 2U);
@@ -704,24 +704,24 @@ TEST(Merge, Good) {
EQ(M.Files[0].Features, {1, 2, 3});
EQ(M.Files[1].Features, {4, 5, 6});
EQ(M.Files[2].Features, {1, 3, 6});
- EXPECT_EQ(0U, M.Merge(&NewFiles));
+ EXPECT_EQ(0U, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles));
EQ(NewFiles, {});
EXPECT_TRUE(M.Parse("3\n1\nA\nB\nC\n"
- "STARTED 0 1000\nDONE 0 1 2 3\n"
- "STARTED 1 1001\nDONE 1 4 5 6 \n"
- "STARTED 2 1002\nDONE 2 6 1 3\n"
+ "STARTED 0 1000\nFT 0 1 2 3\n"
+ "STARTED 1 1001\nFT 1 4 5 6 \n"
+ "STARTED 2 1002\nFT 2 6 1 3\n"
"", true));
EQ(M.Files[0].Features, {1, 2, 3});
EQ(M.Files[1].Features, {4, 5, 6});
EQ(M.Files[2].Features, {1, 3, 6});
- EXPECT_EQ(3U, M.Merge(&NewFiles));
+ EXPECT_EQ(3U, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles));
EQ(NewFiles, {"B"});
// Same as the above, but with InitialFeatures.
EXPECT_TRUE(M.Parse("2\n0\nB\nC\n"
- "STARTED 0 1001\nDONE 0 4 5 6 \n"
- "STARTED 1 1002\nDONE 1 6 1 3\n"
+ "STARTED 0 1001\nFT 0 4 5 6 \n"
+ "STARTED 1 1002\nFT 1 6 1 3\n"
"", true));
EQ(M.Files[0].Features, {4, 5, 6});
EQ(M.Files[1].Features, {1, 3, 6});
@@ -729,36 +729,36 @@ TEST(Merge, Good) {
InitialFeatures.insert(1);
InitialFeatures.insert(2);
InitialFeatures.insert(3);
- EXPECT_EQ(3U, M.Merge(InitialFeatures, &NewFiles));
+ EXPECT_EQ(3U, M.Merge(InitialFeatures, &NewFeatures, {}, &NewCov, &NewFiles));
EQ(NewFiles, {"B"});
}
TEST(Merge, Merge) {
Merge("3\n1\nA\nB\nC\n"
- "STARTED 0 1000\nDONE 0 1 2 3\n"
- "STARTED 1 1001\nDONE 1 4 5 6 \n"
- "STARTED 2 1002\nDONE 2 6 1 3 \n",
+ "STARTED 0 1000\nFT 0 1 2 3\n"
+ "STARTED 1 1001\nFT 1 4 5 6 \n"
+ "STARTED 2 1002\nFT 2 6 1 3 \n",
{"B"}, 3);
Merge("3\n0\nA\nB\nC\n"
- "STARTED 0 2000\nDONE 0 1 2 3\n"
- "STARTED 1 1001\nDONE 1 4 5 6 \n"
- "STARTED 2 1002\nDONE 2 6 1 3 \n",
+ "STARTED 0 2000\nFT 0 1 2 3\n"
+ "STARTED 1 1001\nFT 1 4 5 6 \n"
+ "STARTED 2 1002\nFT 2 6 1 3 \n",
{"A", "B", "C"}, 6);
Merge("4\n0\nA\nB\nC\nD\n"
- "STARTED 0 2000\nDONE 0 1 2 3\n"
- "STARTED 1 1101\nDONE 1 4 5 6 \n"
- "STARTED 2 1102\nDONE 2 6 1 3 100 \n"
- "STARTED 3 1000\nDONE 3 1 \n",
+ "STARTED 0 2000\nFT 0 1 2 3\n"
+ "STARTED 1 1101\nFT 1 4 5 6 \n"
+ "STARTED 2 1102\nFT 2 6 1 3 100 \n"
+ "STARTED 3 1000\nFT 3 1 \n",
{"A", "B", "C", "D"}, 7);
Merge("4\n1\nA\nB\nC\nD\n"
- "STARTED 0 2000\nDONE 0 4 5 6 7 8\n"
- "STARTED 1 1100\nDONE 1 1 2 3 \n"
- "STARTED 2 1100\nDONE 2 2 3 \n"
- "STARTED 3 1000\nDONE 3 1 \n",
+ "STARTED 0 2000\nFT 0 4 5 6 7 8\n"
+ "STARTED 1 1100\nFT 1 1 2 3 \n"
+ "STARTED 2 1100\nFT 2 2 3 \n"
+ "STARTED 3 1000\nFT 3 1 \n",
{"B", "D"}, 3);
}