Skip to content

Commit

Permalink
Merge pull request #79 from fitzgen/custom-mutators
Browse files Browse the repository at this point in the history
Add support for defining custom mutators
  • Loading branch information
fitzgen authored May 13, 2021
2 parents 56ca914 + e80623e commit f6f3f14
Show file tree
Hide file tree
Showing 33 changed files with 739 additions and 583 deletions.
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,14 @@ cc = "1.0"

[features]
arbitrary-derive = ["arbitrary/derive"]

[workspace]
members = [
"./example",
"./example_arbitrary",
"./example_mutator",
]

[dev-dependencies]
flate2 = "1.0.20"
rand = "0.8.3"
17 changes: 17 additions & 0 deletions ci/script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ cd $(dirname $0)/..

export CARGO_TARGET_DIR=$(pwd)/target

cargo test --doc

pushd ./example
cargo rustc \
--release \
Expand Down Expand Up @@ -39,3 +41,18 @@ RUST_LIBFUZZER_DEBUG_PATH=$(pwd)/debug_output \
cat $(pwd)/debug_output
grep -q Rgb $(pwd)/debug_output
popd

pushd ./example_mutator
cargo rustc \
--release \
-- \
-Cpasses='sancov' \
-Cllvm-args=-sanitizer-coverage-level=3 \
-Cllvm-args=-sanitizer-coverage-trace-compares \
-Cllvm-args=-sanitizer-coverage-inline-8bit-counters \
-Cllvm-args=-sanitizer-coverage-stack-depth \
-Cllvm-args=-sanitizer-coverage-trace-geps \
-Cllvm-args=-sanitizer-coverage-prune-blocks=0 \
-Zsanitizer=address
(! $CARGO_TARGET_DIR/release/example_mutator -runs=10000000)
popd
3 changes: 0 additions & 3 deletions example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,5 @@ version = "0.1.0"
authors = ["Simonas Kazlauskas <git@kazlauskas.me>"]
edition = "2018"

[workspace]
members = ["."]

[dependencies]
libfuzzer-sys = { path = ".." }
3 changes: 0 additions & 3 deletions example_arbitrary/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,5 @@ version = "0.1.0"
authors = ["Simonas Kazlauskas <git@kazlauskas.me>"]
edition = "2018"

[workspace]
members = ["."]

[dependencies]
libfuzzer-sys = { path = "..", features = ["arbitrary-derive"] }
1 change: 1 addition & 0 deletions example_mutator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
crash-*
11 changes: 11 additions & 0 deletions example_mutator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "example_mutator"
version = "0.1.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
flate2 = "1.0.20"
libfuzzer-sys = { path = ".." }
55 changes: 55 additions & 0 deletions example_mutator/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#![no_main]

use flate2::{read::GzDecoder, write::GzEncoder, Compression};
use libfuzzer_sys::{fuzz_mutator, fuzz_target};
use std::io::{Read, Write};

fuzz_target!(|data: &[u8]| {
// Decompress the input data and crash if it starts with "boom".
if let Some(data) = decompress(data) {
if data.starts_with(b"boom") {
panic!();
}
}
});

fuzz_mutator!(
|data: &mut [u8], size: usize, max_size: usize, _seed: u32| {
// Decompress the input data. If that fails, use a dummy value.
let mut decompressed = decompress(&data[..size]).unwrap_or_else(|| b"hi".to_vec());

// Mutate the decompressed data with `libFuzzer`'s default mutator. Make
// the `decompressed` vec's extra capacity available for insertion
// mutations via `resize`.
let len = decompressed.len();
let cap = decompressed.capacity();
decompressed.resize(cap, 0);
let new_decompressed_size = libfuzzer_sys::fuzzer_mutate(&mut decompressed, len, cap);

// Recompress the mutated data.
let compressed = compress(&decompressed[..new_decompressed_size]);

// Copy the recompressed mutated data into `data` and return the new size.
let new_size = std::cmp::min(max_size, compressed.len());
data[..new_size].copy_from_slice(&compressed[..new_size]);
new_size
}
);

fn decompress(data: &[u8]) -> Option<Vec<u8>> {
let mut decoder = GzDecoder::new(data);
let mut decompressed = Vec::new();
if decoder.read_to_end(&mut decompressed).is_ok() {
Some(decompressed)
} else {
None
}
}

fn compress(data: &[u8]) -> Vec<u8> {
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
encoder
.write_all(data)
.expect("writing into a vec is infallible");
encoder.finish().expect("writing into a vec is infallible")
}
36 changes: 0 additions & 36 deletions libfuzzer/CREDITS.TXT

