blob: c58c069a5f102c1b9b43b4f2b1c7116e3f4fae35 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_SYS_FUZZING_COMMON_DICTIONARY_H_
#define SRC_SYS_FUZZING_COMMON_DICTIONARY_H_
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "src/lib/fxl/macros.h"
#include "src/sys/fuzzing/common/input.h"
#include "src/sys/fuzzing/common/options.h"
namespace fuzzing {
// This class represents a dictionary of input language keywords or other byte sequences that may be
// included in generating test inputs that are likely to uncover new features in a target, e.g.
// "GET" or "POST" for a fuzzer that takes HTTP inputs. The file format is the same as that of AFL,
// although it is best described by libFuzzer: https://llvm.org/docs/LibFuzzer.html#dictionaries
class Dictionary final {
public:
using Word = std::vector<uint8_t>;
using Level = std::vector<Word>;
Dictionary() = default;
Dictionary(Dictionary&& other) noexcept { *this = std::move(other); }
~Dictionary() = default;
Dictionary& operator=(Dictionary&& other) noexcept;
// Sets options.
void Configure(const OptionsPtr& options);
// Adds |size| bytes as a word to this dictionary.
void Add(const void* data, size_t size, uint16_t level = 0);
// Adds a |word| to the dictionary.
void Add(Word&& word, uint16_t level = 0);
// Resets the dictionary to an initial state and attempts to interpret the given input as a
// dictionary. Invalid entries are skipped.
bool Parse(const Input& input);
// Writes the dictionary out to an input.
Input AsInput() const;
// Apply |func| to each word in the dictionary with a level at or below the previously
// |Configure|d dictionary level (default is 0).
void ForEachWord(fit::function<void(const uint8_t*, size_t)> func) const;
private:
// Parses |str| as a dictionary level, which is an unsigned number. Returns false if |str| is not
// a valid number. Otherwise returns true and the parsed level via |out_level|.
bool ParseLevel(std::string_view str, uint16_t* out_level);
// Parse |str| as a word, which may contain sequences like \\, \", or \xNN where N is a hex digit.
// Returns false if the value is empty or contains invalid escape sequences (e.g. \x5G).
// Otherwise, returns true, the parsed word via |out_word|, and the portion of |str| that was not
// parsed in |out_remaining|.
bool ParseWord(std::string_view str, Word* out_word, std::string* out_remaining);
// Parses |str| as a number with the given |base|, e.g. 10 or 16. Returns false if |str| is not a
// number of if it cannot be expressed as a value of type |T|. Otherwise, returns true and the
// parsed value via |out|.
template <typename T>
bool ParseNumber(std::string_view str, int base, T* out) {
uint64_t u64;
if (!ParseU64(str, base, std::numeric_limits<T>::max(), &u64)) {
return false;
}
*out = static_cast<T>(u64);
return true;
}
// Parses |str| as an unsigned 64-bit integer with the given |base|, e.g. 10 or 16. Returns false
// if |str| is not a number, or if it exceeds |max|. Otherwise, returns true and the parsed value
// via |out|.
bool ParseU64(std::string_view str, int base, uint64_t max, uint64_t* out);
OptionsPtr options_;
std::unordered_map<uint16_t, Level> words_by_level_;
uint16_t max_level_ = 0;
FXL_DISALLOW_COPY_AND_ASSIGN(Dictionary);
};
} // namespace fuzzing
#endif // SRC_SYS_FUZZING_COMMON_DICTIONARY_H_