Skip to content

Commit ec8b03d

Browse files
committed
libfuzzer_driver for fuzzbench
1 parent 0c582d3 commit ec8b03d

File tree

1 file changed

+247
-0
lines changed

1 file changed

+247
-0
lines changed

libfuzzer_driver.cpp

+247
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//===----------------------------------------------------------------------===//
7+
8+
/* This file allows to fuzz libFuzzer-style target functions
9+
(LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode.
10+
11+
Usage:
12+
################################################################################
13+
cat << EOF > test_fuzzer.cc
14+
#include <stddef.h>
15+
#include <stdint.h>
16+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
17+
if (size > 0 && data[0] == 'H')
18+
if (size > 1 && data[1] == 'I')
19+
if (size > 2 && data[2] == '!')
20+
__builtin_trap();
21+
return 0;
22+
}
23+
EOF
24+
# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang.
25+
clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c
26+
# Build afl-llvm-rt.o.c from the AFL distribution.
27+
clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c
28+
# Build this file, link it with afl-llvm-rt.o.o and the target code.
29+
clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o
30+
# Run AFL:
31+
rm -rf IN OUT; mkdir IN OUT; echo z > IN/z;
32+
$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out
33+
################################################################################
34+
AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file
35+
specified. If the file does not exist, it is created. This is useful for getting
36+
stack traces (when using ASAN for example) or original error messages on hard
37+
to reproduce bugs. Note that any content written to stderr will be written to
38+
this file instead of stderr's usual location.
39+
40+
AFL_DRIVER_CLOSE_FD_MASK: Similar to libFuzzer's -close_fd_mask behavior option.
41+
If 1, close stdout at startup. If 2 close stderr; if 3 close both.
42+
43+
*/
44+
#include <assert.h>
45+
#include <errno.h>
46+
#include <stdarg.h>
47+
#include <stdint.h>
48+
#include <stdio.h>
49+
#include <stdlib.h>
50+
#include <string.h>
51+
#include <unistd.h>
52+
53+
#include <fstream>
54+
#include <iostream>
55+
#include <vector>
56+
57+
// Platform detection. Copied from FuzzerInternal.h
58+
#ifdef __linux__
59+
#define LIBFUZZER_LINUX 1
60+
#define LIBFUZZER_APPLE 0
61+
#define LIBFUZZER_NETBSD 0
62+
#define LIBFUZZER_FREEBSD 0
63+
#define LIBFUZZER_OPENBSD 0
64+
#elif __APPLE__
65+
#define LIBFUZZER_LINUX 0
66+
#define LIBFUZZER_APPLE 1
67+
#define LIBFUZZER_NETBSD 0
68+
#define LIBFUZZER_FREEBSD 0
69+
#define LIBFUZZER_OPENBSD 0
70+
#elif __NetBSD__
71+
#define LIBFUZZER_LINUX 0
72+
#define LIBFUZZER_APPLE 0
73+
#define LIBFUZZER_NETBSD 1
74+
#define LIBFUZZER_FREEBSD 0
75+
#define LIBFUZZER_OPENBSD 0
76+
#elif __FreeBSD__
77+
#define LIBFUZZER_LINUX 0
78+
#define LIBFUZZER_APPLE 0
79+
#define LIBFUZZER_NETBSD 0
80+
#define LIBFUZZER_FREEBSD 1
81+
#define LIBFUZZER_OPENBSD 0
82+
#elif __OpenBSD__
83+
#define LIBFUZZER_LINUX 0
84+
#define LIBFUZZER_APPLE 0
85+
#define LIBFUZZER_NETBSD 0
86+
#define LIBFUZZER_FREEBSD 0
87+
#define LIBFUZZER_OPENBSD 1
88+
#else
89+
#error "Support for your platform has not been implemented"
90+
#endif
91+
92+
// libFuzzer interface is thin, so we don't include any libFuzzer headers.
93+
extern "C" {
94+
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
95+
__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv);
96+
}
97+
98+
// Input buffer.
99+
static const size_t kMaxAflInputSize = 1 << 20;
100+
static uint8_t AflInputBuf[kMaxAflInputSize];
101+
102+
// Keep track of where stderr content is being written to, so that
103+
// dup_and_close_stderr can use the correct one.
104+
static FILE *output_file = stderr;
105+
106+
// If the user asks us to duplicate stderr, then do it.
107+
static void maybe_duplicate_stderr() {
108+
char *stderr_duplicate_filename =
109+
getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
110+
111+
if (!stderr_duplicate_filename)
112+
return;
113+
114+
FILE *stderr_duplicate_stream =
115+
freopen(stderr_duplicate_filename, "a+", stderr);
116+
117+
if (!stderr_duplicate_stream) {
118+
fprintf(
119+
stderr,
120+
"Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
121+
abort();
122+
}
123+
output_file = stderr_duplicate_stream;
124+
}
125+
126+
// Most of these I/O functions were inspired by/copied from libFuzzer's code.
127+
static void discard_output(int fd) {
128+
FILE *temp = fopen("/dev/null", "w");
129+
if (!temp)
130+
abort();
131+
dup2(fileno(temp), fd);
132+
fclose(temp);
133+
}
134+
135+
static void close_stdout() { discard_output(STDOUT_FILENO); }
136+
137+
// Prevent the targeted code from writing to "stderr" but allow sanitizers and
138+
// this driver to do so.
139+
static void dup_and_close_stderr() {
140+
int output_fileno = fileno(output_file);
141+
int output_fd = dup(output_fileno);
142+
if (output_fd <= 0)
143+
abort();
144+
FILE *new_output_file = fdopen(output_fd, "w");
145+
if (!new_output_file)
146+
abort();
147+
discard_output(output_fileno);
148+
}
149+
150+
static void Printf(const char *Fmt, ...) {
151+
va_list ap;
152+
va_start(ap, Fmt);
153+
vfprintf(output_file, Fmt, ap);
154+
va_end(ap);
155+
fflush(output_file);
156+
}
157+
158+
// Close stdout and/or stderr if user asks for it.
159+
static void maybe_close_fd_mask() {
160+
char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK");
161+
if (!fd_mask_str)
162+
return;
163+
int fd_mask = atoi(fd_mask_str);
164+
if (fd_mask & 2)
165+
dup_and_close_stderr();
166+
if (fd_mask & 1)
167+
close_stdout();
168+
}
169+
170+
// Define LLVMFuzzerMutate to avoid link failures for targets that use it
171+
// with libFuzzer's LLVMFuzzerCustomMutator.
172+
extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
173+
assert(false && "LLVMFuzzerMutate should not be called from afl_driver");
174+
return 0;
175+
}
176+
177+
// Execute any files provided as parameters.
178+
static int ExecuteFilesOnyByOne(int argc, char **argv) {
179+
for (int i = 1; i < argc; i++) {
180+
std::ifstream in(argv[i], std::ios::binary);
181+
in.seekg(0, in.end);
182+
size_t length = in.tellg();
183+
in.seekg (0, in.beg);
184+
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
185+
// Allocate exactly length bytes so that we reliably catch buffer overflows.
186+
std::vector<char> bytes(length);
187+
in.read(bytes.data(), bytes.size());
188+
assert(in);
189+
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
190+
bytes.size());
191+
std::cout << "Execution successful" << std::endl;
192+
}
193+
return 0;
194+
}
195+
196+
int main(int argc, char **argv) {
197+
Printf(
198+
"======================= INFO =========================\n"
199+
"This binary is built for AFL-fuzz.\n"
200+
"To run the target function on individual input(s) execute this:\n"
201+
" %s < INPUT_FILE\n"
202+
"or\n"
203+
" %s INPUT_FILE1 [INPUT_FILE2 ... ]\n"
204+
"To fuzz with afl-fuzz execute this:\n"
205+
" afl-fuzz [afl-flags] %s [-N]\n"
206+
"afl-fuzz will run N iterations before "
207+
"re-spawning the process (default: 1000)\n"
208+
"======================================================\n",
209+
argv[0], argv[0], argv[0]);
210+
211+
maybe_duplicate_stderr();
212+
maybe_close_fd_mask();
213+
if (LLVMFuzzerInitialize)
214+
LLVMFuzzerInitialize(&argc, &argv);
215+
// Do any other expensive one-time initialization here.
216+
217+
int N = 1000;
218+
if (argc == 2 && argv[1][0] == '-')
219+
N = atoi(argv[1] + 1);
220+
else if(argc == 2 && (N = atoi(argv[1])) > 0)
221+
Printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N);
222+
else if (argc > 1)
223+
return ExecuteFilesOnyByOne(argc, argv);
224+
225+
assert(N > 0);
226+
227+
// Call LLVMFuzzerTestOneInput here so that coverage caused by initialization
228+
// on the first execution of LLVMFuzzerTestOneInput is ignored.
229+
uint8_t dummy_input[1] = {0};
230+
LLVMFuzzerTestOneInput(dummy_input, 1);
231+
232+
int num_runs = 0;
233+
ssize_t n_read = 0;
234+
while (n_read >= 0) {
235+
n_read = read(0, AflInputBuf, kMaxAflInputSize);
236+
if (n_read > 0) {
237+
// Copy AflInputBuf into a separate buffer to let asan find buffer
238+
// overflows. Don't use unique_ptr/etc to avoid extra dependencies.
239+
uint8_t *copy = new uint8_t[n_read];
240+
memcpy(copy, AflInputBuf, n_read);
241+
num_runs++;
242+
LLVMFuzzerTestOneInput(copy, n_read);
243+
delete[] copy;
244+
}
245+
}
246+
Printf("%s: successfully executed %d input(s)\n", argv[0], num_runs);
247+
}

0 commit comments

Comments
 (0)