Skip to content

Commit

Permalink
luzer: reserve and handoff ctrs to lf
Browse files Browse the repository at this point in the history
Until now, luzer had not used at all coverage information for
interpreted code. Hook-based instrumentation collected data, but
it were never passed to libfuzzer to drew features from. Memory
always were allocated in a fixed default kMax... size. This
commit includes a fix to properly pass counters to libfuzzer,
two systems to approximate optimal amount of 8-bit counters:
one based on testing, pre-run phase, and one based on active bytecode
size.
Changes to signatures of counter functions help fix bugs with
sign arithmetic.
Also, a minor fix to signal handling and parameter name changes
to evade name shadowing of global variables.

Fixes #12
  • Loading branch information
azanegin committed Jan 28, 2024
1 parent 0179547 commit 9934011
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 41 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Integration with libFuzzer's `FuzzedDataProvider`.
- Examples with tests.
- Documentation with usecases, API etc.
- Two ways to approximate amount of counters for interpreted code.

### Fixed
- Interpreted code counter never handed to libfuzzer. (#12)
- Bad lifetime and initization of struct sigaction.
1 change: 1 addition & 0 deletions luzer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ set(LUZER_SOURCES luzer.c
fuzzed_data_provider.cc
tracer.c
counters.c
io.cc
${CMAKE_CURRENT_BINARY_DIR}/version.c)

add_library(${CMAKE_PROJECT_NAME} SHARED ${LUZER_SOURCES})
Expand Down
36 changes: 19 additions & 17 deletions luzer/counters.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ void __sanitizer_cov_pcs_init(uint8_t* pcs_beg, uint8_t* pcs_end);
} /* extern "C" */
#endif

static const int kDefaultNumCounters = 1 << 20;
static const size_t kDefaultNumCounters = 1 << 20;

// Number of counters requested by Lua instrumentation.
int counter_index = 0;
size_t counter_index = 0;
// Number of counters given to Libfuzzer.
int counter_index_registered = 0;
size_t counter_index_registered = 0;
// Maximum number of counters and pctable entries that may be reserved and also
// the number that are allocated.
int max_counters = 0;
size_t max_counters = 0;
// Counter Allocations. These are allocated once, before __sanitize_... are
// called and can only be deallocated by test_only_reset_counters.
unsigned char* counters = NULL;
Expand All @@ -51,32 +51,34 @@ test_only_reset_counters(void) {
counter_index_registered = 0;
}

