pkg/afl++: clean up, comments

This commit is contained in:
Mitchell Hashimoto
2026-02-28 20:22:47 -08:00
parent 2a340536a6
commit 54bdbdf87d
3 changed files with 154 additions and 72 deletions

23
pkg/afl++/LICENSE Normal file
View File

@@ -0,0 +1,23 @@
Based on zig-afl-kit: https://github.com/kristoff-it/zig-afl-kit
MIT License
Copyright (c) 2024 Loris Cro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,91 +1,138 @@
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <unistd.h>
/* Main entry point. */
// AFL++ fuzzer harness for Zig fuzz targets.
//
// This file is the C "glue" that connects AFL++'s runtime to Zig-defined
// fuzz test functions. We can't use AFL++'s compiler wrappers (afl-clang,
// afl-gcc) because the code under test is compiled with Zig, so we manually
// expand the AFL macros (__AFL_INIT, __AFL_LOOP, __AFL_FUZZ_INIT, etc.) and
// wire up the sanitizer coverage symbols ourselves.
/* To ensure checks are not optimized out it is recommended to disable
code optimization for the fuzzer harness main() */
// To ensure checks are not optimized out it is recommended to disable
// code optimization for the fuzzer harness main()
#pragma clang optimize off
#pragma GCC optimize("O0")
#pragma GCC optimize("O0")
// Zig integration
// Zig-exported entry points. zig_fuzz_init() performs one-time setup and
// zig_fuzz_test() runs one fuzz iteration on the given input buffer.
// The Zig object should export these.
void zig_fuzz_init();
void zig_fuzz_test(unsigned char *, ssize_t);
void zig_fuzz_test(unsigned char*, ssize_t);
// Linker-provided symbols marking the boundaries of the __sancov_guards section.
// These must be declared extern so the linker provides the actual section boundaries
// from the instrumented code, rather than creating new variables that shadow them.
// On macOS (Mach-O), the linker uses a different naming convention for section
// boundaries than Linux (ELF), so we use asm labels to reference them.
// Linker-provided symbols marking the boundaries of the __sancov_guards
// section. These must be declared extern so the linker provides the actual
// section boundaries from the instrumented code, rather than creating new
// variables that shadow them. On macOS (Mach-O), the linker uses a different
// naming convention for section boundaries than Linux (ELF), so we use asm
// labels to reference them.
#ifdef __APPLE__
extern uint32_t __start___sancov_guards __asm("section$start$__DATA$__sancov_guards");
extern uint32_t __stop___sancov_guards __asm("section$end$__DATA$__sancov_guards");
extern uint32_t __start___sancov_guards __asm(
"section$start$__DATA$__sancov_guards");
extern uint32_t __stop___sancov_guards __asm(
"section$end$__DATA$__sancov_guards");
#else
extern uint32_t __start___sancov_guards;
extern uint32_t __stop___sancov_guards;
#endif
// Provided by afl-compiler-rt; initializes the guard array used by
// SanitizerCoverage's trace-pc-guard instrumentation mode.
void __sanitizer_cov_trace_pc_guard_init(uint32_t*, uint32_t*);
// Stubs for sanitizer coverage callbacks that the Zig-compiled code references
// but AFL's runtime (afl-compiler-rt) does not provide. Without these, linking
// would fail with undefined symbol errors.
__attribute__((visibility("default"))) __attribute__((
tls_model("initial-exec"))) _Thread_local uintptr_t __sancov_lowest_stack;
void __sanitizer_cov_trace_pc_indir() {}
void __sanitizer_cov_8bit_counters_init() {}
void __sanitizer_cov_pcs_init() {}
// Symbols not defined by afl-compiler-rt
__attribute__((visibility("default"))) __attribute__((tls_model("initial-exec"))) _Thread_local uintptr_t __sancov_lowest_stack;
void __sanitizer_cov_trace_pc_indir () {}
void __sanitizer_cov_8bit_counters_init () {}
void __sanitizer_cov_pcs_init () {}
//__AFL_FUZZ_INIT()
// Manual expansion of __AFL_FUZZ_INIT().
//
// Enables shared-memory fuzzing: AFL++ writes test cases directly into
// shared memory (__afl_fuzz_ptr) instead of passing them via stdin, which
// is much faster. When not running under AFL++ (e.g. standalone execution),
// __afl_fuzz_ptr will be NULL and we fall back to reading from stdin into
// __afl_fuzz_alt (a 1 MB static buffer).
int __afl_sharedmem_fuzzing = 1;
extern __attribute__((visibility("default"))) unsigned int *__afl_fuzz_len;
extern __attribute__((visibility("default"))) unsigned char *__afl_fuzz_ptr;
extern __attribute__((visibility("default"))) unsigned int* __afl_fuzz_len;
extern __attribute__((visibility("default"))) unsigned char* __afl_fuzz_ptr;
unsigned char __afl_fuzz_alt[1048576];
unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;
unsigned char* __afl_fuzz_alt_ptr = __afl_fuzz_alt;
int main(int argc, char **argv) {
__sanitizer_cov_trace_pc_guard_init(&__start___sancov_guards, &__stop___sancov_guards);
// __AFL_INIT();
static volatile const char *_A __attribute__((used,unused));
_A = (const char*)"##SIG_AFL_DEFER_FORKSRV##";
int main(int argc, char** argv) {
// Tell AFL's coverage runtime about our guard section so it can track
// which edges in the instrumented Zig code have been hit.
__sanitizer_cov_trace_pc_guard_init(&__start___sancov_guards,
&__stop___sancov_guards);
// Manual expansion of __AFL_INIT() — deferred fork server mode.
//
// The magic string "##SIG_AFL_DEFER_FORKSRV##" is embedded in the binary
// so AFL++'s tooling can detect that this harness uses deferred fork
// server initialization. The `volatile` + `used` attributes prevent the
// compiler/linker from stripping it. We then call __afl_manual_init() to
// start the fork server at this point (after our setup) rather than at
// the very beginning of main().
static volatile const char* _A __attribute__((used, unused));
_A = (const char*)"##SIG_AFL_DEFER_FORKSRV##";
#ifdef __APPLE__
__attribute__((visibility("default")))
void _I(void) __asm__("___afl_manual_init");
__attribute__((visibility("default"))) void _I(void) __asm__(
"___afl_manual_init");
#else
__attribute__((visibility("default")))
void _I(void) __asm__("__afl_manual_init");
#endif
_I();
__attribute__((visibility("default"))) void _I(void) __asm__(
"__afl_manual_init");
#endif
_I();
zig_fuzz_init();
// unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
unsigned char *buf = __afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr;
// while (__AFL_LOOP(UINT_MAX)) {
while (({ static volatile const char *_B __attribute__((used,unused)); _B = (const char*)"##SIG_AFL_PERSISTENT##"; extern __attribute__((visibility("default"))) int __afl_connected;
#ifdef __APPLE__
__attribute__((visibility("default"))) int _L(unsigned int) __asm__("___afl_persistent_loop");
#else
__attribute__((visibility("default"))) int _L(unsigned int) __asm__("__afl_persistent_loop");
#endif
_L(__afl_connected ? UINT_MAX : 1); })) {
// int len = __AFL_FUZZ_TESTCASE_LEN;
int len = __afl_fuzz_ptr ? *__afl_fuzz_len :
(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff ? 0 :
*__afl_fuzz_len;
zig_fuzz_test(buf, len);
// Manual expansion of __AFL_FUZZ_TESTCASE_BUF.
// Use shared memory buffer if available, otherwise fall back to the
// static buffer (for standalone/non-AFL execution).
unsigned char* buf = __afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr;
// Manual expansion of __AFL_LOOP(UINT_MAX) — persistent mode loop.
//
// Persistent mode keeps the process alive across many test cases instead
// of fork()'ing for each one, dramatically improving throughput. The magic
// string "##SIG_AFL_PERSISTENT##" signals to AFL++ that this binary
// supports persistent mode. __afl_persistent_loop() returns non-zero
// while there are more inputs to process.
//
// When connected to AFL++, we loop UINT_MAX times (essentially forever,
// AFL will restart us periodically). When running standalone, we loop
// once so the harness can be used for manual testing/reproduction.
while (({
static volatile const char* _B __attribute__((used, unused));
_B = (const char*)"##SIG_AFL_PERSISTENT##";
extern __attribute__((visibility("default"))) int __afl_connected;
#ifdef __APPLE__
__attribute__((visibility("default"))) int _L(unsigned int) __asm__(
"___afl_persistent_loop");
#else
__attribute__((visibility("default"))) int _L(unsigned int) __asm__(
"__afl_persistent_loop");
#endif
_L(__afl_connected ? UINT_MAX : 1);
})) {
// Manual expansion of __AFL_FUZZ_TESTCASE_LEN.
// In shared-memory mode, the length is provided directly by AFL++.
// In standalone mode, we read from stdin into the fallback buffer.
int len =
__afl_fuzz_ptr ? *__afl_fuzz_len
: (*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff
? 0
: *__afl_fuzz_len;
zig_fuzz_test(buf, len);
}
return 0;

View File

@@ -1,24 +1,36 @@
const std = @import("std");
/// Creates a build step that produces an AFL++-instrumented fuzzing
/// executable.
///
/// Returns a `LazyPath` to the resulting fuzzing executable.
pub fn addInstrumentedExe(
b: *std.Build,
obj: *std.Build.Step.Compile,
) std.Build.LazyPath {
const pkg = b.dependencyFromBuildZig(@This(), .{});
// Force the build system to produce the binary artifact even though we
// only consume the LLVM bitcode below. Without this, the dependency
// tracking doesn't wire up correctly.
_ = obj.getEmittedBin();
const run_afl_cc = b.addSystemCommand(&.{
const pkg = b.dependencyFromBuildZig(
@This(),
.{},
);
const afl_cc = b.addSystemCommand(&.{
b.findProgram(&.{"afl-cc"}, &.{}) catch
@panic("Could not find 'afl-cc', which is required to build"),
"-O3",
});
_ = obj.getEmittedBin(); // hack around build system bug
run_afl_cc.addArg("-o");
const fuzz_exe = run_afl_cc.addOutputFileArg(obj.name);
run_afl_cc.addFileArg(pkg.path("afl.c"));
run_afl_cc.addFileArg(obj.getEmittedLlvmBc());
afl_cc.addArg("-o");
const fuzz_exe = afl_cc.addOutputFileArg(obj.name);
afl_cc.addFileArg(pkg.path("afl.c"));
afl_cc.addFileArg(obj.getEmittedLlvmBc());
return fuzz_exe;
}
// Required so `zig build` works although it does nothing.
pub fn build(b: *std.Build) !void {
_ = b;
}