blob: 92191d66cb026d0ae7630f1045094889dac9d98e [file] [log] [blame]
// Copyright 2022 The Centipede Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_CENTIPEDE_CENTIPEDE_CALLBACKS_H_
#define THIRD_PARTY_CENTIPEDE_CENTIPEDE_CALLBACKS_H_
#include <cstddef>
#include <filesystem>
#include <string>
#include <string_view>
#include <vector>
#include "./centipede/binary_info.h"
#include "./centipede/byte_array_mutator.h"
#include "./centipede/call_graph.h"
#include "./centipede/command.h"
#include "./centipede/control_flow.h"
#include "./centipede/defs.h"
#include "./centipede/environment.h"
#include "./centipede/execution_request.h"
#include "./centipede/execution_result.h"
#include "./centipede/fuzztest_mutator.h"
#include "./centipede/knobs.h"
#include "./centipede/logging.h"
#include "./centipede/mutation_input.h"
#include "./centipede/shared_memory_blob_sequence.h"
#include "./centipede/symbol_table.h"
#include "./centipede/util.h"
namespace centipede {
// User must inherit from this class and override at least the
// pure virtual functions.
//
// The classes inherited from this one must be thread-compatible.
// Note: the interface is not yet stable and may change w/o a notice.
class CentipedeCallbacks {
public:
// `env` is used to pass flags to `this`, it must outlive `this`.
CentipedeCallbacks(const Environment &env)
: env_(env),
byte_array_mutator_(env.knobs, GetRandomSeed(env.seed)),
fuzztest_mutator_(GetRandomSeed(env.seed)),
inputs_blobseq_(shmem_name1_.c_str(), env.shmem_size_mb << 20,
env.use_posix_shmem),
outputs_blobseq_(shmem_name2_.c_str(), env.shmem_size_mb << 20,
env.use_posix_shmem) {
if (env.use_legacy_default_mutator)
CHECK(byte_array_mutator_.set_max_len(env.max_len));
else
CHECK(fuzztest_mutator_.set_max_len(env.max_len));
}
virtual ~CentipedeCallbacks() {}
// Feeds `inputs` into the `binary`, for every input populates `batch_result`.
// Old contents of `batch_result` are cleared.
// Returns true on success, false on failure.
// Post-condition:
// `batch_result` has results for every `input`, even on failure.
virtual bool Execute(std::string_view binary,
const std::vector<ByteArray> &inputs,
BatchResult &batch_result) = 0;
// Takes non-empty `inputs`, discards old contents of `mutants`,
// adds `num_mutants` mutated inputs to `mutants`.
virtual void Mutate(const std::vector<MutationInputRef> &inputs,
size_t num_mutants, std::vector<ByteArray> &mutants) {
env_.use_legacy_default_mutator
? byte_array_mutator_.MutateMany(inputs, num_mutants, mutants)
: fuzztest_mutator_.MutateMany(inputs, num_mutants, mutants);
}
// Populates the BinaryInfo using the `symbolizer_path` and `coverage_binary`
// in `env_`. The tables may not be populated if the PC table cannot be
// determined from the `coverage_binary` or if symbolization fails. Exits if
// PC table was not populated and `env_.require_pc_table` is set.
virtual void PopulateBinaryInfo(BinaryInfo &binary_info);
// Returns some simple non-empty valid input.
virtual ByteArray DummyValidInput() { return {0}; }
protected:
// Helpers that the user-defined class may use if needed.
// Same as ExecuteCentipedeSancovBinary, but uses shared memory.
// Much faster for fast targets since it uses fewer system calls.
int ExecuteCentipedeSancovBinaryWithShmem(
std::string_view binary, const std::vector<ByteArray> &inputs,
BatchResult &batch_result);
// Constructs a string CENTIPEDE_RUNNER_FLAGS=":flag1:flag2:...",
// where the flags are determined by `env` and also include `extra_flags`.
// If `disable_coverage`, coverage options are not added.
std::string ConstructRunnerFlags(std::string_view extra_flags = "",
bool disable_coverage = false);
// Uses an external binary `binary` to mutate `inputs`. The binary should be
// linked against :centipede_runner and implement the Structure-Aware Fuzzing
// interface, as described here:
// github.com/google/fuzzing/blob/master/docs/structure-aware-fuzzing.md
//
// Produces at most `mutants.size()` non-empty mutants,
// replacing the existing elements of `mutants`,
// and shrinking `mutants` if needed.
//
// Returns true on success.
bool MutateViaExternalBinary(std::string_view binary,
const std::vector<MutationInputRef> &inputs,
std::vector<ByteArray> &mutants);
// Loads the dictionary from `dictionary_path`,
// returns the number of dictionary entries loaded.
size_t LoadDictionary(std::string_view dictionary_path);
protected:
const Environment &env_;
ByteArrayMutator byte_array_mutator_;
FuzzTestMutator fuzztest_mutator_;
private:
// Returns a Command object with matching `binary` from commands_,
// creates one if needed.
Command &GetOrCreateCommandForBinary(std::string_view binary);
// Variables required for ExecuteCentipedeSancovBinaryWithShmem.
// They are computed in CTOR, to avoid extra computation in the hot loop.
std::string temp_dir_ = TemporaryLocalDirPath();
std::string temp_input_file_path_ =
std::filesystem::path(temp_dir_).append("temp_input_file");
const std::string execute_log_path_ =
std::filesystem::path(temp_dir_).append("log");
std::string failure_description_path_ =
std::filesystem::path(temp_dir_).append("failure_description");
const std::string shmem_name1_ = ProcessAndThreadUniqueID("/centipede-shm1-");
const std::string shmem_name2_ = ProcessAndThreadUniqueID("/centipede-shm2-");
SharedMemoryBlobSequence inputs_blobseq_;
SharedMemoryBlobSequence outputs_blobseq_;
std::vector<Command> commands_;
};
// Abstract class for creating/destroying CentipedeCallbacks objects.
// A typical implementation would simply new/delete objects of appropriate type,
// see DefaultCallbacksFactory below.
// Other implementations (e.g. for tests) may take the object from elsewhere
// and not actually delete it.
class CentipedeCallbacksFactory {
public:
virtual CentipedeCallbacks *create(const Environment &env) = 0;
virtual void destroy(CentipedeCallbacks *callbacks) = 0;
virtual ~CentipedeCallbacksFactory() {}
};
// This is the typical way to implement a CentipedeCallbacksFactory for a Type.
template <typename Type>
class DefaultCallbacksFactory : public CentipedeCallbacksFactory {
public:
CentipedeCallbacks *create(const Environment &env) override {
return new Type(env);
}
void destroy(CentipedeCallbacks *callbacks) override { delete callbacks; }
};
// Creates a CentipedeCallbacks object in CTOR and destroys it in DTOR.
class ScopedCentipedeCallbacks {
public:
ScopedCentipedeCallbacks(CentipedeCallbacksFactory &factory,
const Environment &env)
: factory_(factory), callbacks_(factory_.create(env)) {}
~ScopedCentipedeCallbacks() { factory_.destroy(callbacks_); }
CentipedeCallbacks *callbacks() { return callbacks_; }
private:
CentipedeCallbacksFactory &factory_;
CentipedeCallbacks *callbacks_;
};
} // namespace centipede
#endif // THIRD_PARTY_CENTIPEDE_CENTIPEDE_CALLBACKS_H_