blob: 2b944b80f88494f1eda5a5a809223fae86b22ed0 [file] [log] [blame]
// Copyright 2020 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_LIB_FUZZING_FIDL_DATA_PROVIDER_H_
#define SRC_LIB_FUZZING_FIDL_DATA_PROVIDER_H_
#include <fuchsia/fuzzer/cpp/fidl.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/fxl/synchronization/thread_annotations.h>
#include <lib/sync/completion.h>
#include <lib/zx/vmo.h>
#include <stddef.h>
#include <stdint.h>
#include <zircon/types.h>
#include <functional>
#include <map>
#include <string>
#include <vector>
#include "test-input.h"
#include "traced-instruction.h"
namespace fuzzing {
namespace {
using ::fuchsia::fuzzer::DataProvider;
using ::fuchsia::fuzzer::LlvmFuzzerHandle;
} // namespace
// Implementation of fuchsia.fuzzer.DataProvider.
//
// This class can be used to partition a single libFuzzer test input into inputs for multiple
// consumers. It is designed to be "fuzzer-stable", that is, inserting or removing bytes do not
// generally change how the input is partitioned.
//
// It is also designed to facilitate handcrafting corpus elements for sending data to multiple
// consumers, using Rust-style attributes,
//
// On startup, the FIDL fuzzing framework must provide the engine with a list of labels designating
// other consumers. Providing them upon startup initialization allows the provider to partition the
// input even before other services have started in response to FIDL requests made by the fuzzer.
//
// Labels may contain any characters except '#', '[', and ']'.
//
// On starting up, the other consumers should discover the DataProvider and call |AddConsumer| with
// a label matching one provided by the fuzzer and a VMO to hold the test input.
//
// The test input is partitioned using the following rules, where "LABEL" corresponds to one of the
// previously provided labels.
// 1. Initially, data is written to the fuzzer-provided TestInput.
// 2. If the input contains a byte sequences like "##[LABEL]", it is mapped to "#[LABEL]" and then
// skipped. (This allows the input to express all patterns, including labels).
// 3. Otherwise, if the input contains a byte sequence like "#[LABEL]", all subsequent data up to
// the next such label or data end is written to the corresponding TestInput.
//
// For example, assuming |Configure| was called with labels like {"foo", "bar", "baz"} and the
// following input:
// 00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAA|
// 00000000 41 23 5B 62 61 7A 5D 42 42 42 23 23 5B 62 61 72 |A#[baz]BBB##[bar|
// 00000000 5D 43 43 23 5B 66 6F 6F 5D 44 44 44 44 44 44 44 |]CC#[foo]DDDDDDD|
//
// The data would be partitioned as follows:
// * The fuzzer would receive 17 bytes of "AAAAAAAAAAAAAAAAA".
// * The "foo" consumer would receive 7 bytes of "DDDDDDD".
// * The "bar" consumer would receive 0 bytes.
// * The "baz" consumer would receive 11 bytes of "BBB#[bar]CC" (*not* 12 bytes of "BBB##[bar]CC").
//
class DataProviderImpl : public DataProvider {
public:
DataProviderImpl();
virtual ~DataProviderImpl();
// Accessors for testing.
bool HasLabel(const std::string &label) FXL_LOCKS_EXCLUDED(lock_);
bool IsMapped(const std::string &label) FXL_LOCKS_EXCLUDED(lock_);
// FIDL methods
void AddConsumer(std::string label, zx::vmo vmo, AddConsumerCallback callback) override
FXL_LOCKS_EXCLUDED(lock_);
// Engine-callable methods
fidl::InterfaceRequestHandler<DataProvider> GetHandler() { return bindings_.GetHandler(this); }
// Sets up the data provider and returns the VMO that the client will use to get test input data.
zx_status_t Initialize(zx::vmo *out) FXL_LOCKS_EXCLUDED(lock_);
// Register the given label as a potential data consumer.
void AddConsumerLabel(std::string label) FXL_LOCKS_EXCLUDED(lock_);
// Partitions the test input according to the class description above. Returns an error if it is
// unable to signal consumers that data is ready, which is fatal.
zx_status_t PartitionTestInput(const void *data, size_t size) FXL_LOCKS_EXCLUDED(lock_);
// Signals all connected consumers that the current iteration is complete, i.e. they should not
// use any more data from the test input. Returns an error if it is unable to signal consumers
// to stop using data, which is fatal.
zx_status_t CompleteIteration() FXL_LOCKS_EXCLUDED(lock_);
// Returns the object to an initial state, i.e. ready for |Configure| to be called again.
void Reset();
private:
// Bindings for connected clients.
fidl::BindingSet<DataProvider> bindings_;
// Lock to synchronize calls from engine and dispatcher threads.
std::mutex lock_;
// Shared memory regions into which test input data is partitioned.
std::map<std::string, TestInput, std::less<>> inputs_ FXL_GUARDED_BY(lock_);
// Label length bounds, to speed up scanning test input when partitioning.
size_t max_label_length_ FXL_GUARDED_BY(lock_);
};
} // namespace fuzzing
#endif // SRC_LIB_FUZZING_FIDL_DATA_PROVIDER_H_