mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-08 16:21:32 +00:00
Merge pull request #2288 from wtdcode/upstream
[RFC] Upstream "SAND: Decoupling Sanitization from Fuzzing for Low Overhead"
This commit is contained in:
commit
6a4b5807b6
@ -230,6 +230,7 @@ Thank you! (For people sending pull requests - please add yourself to this list
|
||||
Ruben ten Hove Joey Jiao
|
||||
fuzzah @intrigus-lgtm
|
||||
Yaakov Saxon Sergej Schumilo
|
||||
Ziqiao Kong
|
||||
```
|
||||
|
||||
</details>
|
||||
|
81
docs/SAND.md
Normal file
81
docs/SAND.md
Normal file
@ -0,0 +1,81 @@
|
||||
# SAND: Decoupling Sanitization from Fuzzing for Low Overhead
|
||||
|
||||
- Authors: Ziqiao Kong, Shaohua Li, Heqing Huang, Zhendong Su
|
||||
- Maintainer: [Ziqiao Kong](https://github.com/wtdcode)
|
||||
- Preprint: [arXiv](https://arxiv.org/abs/2402.16497), accepted by ICSE 2025
|
||||
- Main repo (for paper, reproduction, reference or cite): https://github.com/wtdcode/sand-aflpp
|
||||
|
||||
## Motivation
|
||||
|
||||
SAND introduces a new fuzzing workflow that can greatly reduce (or even eliminate) sanitizer overhead and combine different sanitizers in one fuzzing compaign.
|
||||
|
||||
The key point of SAND is that: sanitizing all inputs is wasting fuzzing power, because bug-triggering inputs are extremely rare (~1%). Obviously, not all inputs worth going through sanitizers. Therefore, if we can somehow "predict" if an input could trigger bugs (defined as "execution pattern"), we could greatly save fuzzing power by only sanitizing a small proportion of all inputs. That's exactly how SAND works.
|
||||
|
||||
## Usage
|
||||
|
||||
For a normal fuzzing workflow, we have:
|
||||
|
||||
1. Build target project with AFL_USE_ASAN=1 to get `target_asan`
|
||||
2. Fuzz the target with `afl-fuzz -i seeds -o out -- ./target_asan`
|
||||
|
||||
For SAND fuzzing workflow, this is slightly different:
|
||||
|
||||
1. Build target project _without_ any sanitizers to get `target_native`, which we will define as a "native binary". It is usually done by using `afl-clang-fast/lto(++)` to compile your project _without_ `AFL_USE_ASAN/UBSAN/MSAN`.
|
||||
2. Build target project with AFL_USE_ASAN=1 AFL_SAN_NO_INST=1 to get `target_asan`. Do note this step can be repeated for multiple sanitizers, like MSAN, UBSAN etc. It is also possible to have ASAN and UBSAN to build together.
|
||||
3. Fuzz the target with `afl-fuzz -i seeds -o out -w ./target_asan -- ./target_native`. Note `-w` can be specified multiple times.
|
||||
|
||||
Then you get:
|
||||
|
||||
- almost the same performance as `afl-fuzz -i seeds -o out -- ./target_native`
|
||||
- and the same bug-finding capability as `afl-fuzz -i seeds -o out -- ./target_asan`
|
||||
|
||||
## Example Workflow
|
||||
|
||||
Take [test-instr.c](../test-instr.c) as an example.
|
||||
|
||||
1. Build the native binary
|
||||
|
||||
```bash
|
||||
afl-clang-fast test-instr.c -o ./native
|
||||
```
|
||||
|
||||
Just like the normal building process, except using `afl-clang-fast`
|
||||
|
||||
2. Build the sanitizers-enabled binaries.
|
||||
|
||||
```bash
|
||||
AFL_SAN_NO_INST=1 AFL_USE_UBSAN=1 AFL_USE_ASAN=1 afl-clang-fast test-instr.c -o ./asanubsan
|
||||
AFL_SAN_NO_INST=1 AFL_USE_MSAN=1 afl-clang-fast test-instr.c -o ./msan
|
||||
```
|
||||
|
||||
Do note `AFL_SAN_NO_INST=1` is crucial, this enables forkservers but disables pc instrumentation. Do not reuse sanitizers-enabled binaries built _without_ `AFL_SAN_NO_INST=1`. This will mess up SAND execution pattern.
|
||||
|
||||
3. Start fuzzing
|
||||
|
||||
```bash
|
||||
mkdir /tmp/test
|
||||
echo "a" > /tmp/test/a
|
||||
AFL_NO_UI=1 AFL_SKIP_CPUFREQ=1 afl-fuzz -i /tmp/test -o /tmp/out -w ./asanubsan -w ./msan -- ./native @@
|
||||
```
|
||||
|
||||
That's it!
|
||||
|
||||
## Tips
|
||||
|
||||
### Alternative execution patterns
|
||||
|
||||
By default, SAND uses the hash value of the simplified coverage map as execution pattern, i.e. if an input has a unique simplefied coverage map, it will be sent to sanitizers for inspection. This shall work for most cases. However, if you are strongly worried about missing bugs, try `AFL_SAN_ABSTRACTION=unique_trace afl-fuzz ...`, which filters inputs having a _unique coverage map_. Do note this significantly increases the number of inputs by 4-10 times, leading to much lower throughput. Alternatively, SAND also supports `AFL_SAN_ABSTRACTION=coverage_increase`, which essentially equals to running sanitizers on the corpus and thus having almost zero overhead, but at a cost of missing ~15% bugs in our evaluation.
|
||||
|
||||
### Run as many sanitizers as possible
|
||||
|
||||
Though we just used ASAN as an example, SAND works best if you provide more sanitizers, for example, UBSAN and MSAN.
|
||||
|
||||
You might do it via `afl-fuzz -i seeds -o out -w ./target_asan -w ./target_msan -w ./target_ubsan -- ./target_native`. Don't worry about the slow sanitizers like MSAN, SAND could still run very fast because only rather a few inputs are sanitized.
|
||||
|
||||
### Bugs types
|
||||
|
||||
The execution pattern evaluated in our papers is targeting the common bugs, as ASAN/MSAN/UBSAN catches. For other bug types, you probably need to define new execution patterns and re-evaluate.
|
||||
|
||||
### My throughput is greatly impacted
|
||||
|
||||
Generally, this is due to too many inputs going through sanitizers, for example, because of unstable targets. You could check stats from `plot_file` to confirm this. Try to switch execution patterns as stated above.
|
@ -203,6 +203,9 @@ instances, so running more than one address sanitized target would be a waste.
|
||||
*IF* you are running a saturated corpus, then you can run up to half of the
|
||||
instances with sanitizers.
|
||||
|
||||
An alternative but more effective approach is to use [SAND](./SAND.md) which could
|
||||
combine different sanitizers at a much higher throughput.
|
||||
|
||||
The following sanitizers have built-in support in AFL++:
|
||||
|
||||
* ASAN = Address SANitizer, finds memory corruption vulnerabilities like
|
||||
|
@ -75,6 +75,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/types.h>
|
||||
#include "asanfuzz.h"
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
|
||||
defined(__NetBSD__) || defined(__DragonFly__)
|
||||
@ -610,7 +611,11 @@ typedef struct afl_state {
|
||||
u8 *var_bytes; /* Bytes that appear to be variable */
|
||||
|
||||
#define N_FUZZ_SIZE (1 << 21)
|
||||
#define N_FUZZ_SIZE_BITMAP (1 << 29)
|
||||
u32 *n_fuzz;
|
||||
u8 *n_fuzz_dup;
|
||||
u8 *classified_n_fuzz;
|
||||
u8 *simplified_n_fuzz;
|
||||
|
||||
volatile u8 stop_soon, /* Ctrl-C pressed? */
|
||||
clear_screen; /* Window resized? */
|
||||
@ -728,6 +733,13 @@ typedef struct afl_state {
|
||||
char *cmplog_binary;
|
||||
afl_forkserver_t cmplog_fsrv; /* cmplog has its own little forkserver */
|
||||
|
||||
/* ASAN Fuzing */
|
||||
char *san_binary[MAX_EXTRA_SAN_BINARY];
|
||||
afl_forkserver_t san_fsrvs[MAX_EXTRA_SAN_BINARY];
|
||||
u8 san_binary_length; /* 0 means extra san binaries not given */
|
||||
u32 san_case_status;
|
||||
enum SanitizerAbstraction san_abstraction;
|
||||
|
||||
/* Custom mutators */
|
||||
struct custom_mutator *mutator;
|
||||
|
||||
|
50
include/asanfuzz.h
Normal file
50
include/asanfuzz.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
american fuzzy lop++ - cmplog header
|
||||
------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de>,
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>,
|
||||
Dominik Maier <mail@dmnk.co>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019-2023 AFLplusplus Project. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Shared code to handle the shared memory. This is used by the fuzzer
|
||||
as well the other components like afl-tmin, afl-showmap, etc...
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _AFL_ASAMFUZZ_H
|
||||
#define _AFL_ASAMFUZZ_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
// new_bits for describe_op
|
||||
// new_bits value 1, 2 and 0x80 are already used!
|
||||
#define SAN_CRASH_ONLY (1 << 4)
|
||||
#define NON_COV_INCREASE_BUG (1 << 5)
|
||||
|
||||
enum SanitizerAbstraction {
|
||||
SIMPLIFY_TRACE = 0, // Feed all unique trace to sanitizers, the most sensitive
|
||||
UNIQUE_TRACE,
|
||||
COVERAGE_INCREASE // Feed all coverage increasing cases to sanitizers, the least sensitive
|
||||
};
|
||||
|
||||
/* Execs the child */
|
||||
|
||||
struct afl_forkserver;
|
||||
void sanfuzz_exec_child(struct afl_forkserver *fsrv, char **argv);
|
||||
|
||||
#endif
|
||||
|
@ -97,6 +97,12 @@
|
||||
/* Maximum allowed fails per CMP value. Default: 96 */
|
||||
#define CMPLOG_FAIL_MAX 96
|
||||
|
||||
/*
|
||||
* Effective fuzzing with selective feeding inputs
|
||||
*/
|
||||
|
||||
#define MAX_EXTRA_SAN_BINARY 4
|
||||
|
||||
/* -------------------------------------*/
|
||||
/* Now non-cmplog configuration options */
|
||||
/* -------------------------------------*/
|
||||
@ -504,6 +510,9 @@
|
||||
|
||||
#define CMPLOG_SHM_ENV_VAR "__AFL_CMPLOG_SHM_ID"
|
||||
|
||||
/* ASAN SHM ID */
|
||||
#define AFL_ASAN_FUZZ_SHM_ENV_VAR "__AFL_ASAN_SHM_ID"
|
||||
|
||||
/* CPU Affinity lockfile env var */
|
||||
|
||||
#define CPU_AFFINITY_ENV_VAR "__AFL_LOCKFILE"
|
||||
|
@ -72,6 +72,22 @@ inline void classify_counts(afl_forkserver_t *fsrv) {
|
||||
|
||||
}
|
||||
|
||||
inline static void classify_counts_mem(u64 *mem, u32 size) {
|
||||
|
||||
u32 i = (size >> 3);
|
||||
|
||||
while (i--) {
|
||||
|
||||
/* Optimize for sparse bitmaps. */
|
||||
|
||||
if (unlikely(*mem)) { *mem = classify_word(*mem); }
|
||||
|
||||
mem++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Updates the virgin bits, then reflects whether a new count or a new tuple is
|
||||
* seen in ret. */
|
||||
inline void discover_word(u8 *ret, u64 *current, u64 *virgin) {
|
||||
|
@ -117,8 +117,8 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_USE_UBSAN", "AFL_UBSAN_VERBOSE", "AFL_USE_TSAN", "AFL_USE_CFISAN",
|
||||
"AFL_CFISAN_VERBOSE", "AFL_USE_LSAN", "AFL_WINE_PATH", "AFL_NO_SNAPSHOT",
|
||||
"AFL_EXPAND_HAVOC_NOW", "AFL_USE_FASAN", "AFL_USE_QASAN",
|
||||
"AFL_PRINT_FILENAMES", "AFL_PIZZA_MODE", "AFL_NO_FASTRESUME", NULL
|
||||
|
||||
"AFL_PRINT_FILENAMES", "AFL_PIZZA_MODE", "AFL_NO_FASTRESUME",
|
||||
"AFL_SAN_ABSTRACTION", "AFL_SAN_NO_INST", "AFL_SAN_RECOVER", NULL
|
||||
};
|
||||
|
||||
extern char *afl_environment_variables[];
|
||||
|
@ -159,6 +159,8 @@ typedef struct afl_forkserver {
|
||||
|
||||
bool debug; /* debug mode? */
|
||||
|
||||
u8 san_but_not_instrumented; /* Is it sanitizer enabled but not instrumented? */
|
||||
|
||||
bool uses_crash_exitcode; /* Custom crash exitcode specified? */
|
||||
u8 crash_exitcode; /* The crash exitcode specified */
|
||||
|
||||
@ -167,6 +169,7 @@ typedef struct afl_forkserver {
|
||||
u8 *shmem_fuzz; /* allocated memory for fuzzing */
|
||||
|
||||
char *cmplog_binary; /* the name of the cmplog binary */
|
||||
char *asanfuzz_binary; /* the name of the ASAN binary */
|
||||
|
||||
/* persistent mode replay functionality */
|
||||
u32 persistent_record; /* persistent replay setting */
|
||||
|
@ -51,6 +51,7 @@ typedef struct sharedmem {
|
||||
size_t map_size; /* actual allocated size */
|
||||
|
||||
int cmplog_mode;
|
||||
int sanfuzz_mode;
|
||||
int shmemfuzz_mode;
|
||||
struct cmp_map *cmp_map;
|
||||
|
||||
|
@ -327,8 +327,14 @@ class ModuleSanitizerCoverageLTOLegacyPass : public ModulePass {
|
||||
|
||||
};
|
||||
|
||||
return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback);
|
||||
|
||||
if (!getenv("AFL_SAN_NO_INST")) {
|
||||
return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback);
|
||||
} else {
|
||||
if (getenv("AFL_DEBUG")) {
|
||||
DEBUGF("Instrument disabled\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -380,8 +386,14 @@ PreservedAnalyses ModuleSanitizerCoverageLTO::run(Module &M,
|
||||
|
||||
};
|
||||
|
||||
if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback))
|
||||
return PreservedAnalyses::none();
|
||||
if (!getenv("AFL_SAN_NO_INST")) {
|
||||
if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback))
|
||||
return PreservedAnalyses::none();
|
||||
} else {
|
||||
if (debug) {
|
||||
DEBUGF("Instrument disabled\n");
|
||||
}
|
||||
}
|
||||
|
||||
return PreservedAnalyses::all();
|
||||
|
||||
|
@ -261,8 +261,16 @@ PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M,
|
||||
|
||||
};
|
||||
|
||||
if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback))
|
||||
return PreservedAnalyses::none();
|
||||
// TODO: Support LTO or llvm classic?
|
||||
// Note we still need afl-compiler-rt so we just disable the instrumentation here.
|
||||
if (!getenv("AFL_SAN_NO_INST")) {
|
||||
if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback))
|
||||
return PreservedAnalyses::none();
|
||||
} else {
|
||||
if (getenv("AFL_DEBUG")) {
|
||||
DEBUGF("Instrument disabled\n");
|
||||
}
|
||||
}
|
||||
return PreservedAnalyses::all();
|
||||
|
||||
}
|
||||
|
@ -223,6 +223,22 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
if (getenv("AFL_DEBUG")) debug = 1;
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
|
||||
if (getenv("AFL_SAN_NO_INST")) {
|
||||
if (debug) {
|
||||
fprintf(stderr, "Intrument disabled\n");
|
||||
}
|
||||
return PreservedAnalyses::all();
|
||||
}
|
||||
#else
|
||||
if (getenv("AFL_SAN_NO_INST")) {
|
||||
if (debug) {
|
||||
fprintf(stderr, "Intrument disabled\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) {
|
||||
|
||||
SAYF(cCYA "afl-llvm-pass" VERSION cRST
|
||||
|
22
src/afl-cc.c
22
src/afl-cc.c
@ -247,6 +247,13 @@ static inline void insert_object(aflcc_state_t *aflcc, u8 *obj, u8 *fmt,
|
||||
/* Insert params into the new argv, make clang load the pass. */
|
||||
static inline void load_llvm_pass(aflcc_state_t *aflcc, u8 *pass) {
|
||||
|
||||
if (getenv("AFL_SAN_NO_INST")) {
|
||||
|
||||
if (!be_quiet) { DEBUGF("SAND: Coverage instrumentation disabled\n"); }
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
#if LLVM_MAJOR >= 11 /* use new pass manager */
|
||||
#if LLVM_MAJOR < 16
|
||||
insert_param(aflcc, "-fexperimental-new-pass-manager");
|
||||
@ -2079,6 +2086,12 @@ void add_native_pcguard(aflcc_state_t *aflcc) {
|
||||
* anyway.
|
||||
*/
|
||||
if (aflcc->have_rust_asanrt) { return; }
|
||||
if (getenv("AFL_SAN_NO_INST")) {
|
||||
|
||||
if (!be_quiet) { DEBUGF("SAND: Coverage instrumentation disabled\n"); }
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/* If llvm-config doesn't figure out LLVM_MAJOR, just
|
||||
go on anyway and let compiler complain if doesn't work. */
|
||||
@ -2091,6 +2104,7 @@ void add_native_pcguard(aflcc_state_t *aflcc) {
|
||||
"pcguard instrumentation with pc-table requires LLVM 6.0.1+"
|
||||
" otherwise the compiler will fail");
|
||||
#endif
|
||||
|
||||
if (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CODECOV) {
|
||||
|
||||
insert_param(aflcc,
|
||||
@ -2113,9 +2127,15 @@ void add_native_pcguard(aflcc_state_t *aflcc) {
|
||||
*/
|
||||
void add_optimized_pcguard(aflcc_state_t *aflcc) {
|
||||
|
||||
if (getenv("AFL_SAN_NO_INST")) {
|
||||
|
||||
if (!be_quiet) { DEBUGF("SAND: Coverage instrumentation disabled\n"); }
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
#if LLVM_MAJOR >= 13
|
||||
#if defined __ANDROID__ || ANDROID
|
||||
|
||||
insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard");
|
||||
aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE;
|
||||
|
||||
|
@ -1918,18 +1918,21 @@ fsrv_run_result_t __attribute__((hot)) afl_fsrv_run_target(
|
||||
must prevent any earlier operations from venturing into that
|
||||
territory. */
|
||||
|
||||
/* If the binary is not instrumented, we don't care about the coverage. Make it a bit faster */
|
||||
if (!fsrv->san_but_not_instrumented) {
|
||||
#ifdef __linux__
|
||||
if (likely(!fsrv->nyx_mode)) {
|
||||
|
||||
memset(fsrv->trace_bits, 0, fsrv->map_size);
|
||||
MEM_BARRIER();
|
||||
memset(fsrv->trace_bits, 0, fsrv->map_size);
|
||||
MEM_BARRIER();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
memset(fsrv->trace_bits, 0, fsrv->map_size);
|
||||
MEM_BARRIER();
|
||||
memset(fsrv->trace_bits, 0, fsrv->map_size);
|
||||
MEM_BARRIER();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* we have the fork server (or faux server) up and running
|
||||
First, tell it if the previous run timed out. */
|
||||
|
@ -25,6 +25,9 @@
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "asanfuzz.h"
|
||||
#if !defined NAME_MAX
|
||||
#define NAME_MAX _XOPEN_NAME_MAX
|
||||
#endif
|
||||
@ -297,6 +300,8 @@ void minimize_bits(afl_state_t *afl, u8 *dst, u8 *src) {
|
||||
u8 *describe_op(afl_state_t *afl, u8 new_bits, size_t max_description_len) {
|
||||
|
||||
u8 is_timeout = 0;
|
||||
u8 san_crash_only = (afl->san_case_status & SAN_CRASH_ONLY);
|
||||
u8 non_cov_incr = (afl->san_case_status & NON_COV_INCREASE_BUG);
|
||||
|
||||
if (new_bits & 0xf0) {
|
||||
|
||||
@ -388,6 +393,10 @@ u8 *describe_op(afl_state_t *afl, u8 new_bits, size_t max_description_len) {
|
||||
|
||||
if (new_bits == 2) { strcat(ret, ",+cov"); }
|
||||
|
||||
if (san_crash_only) { strcat(ret, ",+san"); }
|
||||
|
||||
if (non_cov_incr) { strcat(ret, ",+noncov"); }
|
||||
|
||||
if (unlikely(strlen(ret) >= max_description_len))
|
||||
FATAL("describe string is too long");
|
||||
|
||||
@ -452,6 +461,18 @@ void write_crash_readme(afl_state_t *afl) {
|
||||
|
||||
}
|
||||
|
||||
static inline void bitmap_set(u8 *map, u32 index) {
|
||||
|
||||
map[index / 8] |= (1u << (index % 8));
|
||||
|
||||
}
|
||||
|
||||
static inline u8 bitmap_read(u8 *map, u32 index) {
|
||||
|
||||
return (map[index / 8] >> (index % 8)) & 1;
|
||||
|
||||
}
|
||||
|
||||
/* Check if the result of an execve() during routine fuzzing is interesting,
|
||||
save or queue the input test case for further analysis if so. Returns 1 if
|
||||
entry is saved, 0 otherwise. */
|
||||
@ -484,6 +505,12 @@ u8 __attribute__((hot)) save_if_interesting(afl_state_t *afl, void *mem,
|
||||
need_hash = 1;
|
||||
s32 fd;
|
||||
u64 cksum = 0;
|
||||
u32 cksum_simplified = 0, cksum_unique = 0;
|
||||
u8 san_fault = 0;
|
||||
u8 san_idx = 0;
|
||||
u8 feed_san = 0;
|
||||
|
||||
afl->san_case_status = 0;
|
||||
|
||||
/* Update path frequency. */
|
||||
|
||||
@ -503,30 +530,131 @@ u8 __attribute__((hot)) save_if_interesting(afl_state_t *afl, void *mem,
|
||||
|
||||
}
|
||||
|
||||
/* Only "normal" inputs seem interested to us */
|
||||
if (likely(fault == afl->crash_mode)) {
|
||||
|
||||
if (unlikely(afl->san_binary_length) &&
|
||||
likely(afl->san_abstraction == SIMPLIFY_TRACE)) {
|
||||
|
||||
memcpy(afl->san_fsrvs[0].trace_bits, afl->fsrv.trace_bits,
|
||||
afl->fsrv.map_size);
|
||||
classify_counts_mem((u64 *)afl->san_fsrvs[0].trace_bits,
|
||||
afl->fsrv.map_size);
|
||||
simplify_trace(afl, afl->san_fsrvs[0].trace_bits);
|
||||
|
||||
// Note: Original SAND implementation used XXHASH32
|
||||
cksum_simplified = hash32(afl->san_fsrvs[0].trace_bits,
|
||||
afl->fsrv.map_size, HASH_CONST);
|
||||
|
||||
if (unlikely(!bitmap_read(afl->simplified_n_fuzz, cksum_simplified))) {
|
||||
|
||||
feed_san = 1;
|
||||
bitmap_set(afl->simplified_n_fuzz, cksum_simplified);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (unlikely(afl->san_binary_length) &&
|
||||
unlikely(afl->san_abstraction == COVERAGE_INCREASE)) {
|
||||
|
||||
/* Check if the input increase the coverage */
|
||||
new_bits = has_new_bits_unclassified(afl, afl->virgin_bits);
|
||||
|
||||
if (unlikely(new_bits)) { feed_san = 1; }
|
||||
|
||||
}
|
||||
|
||||
if (unlikely(afl->san_binary_length) &&
|
||||
likely(afl->san_abstraction == UNIQUE_TRACE)) {
|
||||
|
||||
cksum_unique =
|
||||
hash32(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
|
||||
if (unlikely(!bitmap_read(afl->n_fuzz_dup, cksum) &&
|
||||
fault == afl->crash_mode)) {
|
||||
|
||||
feed_san = 1;
|
||||
bitmap_set(afl->n_fuzz_dup, cksum_unique);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (feed_san) {
|
||||
|
||||
/* The input seems interested to other sanitizers, feed it into extra
|
||||
* binaries. */
|
||||
|
||||
for (san_idx = 0; san_idx < afl->san_binary_length; san_idx++) {
|
||||
|
||||
len = write_to_testcase(afl, &mem, len, 0);
|
||||
san_fault = fuzz_run_target(afl, &afl->san_fsrvs[san_idx],
|
||||
afl->san_fsrvs[san_idx].exec_tmout);
|
||||
|
||||
// DEBUGF("ASAN Result: %hhd\n", asan_fault);
|
||||
|
||||
if (unlikely(san_fault && fault == afl->crash_mode)) {
|
||||
|
||||
/* sanitizers discovers distinct bugs! */
|
||||
afl->san_case_status |= SAN_CRASH_ONLY;
|
||||
|
||||
}
|
||||
|
||||
if (san_fault == FSRV_RUN_CRASH) {
|
||||
|
||||
/* Treat this execution as fault detected by ASAN */
|
||||
// fault = san_fault;
|
||||
|
||||
/* That's pretty enough, break to avoid more overhead. */
|
||||
break;
|
||||
|
||||
} else {
|
||||
|
||||
// or keep san_fault as ok
|
||||
san_fault = FSRV_RUN_OK;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* If there is no crash, everything is fine. */
|
||||
if (likely(fault == afl->crash_mode)) {
|
||||
|
||||
/* Keep only if there are new bits in the map, add to queue for
|
||||
future fuzzing, etc. */
|
||||
if (!unlikely(afl->san_abstraction == COVERAGE_INCREASE && feed_san)) {
|
||||
|
||||
if (likely(classified)) {
|
||||
|
||||
new_bits = has_new_bits(afl, afl->virgin_bits);
|
||||
|
||||
} else {
|
||||
|
||||
/* If we are in coverage increasing abstraction and have fed input to
|
||||
sanitizers, we are sure it has new bits.*/
|
||||
new_bits = has_new_bits_unclassified(afl, afl->virgin_bits);
|
||||
|
||||
if (unlikely(new_bits)) { classified = 1; }
|
||||
|
||||
}
|
||||
|
||||
if (likely(!new_bits)) {
|
||||
|
||||
if (unlikely(afl->crash_mode)) { ++afl->total_crashes; }
|
||||
return 0;
|
||||
if (san_fault == FSRV_RUN_OK) {
|
||||
|
||||
if (unlikely(afl->crash_mode)) { ++afl->total_crashes; }
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
afl->san_case_status |= NON_COV_INCREASE_BUG;
|
||||
fault = san_fault;
|
||||
classified = new_bits;
|
||||
goto may_save_fault;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fault = san_fault;
|
||||
classified = new_bits;
|
||||
|
||||
save_to_queue:
|
||||
|
||||
#ifndef SIMPLE_FILES
|
||||
@ -654,6 +782,7 @@ u8 __attribute__((hot)) save_if_interesting(afl_state_t *afl, void *mem,
|
||||
|
||||
}
|
||||
|
||||
may_save_fault:
|
||||
switch (fault) {
|
||||
|
||||
case FSRV_RUN_TMOUT:
|
||||
|
@ -2332,7 +2332,15 @@ void setup_dirs_fds(afl_state_t *afl) {
|
||||
afl->fsrv.plot_file,
|
||||
"# relative_time, cycles_done, cur_item, corpus_count, "
|
||||
"pending_total, pending_favs, map_size, saved_crashes, "
|
||||
"saved_hangs, max_depth, execs_per_sec, total_execs, edges_found\n");
|
||||
"saved_hangs, max_depth, execs_per_sec, total_execs, edges_found, total_crashes, servers_count");
|
||||
|
||||
if (afl->san_binary_length) {
|
||||
for (u8 i = 0; i < afl->san_binary_length; i++) {
|
||||
fprintf(afl->fsrv.plot_file, ", sand_fsrv%u_exec", i);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(afl->fsrv.plot_file, "\n");
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#endif
|
||||
|
||||
#include "cmplog.h"
|
||||
#include "asanfuzz.h"
|
||||
|
||||
#ifdef PROFILING
|
||||
u64 time_spent_working = 0;
|
||||
|
43
src/afl-fuzz-sanfuzz.c
Normal file
43
src/afl-fuzz-sanfuzz.c
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
american fuzzy lop++ - cmplog execution routines
|
||||
------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019-2023 AFLplusplus Project. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Shared code to handle the shared memory. This is used by the fuzzer
|
||||
as well the other components like afl-tmin, afl-showmap, etc...
|
||||
|
||||
*/
|
||||
|
||||
/* This file roughly folows afl-fuzz-asanfuzz */
|
||||
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
void sanfuzz_exec_child(afl_forkserver_t *fsrv, char **argv) {
|
||||
|
||||
if (!fsrv->qemu_mode && !fsrv->frida_mode && argv[0] != fsrv->asanfuzz_binary) {
|
||||
|
||||
argv[0] = fsrv->asanfuzz_binary;
|
||||
|
||||
}
|
||||
|
||||
execv(fsrv->target_path, argv);
|
||||
|
||||
}
|
@ -356,107 +356,122 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
|
||||
1000;
|
||||
if (!runtime_ms) { runtime_ms = 1; }
|
||||
|
||||
fprintf(
|
||||
f,
|
||||
"start_time : %llu\n"
|
||||
"last_update : %llu\n"
|
||||
"run_time : %llu\n"
|
||||
"fuzzer_pid : %u\n"
|
||||
"cycles_done : %llu\n"
|
||||
"cycles_wo_finds : %llu\n"
|
||||
"time_wo_finds : %llu\n"
|
||||
"fuzz_time : %llu\n"
|
||||
"calibration_time : %llu\n"
|
||||
"cmplog_time : %llu\n"
|
||||
"sync_time : %llu\n"
|
||||
"trim_time : %llu\n"
|
||||
"execs_done : %llu\n"
|
||||
"execs_per_sec : %0.02f\n"
|
||||
"execs_ps_last_min : %0.02f\n"
|
||||
"corpus_count : %u\n"
|
||||
"corpus_favored : %u\n"
|
||||
"corpus_found : %u\n"
|
||||
"corpus_imported : %u\n"
|
||||
"corpus_variable : %u\n"
|
||||
"max_depth : %u\n"
|
||||
"cur_item : %u\n"
|
||||
"pending_favs : %u\n"
|
||||
"pending_total : %u\n"
|
||||
"stability : %0.02f%%\n"
|
||||
"bitmap_cvg : %0.02f%%\n"
|
||||
"saved_crashes : %llu\n"
|
||||
"saved_hangs : %llu\n"
|
||||
"last_find : %llu\n"
|
||||
"last_crash : %llu\n"
|
||||
"last_hang : %llu\n"
|
||||
"execs_since_crash : %llu\n"
|
||||
"exec_timeout : %u\n"
|
||||
"slowest_exec_ms : %u\n"
|
||||
"peak_rss_mb : %lu\n"
|
||||
"cpu_affinity : %d\n"
|
||||
"edges_found : %u\n"
|
||||
"total_edges : %u\n"
|
||||
"var_byte_count : %u\n"
|
||||
"havoc_expansion : %u\n"
|
||||
"auto_dict_entries : %u\n"
|
||||
"testcache_size : %llu\n"
|
||||
"testcache_count : %u\n"
|
||||
"testcache_evict : %u\n"
|
||||
"afl_banner : %s\n"
|
||||
"afl_version : " VERSION
|
||||
"\n"
|
||||
"target_mode : %s%s%s%s%s%s%s%s%s%s\n"
|
||||
"command_line : %s\n",
|
||||
(afl->start_time /*- afl->prev_run_time*/) / 1000, cur_time / 1000,
|
||||
runtime_ms / 1000, (u32)getpid(),
|
||||
afl->queue_cycle ? (afl->queue_cycle - 1) : 0, afl->cycles_wo_finds,
|
||||
afl->longest_find_time > cur_time - afl->last_find_time
|
||||
? afl->longest_find_time / 1000
|
||||
: ((afl->start_time == 0 || afl->last_find_time == 0)
|
||||
? 0
|
||||
: (cur_time - afl->last_find_time) / 1000),
|
||||
(runtime_ms - MIN(runtime_ms, overhead_ms)) / 1000,
|
||||
afl->calibration_time_us / 1000000, afl->cmplog_time_us / 1000000,
|
||||
afl->sync_time_us / 1000000, afl->trim_time_us / 1000000,
|
||||
afl->fsrv.total_execs,
|
||||
afl->fsrv.total_execs / ((double)(runtime_ms) / 1000),
|
||||
afl->last_avg_execs_saved, afl->queued_items, afl->queued_favored,
|
||||
afl->queued_discovered, afl->queued_imported, afl->queued_variable,
|
||||
afl->max_depth, afl->current_entry, afl->pending_favored,
|
||||
afl->pending_not_fuzzed, stability, bitmap_cvg, afl->saved_crashes,
|
||||
afl->saved_hangs, afl->last_find_time / 1000, afl->last_crash_time / 1000,
|
||||
afl->last_hang_time / 1000, afl->fsrv.total_execs - afl->last_crash_execs,
|
||||
afl->fsrv.exec_tmout, afl->slowest_exec_ms,
|
||||
fprintf(f,
|
||||
"start_time : %llu\n"
|
||||
"last_update : %llu\n"
|
||||
"run_time : %llu\n"
|
||||
"fuzzer_pid : %u\n"
|
||||
"cycles_done : %llu\n"
|
||||
"cycles_wo_finds : %llu\n"
|
||||
"time_wo_finds : %llu\n"
|
||||
"fuzz_time : %llu\n"
|
||||
"calibration_time : %llu\n"
|
||||
"cmplog_time : %llu\n"
|
||||
"sync_time : %llu\n"
|
||||
"trim_time : %llu\n"
|
||||
"execs_done : %llu\n"
|
||||
"execs_per_sec : %0.02f\n"
|
||||
"execs_ps_last_min : %0.02f\n"
|
||||
"corpus_count : %u\n"
|
||||
"corpus_favored : %u\n"
|
||||
"corpus_found : %u\n"
|
||||
"corpus_imported : %u\n"
|
||||
"corpus_variable : %u\n"
|
||||
"max_depth : %u\n"
|
||||
"cur_item : %u\n"
|
||||
"pending_favs : %u\n"
|
||||
"pending_total : %u\n"
|
||||
"stability : %0.02f%%\n"
|
||||
"bitmap_cvg : %0.02f%%\n"
|
||||
"saved_crashes : %llu\n"
|
||||
"saved_hangs : %llu\n"
|
||||
"total_tmout : %llu\n"
|
||||
"last_find : %llu\n"
|
||||
"last_crash : %llu\n"
|
||||
"last_hang : %llu\n"
|
||||
"execs_since_crash : %llu\n"
|
||||
"exec_timeout : %u\n"
|
||||
"slowest_exec_ms : %u\n"
|
||||
"peak_rss_mb : %lu\n"
|
||||
"cpu_affinity : %d\n"
|
||||
"edges_found : %u\n"
|
||||
"total_edges : %u\n"
|
||||
"var_byte_count : %u\n"
|
||||
"havoc_expansion : %u\n"
|
||||
"auto_dict_entries : %u\n"
|
||||
"testcache_size : %llu\n"
|
||||
"testcache_count : %u\n"
|
||||
"testcache_evict : %u\n"
|
||||
"afl_banner : %s\n"
|
||||
"afl_version : " VERSION
|
||||
"\n"
|
||||
"target_mode : %s%s%s%s%s%s%s%s%s%s\n"
|
||||
"command_line : %s\n",
|
||||
(afl->start_time /*- afl->prev_run_time*/) / 1000, cur_time / 1000,
|
||||
runtime_ms / 1000, (u32)getpid(),
|
||||
afl->queue_cycle ? (afl->queue_cycle - 1) : 0, afl->cycles_wo_finds,
|
||||
afl->longest_find_time > cur_time - afl->last_find_time
|
||||
? afl->longest_find_time / 1000
|
||||
: ((afl->start_time == 0 || afl->last_find_time == 0)
|
||||
? 0
|
||||
: (cur_time - afl->last_find_time) / 1000),
|
||||
(runtime_ms - MIN(runtime_ms, overhead_ms)) / 1000,
|
||||
afl->calibration_time_us / 1000000, afl->cmplog_time_us / 1000000,
|
||||
afl->sync_time_us / 1000000, afl->trim_time_us / 1000000,
|
||||
afl->fsrv.total_execs,
|
||||
afl->fsrv.total_execs / ((double)(runtime_ms) / 1000),
|
||||
afl->last_avg_execs_saved, afl->queued_items, afl->queued_favored,
|
||||
afl->queued_discovered, afl->queued_imported, afl->queued_variable,
|
||||
afl->max_depth, afl->current_entry, afl->pending_favored,
|
||||
afl->pending_not_fuzzed, stability, bitmap_cvg, afl->saved_crashes,
|
||||
afl->saved_hangs, afl->total_tmouts, afl->last_find_time / 1000,
|
||||
afl->last_crash_time / 1000, afl->last_hang_time / 1000,
|
||||
afl->fsrv.total_execs - afl->last_crash_execs, afl->fsrv.exec_tmout,
|
||||
afl->slowest_exec_ms,
|
||||
#ifndef __HAIKU__
|
||||
#ifdef __APPLE__
|
||||
(unsigned long int)(rus.ru_maxrss >> 20),
|
||||
(unsigned long int)(rus.ru_maxrss >> 20),
|
||||
#else
|
||||
(unsigned long int)(rus.ru_maxrss >> 10),
|
||||
(unsigned long int)(rus.ru_maxrss >> 10),
|
||||
#endif
|
||||
#else
|
||||
-1UL,
|
||||
-1UL,
|
||||
#endif
|
||||
#ifdef HAVE_AFFINITY
|
||||
afl->cpu_aff,
|
||||
afl->cpu_aff,
|
||||
#else
|
||||
-1,
|
||||
-1,
|
||||
#endif
|
||||
t_bytes, afl->fsrv.real_map_size, afl->var_byte_count, afl->expand_havoc,
|
||||
afl->a_extras_cnt, afl->q_testcase_cache_size,
|
||||
afl->q_testcase_cache_count, afl->q_testcase_evictions, afl->use_banner,
|
||||
afl->unicorn_mode ? "unicorn" : "", afl->fsrv.qemu_mode ? "qemu " : "",
|
||||
afl->fsrv.cs_mode ? "coresight" : "",
|
||||
afl->non_instrumented_mode ? " non_instrumented " : "",
|
||||
afl->no_forkserver ? "no_fsrv " : "", afl->crash_mode ? "crash " : "",
|
||||
afl->persistent_mode ? "persistent " : "",
|
||||
afl->shmem_testcase_mode ? "shmem_testcase " : "",
|
||||
afl->deferred_mode ? "deferred " : "",
|
||||
(afl->unicorn_mode || afl->fsrv.qemu_mode || afl->fsrv.cs_mode ||
|
||||
afl->non_instrumented_mode || afl->no_forkserver || afl->crash_mode ||
|
||||
afl->persistent_mode || afl->deferred_mode)
|
||||
? ""
|
||||
: "default",
|
||||
afl->orig_cmdline);
|
||||
t_bytes, afl->fsrv.real_map_size, afl->var_byte_count,
|
||||
afl->expand_havoc, afl->a_extras_cnt, afl->q_testcase_cache_size,
|
||||
afl->q_testcase_cache_count, afl->q_testcase_evictions,
|
||||
afl->use_banner, afl->unicorn_mode ? "unicorn" : "",
|
||||
afl->fsrv.qemu_mode ? "qemu " : "",
|
||||
afl->fsrv.cs_mode ? "coresight" : "",
|
||||
afl->non_instrumented_mode ? " non_instrumented " : "",
|
||||
afl->no_forkserver ? "no_fsrv " : "", afl->crash_mode ? "crash " : "",
|
||||
afl->persistent_mode ? "persistent " : "",
|
||||
afl->shmem_testcase_mode ? "shmem_testcase " : "",
|
||||
afl->deferred_mode ? "deferred " : "",
|
||||
(afl->unicorn_mode || afl->fsrv.qemu_mode || afl->fsrv.cs_mode ||
|
||||
afl->non_instrumented_mode || afl->no_forkserver ||
|
||||
afl->crash_mode || afl->persistent_mode || afl->deferred_mode)
|
||||
? ""
|
||||
: "default",
|
||||
afl->orig_cmdline);
|
||||
|
||||
if (afl->san_binary_length) {
|
||||
|
||||
for (u8 i = 0; i < afl->san_binary_length; i++) {
|
||||
|
||||
fprintf(f,
|
||||
"extra_binary : %s\n"
|
||||
"total_execs : %llu\n",
|
||||
afl->san_binary[i], afl->san_fsrvs[i].total_execs);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ignore errors */
|
||||
|
||||
@ -565,12 +580,21 @@ void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
|
||||
|
||||
fprintf(afl->fsrv.plot_file,
|
||||
"%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu, "
|
||||
"%u\n",
|
||||
"%u, %llu, %u",
|
||||
((afl->prev_run_time + get_cur_time() - afl->start_time) / 1000),
|
||||
afl->queue_cycle - 1, afl->current_entry, afl->queued_items,
|
||||
afl->pending_not_fuzzed, afl->pending_favored, bitmap_cvg,
|
||||
afl->saved_crashes, afl->saved_hangs, afl->max_depth, eps,
|
||||
afl->plot_prev_ed, t_bytes); /* ignore errors */
|
||||
afl->plot_prev_ed, t_bytes, afl->total_crashes,
|
||||
(u32)afl->san_binary_length); /* ignore errors */
|
||||
|
||||
for (u32 i = 0; i < afl->san_binary_length; i++) {
|
||||
|
||||
fprintf(afl->fsrv.plot_file, ", %llu", afl->san_fsrvs[i].total_execs);
|
||||
|
||||
}
|
||||
|
||||
fprintf(afl->fsrv.plot_file, "\n");
|
||||
|
||||
fflush(afl->fsrv.plot_file);
|
||||
|
||||
|
183
src/afl-fuzz.c
183
src/afl-fuzz.c
@ -25,7 +25,9 @@
|
||||
*/
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
#include "alloc-inl.h"
|
||||
#include "cmplog.h"
|
||||
#include "asanfuzz.h"
|
||||
#include "common.h"
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
@ -252,13 +254,17 @@ static void usage(u8 *argv0, int more_help) {
|
||||
" X=extreme transform solving, R=random colorization "
|
||||
"bytes.\n\n"
|
||||
"Fuzzing behavior settings:\n"
|
||||
" -Z - sequential queue selection instead of weighted "
|
||||
" -Z - sequential queue selection instead of weighted "
|
||||
"random\n"
|
||||
" -N - do not unlink the fuzzing input file (for devices "
|
||||
" -N - do not unlink the fuzzing input file (for devices "
|
||||
"etc.)\n"
|
||||
" -n - fuzz without instrumentation (non-instrumented mode)\n"
|
||||
" -x dict_file - fuzzer dictionary (see README.md, specify up to 4 "
|
||||
"times)\n\n"
|
||||
" -n - fuzz without instrumentation (non-instrumented "
|
||||
"mode)\n"
|
||||
" -x dict_file - fuzzer dictionary (see README.md, specify up to 4 "
|
||||
"times)\n"
|
||||
" -w san_binary - Specify the extra sanitizer instrumented binaries,\n"
|
||||
" can be specified multiple times.\n"
|
||||
" Read docs/SAND.md for details.\n\n"
|
||||
|
||||
"Test settings:\n"
|
||||
" -s seed - use a fixed seed for the RNG\n"
|
||||
@ -549,6 +555,7 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
u8 mem_limit_given = 0, exit_1 = 0, debug = 0,
|
||||
extras_dir_cnt = 0 /*, have_p = 0*/;
|
||||
char *afl_preload;
|
||||
char *san_abstraction;
|
||||
char *frida_afl_preload = NULL;
|
||||
char **use_argv;
|
||||
|
||||
@ -606,9 +613,10 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
afl->shmem_testcase_mode = 1; // we always try to perform shmem fuzzing
|
||||
|
||||
// still available: HjJkKqruvwz
|
||||
while ((opt = getopt(argc, argv,
|
||||
"+a:Ab:B:c:CdDe:E:f:F:g:G:hi:I:l:L:m:M:nNo:Op:P:QRs:S:t:"
|
||||
"T:uUV:WXx:YzZ")) > 0) {
|
||||
while (
|
||||
(opt = getopt(argc, argv,
|
||||
"+aw:Ab:B:c:CdDe:E:f:F:g:G:hi:I:l:L:m:M:nNo:Op:P:QRs:S:t:"
|
||||
"T:uUV:WXx:YzZ")) > 0) {
|
||||
|
||||
switch (opt) {
|
||||
|
||||
@ -741,6 +749,21 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
case 'w': {
|
||||
|
||||
if (afl->san_binary_length == MAX_EXTRA_SAN_BINARY) {
|
||||
|
||||
FATAL("Only %d extra sanitizer instrumented binaries are supported.",
|
||||
MAX_EXTRA_SAN_BINARY);
|
||||
|
||||
}
|
||||
|
||||
afl->shm.sanfuzz_mode = 1;
|
||||
afl->san_binary[afl->san_binary_length++] = optarg;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 's': {
|
||||
|
||||
if (optarg == NULL) { FATAL("No valid seed provided. Got NULL."); }
|
||||
@ -1727,6 +1750,9 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
afl->n_fuzz_dup = ck_alloc(N_FUZZ_SIZE_BITMAP * sizeof(u8));
|
||||
afl->simplified_n_fuzz = ck_alloc(N_FUZZ_SIZE_BITMAP * sizeof(u8));
|
||||
|
||||
if (get_afl_env("AFL_NO_FORKSRV")) { afl->no_forkserver = 1; }
|
||||
if (get_afl_env("AFL_NO_CPU_RED")) { afl->no_cpu_meter_red = 1; }
|
||||
if (get_afl_env("AFL_NO_ARITH")) { afl->no_arith = 1; }
|
||||
@ -2218,6 +2244,14 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
}
|
||||
|
||||
setup_cmdline_file(afl, argv + optind);
|
||||
|
||||
// Let's check SAND sanitizers binaries a bit earlier
|
||||
// so that we won't overwrite target_path.
|
||||
// Lazymio: why does cmplog fsrv even work?!
|
||||
for (u8 i = 0; i < afl->san_binary_length; i++) {
|
||||
check_binary(afl, afl->san_binary[i]);
|
||||
}
|
||||
|
||||
check_binary(afl, argv[optind]);
|
||||
|
||||
u64 prev_target_hash = 0;
|
||||
@ -2554,6 +2588,129 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
san_abstraction = getenv("AFL_SAN_ABSTRACTION");
|
||||
if (!san_abstraction || !strcmp(san_abstraction, "simplify_trace")) {
|
||||
|
||||
afl->san_abstraction = SIMPLIFY_TRACE;
|
||||
|
||||
} else if (!strcmp(san_abstraction, "coverage_increase")) {
|
||||
|
||||
afl->san_abstraction = COVERAGE_INCREASE;
|
||||
|
||||
} else if (!strcmp(san_abstraction, "unique_trace")) {
|
||||
|
||||
afl->san_abstraction = UNIQUE_TRACE;
|
||||
|
||||
} else {
|
||||
|
||||
WARNF("Unkown abstraction: %s, fallback to unique trace.\n",
|
||||
san_abstraction);
|
||||
afl->san_abstraction = SIMPLIFY_TRACE;
|
||||
|
||||
}
|
||||
|
||||
if (!afl->san_binary_length && san_abstraction) {
|
||||
|
||||
WARNF(
|
||||
"No extra sanitizer instrumented binaries are given, do you forget "
|
||||
"-a?\n");
|
||||
|
||||
}
|
||||
|
||||
/* Maybe merge with cmplog but much cmplog code was already copy-paste
|
||||
* style... */
|
||||
if (afl->san_binary_length) {
|
||||
|
||||
for (u8 i = 0; i < afl->san_binary_length; i++) {
|
||||
|
||||
ACTF("Spawning SAND forkserver for %s", afl->san_binary[i]);
|
||||
afl_fsrv_init_dup(&afl->san_fsrvs[i], &afl->fsrv);
|
||||
|
||||
/*
|
||||
* We don't really collect trace bits for sanitizer instrumented binary so
|
||||
* we just allocate some dummy memory here.
|
||||
*/
|
||||
afl->san_fsrvs[i].trace_bits = ck_alloc(
|
||||
afl->fsrv.map_size + 8); /* One more u64 according to afl_shm_init*/
|
||||
afl->san_fsrvs[i].map_size = afl->fsrv.map_size;
|
||||
afl->san_fsrvs[i].san_but_not_instrumented = 1;
|
||||
|
||||
afl->san_fsrvs[i].cs_mode = afl->fsrv.cs_mode;
|
||||
afl->san_fsrvs[i].qemu_mode = afl->fsrv.qemu_mode;
|
||||
afl->san_fsrvs[i].frida_mode = afl->fsrv.frida_mode;
|
||||
afl->san_fsrvs[i].asanfuzz_binary = afl->san_binary[i];
|
||||
afl->san_fsrvs[i].target_path = afl->san_binary[i];
|
||||
afl->san_fsrvs[i].init_child_func = sanfuzz_exec_child;
|
||||
|
||||
afl->san_fsrvs[i].child_kill_signal =
|
||||
afl->fsrv.child_kill_signal; // I believe cmplog also needs this.
|
||||
afl->san_fsrvs[i].fsrv_kill_signal = afl->fsrv.fsrv_kill_signal;
|
||||
|
||||
if ((map_size <= DEFAULT_SHMEM_SIZE ||
|
||||
afl->san_fsrvs[i].map_size < map_size) &&
|
||||
!afl->non_instrumented_mode && !afl->fsrv.qemu_mode &&
|
||||
!afl->fsrv.frida_mode && !afl->unicorn_mode && !afl->fsrv.cs_mode &&
|
||||
!afl->afl_env.afl_skip_bin_check) {
|
||||
|
||||
afl->san_fsrvs[i].map_size = MAX(map_size, (u32)DEFAULT_SHMEM_SIZE);
|
||||
char vbuf[16];
|
||||
snprintf(vbuf, sizeof(vbuf), "%u", afl->san_fsrvs[i].map_size);
|
||||
setenv("AFL_MAP_SIZE", vbuf, 1);
|
||||
|
||||
}
|
||||
|
||||
u32 new_map_size =
|
||||
afl_fsrv_get_mapsize(&afl->san_fsrvs[i], afl->argv, &afl->stop_soon,
|
||||
afl->afl_env.afl_debug_child);
|
||||
|
||||
// only reinitialize when it needs to be larger
|
||||
if (map_size < new_map_size) {
|
||||
|
||||
OKF("Re-initializing maps to %u bytes due to SAN instrumented binary",
|
||||
new_map_size);
|
||||
|
||||
afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size);
|
||||
afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size);
|
||||
afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size);
|
||||
afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size);
|
||||
afl->top_rated =
|
||||
ck_realloc(afl->top_rated, new_map_size * sizeof(void *));
|
||||
afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size);
|
||||
afl->clean_trace_custom =
|
||||
ck_realloc(afl->clean_trace_custom, new_map_size);
|
||||
afl->first_trace = ck_realloc(afl->first_trace, new_map_size);
|
||||
afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size);
|
||||
|
||||
afl_fsrv_kill(&afl->fsrv);
|
||||
afl_fsrv_kill(&afl->san_fsrvs[i]);
|
||||
afl_shm_deinit(&afl->shm);
|
||||
|
||||
afl->san_fsrvs[i].map_size = new_map_size; // non-cmplog stays the same
|
||||
map_size = new_map_size;
|
||||
|
||||
setenv("AFL_NO_AUTODICT", "1", 1); // loaded already
|
||||
afl->fsrv.trace_bits =
|
||||
afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode);
|
||||
ck_free(afl->san_fsrvs[i].trace_bits);
|
||||
afl->san_fsrvs[i].trace_bits = ck_alloc(afl->fsrv.map_size + 8);
|
||||
afl->san_fsrvs[i].map_size = afl->fsrv.map_size;
|
||||
afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon,
|
||||
afl->afl_env.afl_debug_child);
|
||||
afl_fsrv_start(&afl->san_fsrvs[i], afl->argv, &afl->stop_soon,
|
||||
afl->afl_env.afl_debug_child);
|
||||
|
||||
}
|
||||
|
||||
OKF("SAND forkserver for %s successfully started", afl->san_binary[i]);
|
||||
|
||||
}
|
||||
|
||||
OKF("All forkservers for extra sanitizers instrumented binares are up and "
|
||||
"we have abstraction = %d",
|
||||
afl->san_abstraction);
|
||||
|
||||
}
|
||||
|
||||
if (afl->cmplog_binary) {
|
||||
|
||||
ACTF("Spawning cmplog forkserver");
|
||||
@ -3450,6 +3607,16 @@ stop_fuzzing:
|
||||
|
||||
afl_fsrv_deinit(&afl->fsrv);
|
||||
|
||||
for (u8 i = 0; i < afl->san_binary_length; i++) {
|
||||
|
||||
ck_free(afl->san_fsrvs[i].trace_bits);
|
||||
afl_fsrv_deinit(&afl->san_fsrvs[i]);
|
||||
}
|
||||
|
||||
if (afl->cmplog_binary) {
|
||||
afl_fsrv_deinit(&afl->cmplog_fsrv);
|
||||
}
|
||||
|
||||
/* remove tmpfile */
|
||||
if (!afl->in_place_resume && afl->fsrv.out_file) {
|
||||
|
||||
|
@ -424,4 +424,3 @@ char *sha1_hex_for_file(const char *fname, u32 len) {
|
||||
return hex;
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user