72 changes: 61 additions & 11 deletions llvm/lib/Fuzzer/FuzzerLoop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,57 @@

namespace fuzzer {

// static
Unit Fuzzer::CurrentUnit;
system_clock::time_point Fuzzer::UnitStartTime;
// Only one Fuzzer per process.
static Fuzzer *F;

Fuzzer::Fuzzer(UserCallback Callback, FuzzingOptions Options)
: Callback(Callback), Options(Options) {
SetDeathCallback();
InitializeDFSan();
assert(!F);
F = this;
}

void Fuzzer::SetDeathCallback() {
__sanitizer_set_death_callback(DeathCallback);
__sanitizer_set_death_callback(StaticDeathCallback);
}

void Fuzzer::PrintUnitInASCIIOrTokens(const Unit &U, const char *PrintAfter) {
if (Options.Tokens.empty()) {
PrintASCII(U, PrintAfter);
} else {
auto T = SubstituteTokens(U);
T.push_back(0);
std::cerr << T.data();
std::cerr << PrintAfter;
}
}

void Fuzzer::StaticDeathCallback() {
assert(F);
F->DeathCallback();
}

void Fuzzer::DeathCallback() {
std::cerr << "DEATH: " << std::endl;
Print(CurrentUnit, "\n");
PrintASCII(CurrentUnit, "\n");
PrintUnitInASCIIOrTokens(CurrentUnit, "\n");
WriteToCrash(CurrentUnit, "crash-");
}

void Fuzzer::StaticAlarmCallback() {
assert(F);
F->AlarmCallback();
}

void Fuzzer::AlarmCallback() {
size_t Seconds =
duration_cast<seconds>(system_clock::now() - UnitStartTime).count();
std::cerr << "ALARM: working on the last Unit for " << Seconds << " seconds"
<< std::endl;
if (Seconds >= 3) {
Print(CurrentUnit, "\n");
PrintASCII(CurrentUnit, "\n");
PrintUnitInASCIIOrTokens(CurrentUnit, "\n");
WriteToCrash(CurrentUnit, "timeout-");
}
exit(1);
Expand Down Expand Up @@ -123,12 +151,35 @@ static uintptr_t HashOfArrayOfPCs(uintptr_t *PCs, uintptr_t NumPCs) {
return Res;
}

Unit Fuzzer::SubstituteTokens(const Unit &U) const {
Unit Res;
for (auto Idx : U) {
if (Idx < Options.Tokens.size()) {
std::string Token = Options.Tokens[Idx];
Res.insert(Res.end(), Token.begin(), Token.end());
} else {
Res.push_back(' ');
}
}
// FIXME: Apply DFSan labels.
return Res;
}

void Fuzzer::ExecuteCallback(const Unit &U) {
if (Options.Tokens.empty()) {
Callback(U.data(), U.size());
} else {
auto T = SubstituteTokens(U);
Callback(T.data(), T.size());
}
}

// Experimental. Does not yet scale.
// Fuly reset the current coverage state, run a single unit,
// collect all coverage pairs and return non-zero if a new pair is observed.
size_t Fuzzer::RunOneMaximizeCoveragePairs(const Unit &U) {
__sanitizer_reset_coverage();
Callback(U.data(), U.size());
ExecuteCallback(U);
uintptr_t *PCs;
uintptr_t NumPCs = __sanitizer_get_coverage_guards(&PCs);
bool HasNewPairs = false;
Expand All @@ -153,7 +204,7 @@ size_t Fuzzer::RunOneMaximizeCoveragePairs(const Unit &U) {
// e.g. test/FullCoverageSetTest.cpp. FIXME: make it scale.
size_t Fuzzer::RunOneMaximizeFullCoverageSet(const Unit &U) {
__sanitizer_reset_coverage();
Callback(U.data(), U.size());
ExecuteCallback(U);
uintptr_t *PCs;
uintptr_t NumPCs =__sanitizer_get_coverage_guards(&PCs);
if (FullCoverageSets.insert(HashOfArrayOfPCs(PCs, NumPCs)).second)
Expand All @@ -168,7 +219,7 @@ size_t Fuzzer::RunOneMaximizeTotalCoverage(const Unit &U) {
__sanitizer_update_counter_bitset_and_clear_counters(0);
}
size_t OldCoverage = __sanitizer_get_total_unique_coverage();
Callback(U.data(), U.size());
ExecuteCallback(U);
size_t NewCoverage = __sanitizer_get_total_unique_coverage();
size_t NumNewBits = 0;
if (Options.UseCounters)
Expand Down Expand Up @@ -222,8 +273,7 @@ size_t Fuzzer::MutateAndTestOne(Unit *U) {
std::cerr << " L: " << U->size();
if (U->size() < 30) {
std::cerr << " ";
PrintASCII(*U);
std::cerr << "\t";
PrintUnitInASCIIOrTokens(*U, "\t");
Print(*U);
}
std::cerr << "\n";
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Fuzzer/FuzzerUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ std::string Hash(const Unit &in) {
}

static void AlarmHandler(int, siginfo_t *, void *) {
Fuzzer::AlarmCallback();
Fuzzer::StaticAlarmCallback();
}

void SetTimer(int Seconds) {
Expand Down
218 changes: 218 additions & 0 deletions llvm/lib/Fuzzer/cxx_fuzzer_tokens.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
#
##
`
~
!
@
$
%
^
&
*
(
)
_
-
_
=
+
{
}
[
]
|
\
,
.
/
?
>
<
;
:
'
"
++
--
<<
>>
+=
-=
*=
/=
>>=
<<=
&=
|=
^=
%=
!=
&&
||
==
>=
<=
->
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
alignas
alignof
and
and_eq
asm
auto
bitand
bitor
bool
break
case
catch
char
char16_t
char32_t
class
compl
concept
const
constexpr
const_cast
continue
decltype
default
delete
do
double
dynamic_cast
else
enum
explicit
export
extern
false
float
for
friend
goto
if
inline
int
long
mutable
namespace
new
noexcept
not
not_eq
nullptr
operator
or
or_eq
private
protected
public
register
reinterpret_cast
requires
return
short
signed
sizeof
static
static_assert
static_cast
struct
switch
template
this
thread_local
throw
true
try
typedef
typeid
typename
union
unsigned
using
virtual
void
volatile
wchar_t
while
xor
xor_eq
if
elif
else
endif
defined
ifdef
ifndef
define
undef
include
line
error
pragma
override
final
1 change: 1 addition & 0 deletions llvm/lib/Fuzzer/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(CMAKE_CXX_FLAGS_RELEASE "${LIBFUZZER_FLAGS_BASE} -O0 -fsanitize-coverage=4")

set(Tests
CounterTest
CxxTokensTest
FourIndependentBranchesTest
FullCoverageSetTest
InfiniteTest
Expand Down
24 changes: 24 additions & 0 deletions llvm/lib/Fuzzer/test/CxxTokensTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Simple test for a fuzzer. The fuzzer must find a sequence of C++ tokens.
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <cstring>
#include <iostream>

static void Found() {
std::cout << "Found the target, exiting\n";
exit(1);
}

extern "C" void TestOneInput(const uint8_t *Data, size_t Size) {
// looking for "thread_local unsigned A;"
if (Size < 24) return;
if (0 == memcmp(&Data[0], "thread_local", 12))
if (Data[12] == ' ')
if (0 == memcmp(&Data[13], "unsigned", 8))
if (Data[21] == ' ')
if (Data[22] == 'A')
if (Data[23] == ';')
Found();
}

3 changes: 3 additions & 0 deletions llvm/lib/Fuzzer/test/fuzzer.test
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ CounterTest: BINGO

RUN: not ./LLVMFuzzer-DFSanSimpleCmpTest -seed=1 -timeout=15 2>&1 | FileCheck %s --check-prefix=DFSanSimpleCmpTest
DFSanSimpleCmpTest: Found the target:

RUN: not ./LLVMFuzzer-CxxTokensTest -seed=1 -timeout=15 -tokens=%S/../cxx_fuzzer_tokens.txt 2>&1 | FileCheck %s --check-prefix=CxxTokensTest
CxxTokensTest: Found the target, exiting