NO_SANITIZE int
reserve_counters(int counters) {
NO_SANITIZE size_t
reserve_counters(size_t amount) {
int ret = counter_index;
counter_index += counters;
counter_index += amount;
return ret;
}

NO_SANITIZE int
NO_SANITIZE size_t
reserve_counter(void)
{
return counter_index++;
}

NO_SANITIZE void
increment_counter(int counter_index)
increment_counter(size_t index)
{
if (counters != NULL && pctable != NULL) {
// `counters` is an allocation of length `max_counters`. If we reserve more
// than the allocated number of counters, we'll wrap around and overload
// old counters, trading away fuzzing quality for limits on memory usage.
counters[counter_index % max_counters]++;
if (counters != NULL) {
// Global array `counters` is an allocation of length `max_counters`.
// But we use only registered amount of them.
// If we reserve more than the allocated number of counters, we'll wrap
// around and overload old counters, trading away fuzzing quality
// for limits on memory usage.
counters[index % counter_index_registered]++;
}
}

NO_SANITIZE void
set_max_counters(int max)
set_max_counters(size_t max)
{
if (counters != NULL && pctable != NULL) {
fprintf(stderr, "Internal error: attempt to set max number of counters after "
Expand All @@ -89,7 +91,7 @@ set_max_counters(int max)
max_counters = max;
}

NO_SANITIZE int
NO_SANITIZE size_t
get_max_counters(void)
{
return max_counters;
Expand Down Expand Up @@ -122,7 +124,7 @@ allocate_counters_and_pcs(void) {
}
}

const int next_index = MIN(counter_index, max_counters);
const size_t next_index = MIN(counter_index, max_counters);
if (counter_index_registered >= next_index) {
// There are no counters to pass. Perhaps because we've reserved more than
// max_counters, or because no counters have been reserved since this was
Expand Down
11 changes: 6 additions & 5 deletions luzer/counters.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#ifndef LUZER_COUNTERS_H_
#define LUZER_COUNTERS_H_
#include <stddef.h>

struct PCTableEntry {
void* pc;
Expand All @@ -8,21 +9,21 @@ struct PCTableEntry {

// Sets the global number of counters.
// Must not be called after InitializeCountersWithLLVM is called.
void set_max_counters(int max);
void set_max_counters(size_t max);

// Returns the maximum number of allocatable luzer counters. If more than this
// many counters are reserved, luzer reuses counters, lowering fuzz quality.
int get_max_counters(void);
size_t get_max_counters(void);

// Returns a new counter index.
int reserve_counter(void);
size_t reserve_counter(void);
// Reserves a number of counters with contiguous indices, and returns the first
// index.
int reserve_counters(int counters);
size_t reserve_counters(size_t amount);

// Increments a counter at the given index. If more than the maximum number of
// counters has been reserved, reuse counters.
void increment_counter(int counter_index);
void increment_counter(size_t index);

typedef struct counter_and_pc_table_range {
unsigned char* counters_start;
Expand Down
91 changes: 91 additions & 0 deletions luzer/io.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* SPDX-License-Identifier: ISC
*
*/
#include <vector>
#include <string>
#include <cstdint>
/**
* Okay, we all know this is bad, but unless we want to include third-party
* headers or libs to do crossplatform IO (damn Windows cannot into readdir)
* we better use whatever libfuzzer... shyly gives to us with no guarantees.
* Remember - those things do not have ATTRIBUTE_INTERFACE in LF's codebase.
* Bu-u-u-ut libfuzzer is pretty much in maintenance mode so I think it's
* safe.
* What's worse than using non-public-API is using C++. But this project already
* uses clang++ with 'fuzzed_data_provider.cc'. Hey, libfuzzer IS written in C++.
*/

extern "C" {
#include "macros.h"

int map_over_dir_contents(char const *dirpath, int (*user_cb)(uint8_t const *data, size_t length));
}

/**
* See links for source of this
* https://github.com/llvm/llvm-project/blob/493cc71d72c471c841b490f30dd8f26f3a0d89de/compiler-rt/lib/fuzzer/FuzzerIO.cpp#L101
* https://github.com/llvm/llvm-project/blob/493cc71d72c471c841b490f30dd8f26f3a0d89de/compiler-rt/lib/fuzzer/FuzzerDefs.h#L41
*/
namespace fuzzer {
#if __clang_major__ <= 13
template<typename T>
class fuzzer_allocator: public std::allocator<T> {
public:
fuzzer_allocator() = default;

template<class U>
fuzzer_allocator(const fuzzer_allocator<U>&) {}

template<class Other>
struct rebind { typedef fuzzer_allocator<Other> other; };
};

template<typename T>
using Vector = std::vector<T, fuzzer_allocator<T>>;
#else // __clang_major__ <= 13
template<typename T>
using Vector = std::vector<T>;
#endif

typedef Vector<uint8_t> Unit;

void ReadDirToVectorOfUnits(
const char *Path,
Vector<Unit> *V,
long *Epoch,
size_t MaxSize,
bool ExitOnError,
Vector<std::string> *VPaths = 0
);

bool IsDirectory(const std::string &Path);
}

NO_SANITIZE int
map_over_dir_contents(char const *dirpath, int (*user_cb)(uint8_t const * data, size_t length))
{
if (nullptr == user_cb || nullptr == dirpath) {
return -1;
}

if (!fuzzer::IsDirectory(dirpath)) {
return -2;
}

fuzzer::Vector<fuzzer::Unit> seed_corpus;

fuzzer::ReadDirToVectorOfUnits(
dirpath,
&seed_corpus,
/*Epoch = */nullptr,
/*MaxSize = */SIZE_MAX,
/*ExitOnError = */false,
/*VPaths = */nullptr
);

for (auto unit : seed_corpus) {
user_cb(unit.data(), unit.size());
}
return 0;
}
Loading

0 comments on commit 9934011

Please sign in to comment.