This file was deleted.

1 change: 0 additions & 1 deletion libfuzzer/FuzzerBuiltins.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }

inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); }
inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); }
inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); }

} // namespace fuzzer
Expand Down
6 changes: 0 additions & 6 deletions libfuzzer/FuzzerBuiltinsMsvc.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,6 @@ inline uint32_t Clzll(uint64_t X) {
return 64;
}

inline uint32_t Clz(uint32_t X) {
unsigned long LeadZeroIdx = 0;
if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx;
return 32;
}

inline int Popcountll(unsigned long long X) {
#if !defined(_M_ARM) && !defined(_M_X64)
return __popcnt(X) + __popcnt(X >> 32);
Expand Down
38 changes: 22 additions & 16 deletions libfuzzer/FuzzerCorpus.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ struct InputInfo {
// Power schedule.
bool NeedsEnergyUpdate = false;
double Energy = 0.0;
size_t SumIncidence = 0;
double SumIncidence = 0.0;
Vector<std::pair<uint32_t, uint16_t>> FeatureFreqs;

// Delete feature Idx and its frequency from FeatureFreqs.
Expand Down Expand Up @@ -74,27 +74,28 @@ struct InputInfo {
void UpdateEnergy(size_t GlobalNumberOfFeatures, bool ScalePerExecTime,
std::chrono::microseconds AverageUnitExecutionTime) {
Energy = 0.0;
SumIncidence = 0;
SumIncidence = 0.0;

// Apply add-one smoothing to locally discovered features.
for (auto F : FeatureFreqs) {
size_t LocalIncidence = F.second + 1;
Energy -= LocalIncidence * logl(LocalIncidence);
double LocalIncidence = F.second + 1;
Energy -= LocalIncidence * log(LocalIncidence);
SumIncidence += LocalIncidence;
}

// Apply add-one smoothing to locally undiscovered features.
// PreciseEnergy -= 0; // since logl(1.0) == 0)
SumIncidence += (GlobalNumberOfFeatures - FeatureFreqs.size());
// PreciseEnergy -= 0; // since log(1.0) == 0)
SumIncidence +=
static_cast<double>(GlobalNumberOfFeatures - FeatureFreqs.size());

// Add a single locally abundant feature apply add-one smoothing.
size_t AbdIncidence = NumExecutedMutations + 1;
Energy -= AbdIncidence * logl(AbdIncidence);
double AbdIncidence = static_cast<double>(NumExecutedMutations + 1);
Energy -= AbdIncidence * log(AbdIncidence);
SumIncidence += AbdIncidence;

// Normalize.
if (SumIncidence != 0)
Energy = (Energy / SumIncidence) + logl(SumIncidence);
Energy = Energy / SumIncidence + log(SumIncidence);

if (ScalePerExecTime) {
// Scaling to favor inputs with lower execution time.
Expand Down Expand Up @@ -213,6 +214,8 @@ class InputCorpus {
assert(!U.empty());
if (FeatureDebug)
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
// Inputs.size() is cast to uint32_t below.
assert(Inputs.size() < std::numeric_limits<uint32_t>::max());
Inputs.push_back(new InputInfo());
InputInfo &II = *Inputs.back();
II.U = U;
Expand All @@ -224,7 +227,7 @@ class InputCorpus {
II.HasFocusFunction = HasFocusFunction;
// Assign maximal energy to the new seed.
II.Energy = RareFeatures.empty() ? 1.0 : log(RareFeatures.size());
II.SumIncidence = RareFeatures.size();
II.SumIncidence = static_cast<double>(RareFeatures.size());
II.NeedsEnergyUpdate = false;
std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end());
ComputeSHA1(U.data(), U.size(), II.Sha1);
Expand Down Expand Up @@ -399,7 +402,7 @@ class InputCorpus {
// Zero energy seeds will never be fuzzed and remain zero energy.
if (II->Energy > 0.0) {
II->SumIncidence += 1;
II->Energy += logl(II->SumIncidence) / II->SumIncidence;
II->Energy += log(II->SumIncidence) / II->SumIncidence;
}
}

Expand All @@ -426,7 +429,8 @@ class InputCorpus {
NumUpdatedFeatures++;
if (FeatureDebug)
Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize);
SmallestElementPerFeature[Idx] = Inputs.size();
// Inputs.size() is guaranteed to be less than UINT32_MAX by AddToCorpus.
SmallestElementPerFeature[Idx] = static_cast<uint32_t>(Inputs.size());
InputSizesPerFeature[Idx] = NewSize;
return true;
}
Expand Down Expand Up @@ -464,7 +468,7 @@ class InputCorpus {

static const bool FeatureDebug = false;

size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; }
uint32_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; }

void ValidateFeatureSet() {
if (FeatureDebug)
Expand Down Expand Up @@ -539,9 +543,11 @@ class InputCorpus {

if (VanillaSchedule) {
for (size_t i = 0; i < N; i++)
Weights[i] = Inputs[i]->NumFeatures
? (i + 1) * (Inputs[i]->HasFocusFunction ? 1000 : 1)
: 0.;
Weights[i] =
Inputs[i]->NumFeatures
? static_cast<double>((i + 1) *
(Inputs[i]->HasFocusFunction ? 1000 : 1))
: 0.;
}

if (FeatureDebug) {
Expand Down
4 changes: 3 additions & 1 deletion libfuzzer/FuzzerDataFlowTrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ bool BlockCoverage::AppendCoverage(std::istream &IN) {
CoveredBlocks.push_back(BB);
}
if (CoveredBlocks.empty()) return false;
// Ensures no CoverageVector is longer than UINT32_MAX.
uint32_t NumBlocks = CoveredBlocks.back();
CoveredBlocks.pop_back();
for (auto BB : CoveredBlocks)
Expand Down Expand Up @@ -200,7 +201,8 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction,
Printf("INFO: AUTOFOCUS: %zd %s\n", FocusFuncIdx,
FunctionNames[FocusFuncIdx].c_str());
for (size_t i = 0; i < NumFunctions; i++) {
if (!Weights[i]) continue;
if (Weights[i] == 0.0)
continue;
Printf(" [%zd] W %g\tBB-tot %u\tBB-cov %u\tEntryFreq %u:\t%s\n", i,
Weights[i], Coverage.GetNumberOfBlocks(i),
Coverage.GetNumberOfCoveredBlocks(i), Coverage.GetCounter(i, 0),
Expand Down
14 changes: 8 additions & 6 deletions libfuzzer/FuzzerDataFlowTrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,17 @@ int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath,
const Vector<SizedFile> &CorporaFiles);

class BlockCoverage {
public:
public:
// These functions guarantee no CoverageVector is longer than UINT32_MAX.
bool AppendCoverage(std::istream &IN);
bool AppendCoverage(const std::string &S);

size_t NumCoveredFunctions() const { return Functions.size(); }

uint32_t GetCounter(size_t FunctionId, size_t BasicBlockId) {
auto It = Functions.find(FunctionId);
if (It == Functions.end()) return 0;
if (It == Functions.end())
return 0;
const auto &Counters = It->second;
if (BasicBlockId < Counters.size())
return Counters[BasicBlockId];
Expand All @@ -61,7 +63,7 @@ class BlockCoverage {
auto It = Functions.find(FunctionId);
if (It == Functions.end()) return 0;
const auto &Counters = It->second;
return Counters.size();
return static_cast<uint32_t>(Counters.size());
}

uint32_t GetNumberOfCoveredBlocks(size_t FunctionId) {
Expand All @@ -78,8 +80,7 @@ class BlockCoverage {
Vector<double> FunctionWeights(size_t NumFunctions) const;
void clear() { Functions.clear(); }

private:

private:
typedef Vector<uint32_t> CoverageVector;

uint32_t NumberOfCoveredBlocks(const CoverageVector &Counters) const {
Expand All @@ -91,7 +92,8 @@ class BlockCoverage {
}

uint32_t NumberOfUncoveredBlocks(const CoverageVector &Counters) const {
return Counters.size() - NumberOfCoveredBlocks(Counters);
return static_cast<uint32_t>(Counters.size()) -
NumberOfCoveredBlocks(Counters);
}

uint32_t SmallestNonZeroCounter(const CoverageVector &Counters) const {
Expand Down
8 changes: 5 additions & 3 deletions libfuzzer/FuzzerDictionary.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ template <size_t kMaxSizeT> class FixedWord {
public:
static const size_t kMaxSize = kMaxSizeT;
FixedWord() {}
FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); }
FixedWord(const uint8_t *B, size_t S) { Set(B, S); }

void Set(const uint8_t *B, uint8_t S) {
void Set(const uint8_t *B, size_t S) {
static_assert(kMaxSizeT <= std::numeric_limits<uint8_t>::max(),
"FixedWord::kMaxSizeT cannot fit in a uint8_t.");
assert(S <= kMaxSize);
memcpy(Data, B, S);
Size = S;
Size = static_cast<uint8_t>(S);
}

bool operator==(const FixedWord<kMaxSize> &w) const {
Expand Down
Loading

0 comments on commit f6f3f14

Please sign in to comment.