blob: 784a6120d65a7634305aef6fb267c7144d123949 [file]
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
#include "absl/log/check.h"
#include "absl/random/bit_gen_ref.h"
#include "absl/random/random.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "./fuzztest/fuzztest.h"
#include "./fuzztest/internal/domains/arbitrary_impl.h"
#include "./fuzztest/internal/domains/container_of_impl.h"
#include "./fuzztest/internal/domains/domain_base.h"
#include "./fuzztest/internal/io.h"
#include "re2/re2.h"
ABSL_DECLARE_FLAG(std::string, llvm_fuzzer_wrapper_dict_file);
ABSL_DECLARE_FLAG(std::string, llvm_fuzzer_wrapper_corpus_dir);
constexpr static size_t kByteArrayMaxLen = 4096;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
extern "C" size_t __attribute__((weak))
LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max_size,
unsigned int seed);
// TODO(b/303267857): Migrate fuzz targets that use this feature manually.
// `LLVMFuzzerCustomCrossOver` is defined to produce a link-error (duplicate
// definition) if it's also defined by user.
extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t* data1, size_t size1,
const uint8_t* data2, size_t size2,
uint8_t* out, size_t max_out_size,
unsigned int seed) {
std::cerr << "LLVMFuzzerCustomCrossOver is not supported in FuzzTest\n";
exit(-1);
}
std::vector<std::vector<uint8_t>> ReadByteArraysFromDirectory() {
const std::string flag = absl::GetFlag(FLAGS_llvm_fuzzer_wrapper_corpus_dir);
if (flag.empty()) return {};
std::vector<fuzztest::internal::FilePathAndData> files =
fuzztest::internal::ReadFileOrDirectory(flag);
std::vector<std::vector<uint8_t>> out;
out.reserve(files.size());
for (const fuzztest::internal::FilePathAndData& file : files) {
out.push_back(
{file.data.begin(),
file.data.begin() + std::min(file.data.size(), kByteArrayMaxLen)});
}
return out;
}
std::vector<std::vector<uint8_t>> ReadByteArrayDictionaryFromFile() {
const std::string flag = absl::GetFlag(FLAGS_llvm_fuzzer_wrapper_dict_file);
if (flag.empty()) return {};
std::vector<fuzztest::internal::FilePathAndData> files =
fuzztest::internal::ReadFileOrDirectory(flag);
std::vector<std::vector<uint8_t>> out;
out.reserve(files.size());
// Dictionary must be in the format specified at
// https://llvm.org/docs/LibFuzzer.html#dictionaries
constexpr absl::string_view kLineRegex =
"[^\\\"]*" // Skip an arbitrary prefix.
"\\\"(.+)\\\"" // Must be enclosed in quotes.
"[^\\\"]*"; // Skip an arbitrary suffix.
for (const fuzztest::internal::FilePathAndData& file : files) {
for (absl::string_view line : absl::StrSplit(file.data, '\n')) {
if (line.empty() || line[0] == '#') continue;
std::string entry;
CHECK(RE2::FullMatch(line, kLineRegex, &entry))
<< "Invalid dictionary entry: " << line;
std::string unescaped_entry;
CHECK(absl::CUnescape(entry, &unescaped_entry))
<< "Could not unescape: " << entry;
out.emplace_back(unescaped_entry.begin(), unescaped_entry.end());
}
}
return out;
}
template <typename T>
class InplaceVector {
public:
using iterator = T*;
using value_type = T;
InplaceVector() : data_(nullptr), size_(0) {}
InplaceVector(T* data, std::size_t size) : data_(data), size_(size) {}
T& operator[](int i) { return data_[i]; }
T const& operator[](int i) const { return data_[i]; }
std::size_t size() const { return size_; }
T* begin() const { return data_; }
T* end() const { return data_ + size_; }
void insert(T* index, T val) {
for (T* itr = data_ + size_; itr > index; --itr) {
*itr = *(itr - 1);
}
*index = val;
++size_;
}
void erase(T* index) {
for (T* itr = index; itr < data_ + size_ - 1; ++itr) {
*itr = *(itr + 1);
}
--size_;
}
void erase(T* begin, T* end) {
for (T *itr = begin, *jtr = end; jtr < data_ + size_; ++itr, ++jtr) {
*itr = *jtr;
}
size_ -= (end - begin);
}
private:
T* data_;
std::size_t size_;
};
extern "C" size_t LLVMFuzzerMutate(uint8_t* data, size_t size,
size_t max_size) {
static auto domain = fuzztest::internal::SequenceContainerOfImpl<
InplaceVector<uint8_t>, fuzztest::internal::ArbitraryImpl<uint8_t>>();
domain.WithMaxSize(max_size);
absl::BitGen bitgen;
InplaceVector<uint8_t> val(data, size);
domain.Mutate(val, bitgen, false);
return val.size();
}
class ArbitraryByteVector
: public fuzztest::internal::SequenceContainerOfImpl<
std::vector<uint8_t>, fuzztest::internal::ArbitraryImpl<uint8_t>> {
using Base = typename ArbitraryByteVector::ContainerOfImplBase;
public:
using typename Base::corpus_type;
ArbitraryByteVector() { WithMaxSize(kByteArrayMaxLen); }
void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) {
if (LLVMFuzzerCustomMutator) {
const size_t size = val.size();
const size_t max_size = only_shrink ? size : kByteArrayMaxLen;
val.resize(max_size);
val.resize(LLVMFuzzerCustomMutator(val.data(), size, max_size, prng()));
} else {
Base::Mutate(val, prng, only_shrink);
}
}
};
void TestOneInput(const std::vector<uint8_t>& data) {
LLVMFuzzerTestOneInput(data.data(), data.size());
}
FUZZ_TEST(LLVMFuzzer, TestOneInput)
.WithDomains(ArbitraryByteVector()
.WithDictionary(ReadByteArrayDictionaryFromFile)
.WithSeeds(ReadByteArraysFromDirectory));