You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
9.8 KiB
224 lines
9.8 KiB
// 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_H_
|
|
#define THIRD_PARTY_CENTIPEDE_CENTIPEDE_H_
|
|
|
|
#include <atomic>
|
|
#include <cstddef>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
#include "absl/base/nullability.h"
|
|
#include "absl/status/status.h"
|
|
#include "absl/time/time.h"
|
|
#include "./centipede/binary_info.h"
|
|
#include "./centipede/centipede_callbacks.h"
|
|
#include "./centipede/command.h"
|
|
#include "./centipede/control_flow.h"
|
|
#include "./centipede/corpus.h"
|
|
#include "./centipede/coverage.h"
|
|
#include "./centipede/environment.h"
|
|
#include "./centipede/feature.h"
|
|
#include "./centipede/feature_set.h"
|
|
#include "./centipede/pc_info.h"
|
|
#include "./centipede/runner_result.h"
|
|
#include "./centipede/rusage_profiler.h"
|
|
#include "./centipede/stats.h"
|
|
#include "./centipede/symbol_table.h"
|
|
#include "./centipede/workdir.h"
|
|
#include "./common/blob_file.h"
|
|
#include "./common/defs.h"
|
|
|
|
namespace fuzztest::internal {
|
|
|
|
// The main fuzzing class.
|
|
class Centipede {
|
|
public:
|
|
Centipede(const Environment &env, CentipedeCallbacks &user_callbacks,
|
|
const BinaryInfo &binary_info, CoverageLogger &coverage_logger,
|
|
std::atomic<Stats> &stats);
|
|
virtual ~Centipede() = default;
|
|
|
|
// Non-copyable and non-movable.
|
|
Centipede(const Centipede &) = delete;
|
|
Centipede(Centipede &&) noexcept = delete;
|
|
Centipede &operator=(const Centipede &) = delete;
|
|
Centipede &operator=(Centipede &&) noexcept = delete;
|
|
|
|
// Main loop.
|
|
void FuzzingLoop();
|
|
|
|
// Saves the sharded corpus into `dir`, one file per input.
|
|
static void CorpusToFiles(const Environment &env, std::string_view dir);
|
|
// Exports the corpus from `dir` (one file per input) into the sharded corpus.
|
|
// Reads `dir` recursively.
|
|
// Ignores inputs that already exist in the shard they need to be added to.
|
|
// Sharding is stable and depends only on env.total_shards and the file name.
|
|
static void CorpusFromFiles(const Environment &env, std::string_view dir);
|
|
// Saves the sharded crash reproducers and metadata (failure description) into
|
|
// `dir`. Each crash with `ID` will be saved with file `ID.data` for the
|
|
// reproducer and `ID.metadata` metadata.
|
|
static absl::Status CrashesToFiles(const Environment &env,
|
|
std::string_view dir);
|
|
|
|
private:
|
|
// Executes inputs from `input_vec`.
|
|
// For every input, its pruned features are written to
|
|
// `unconditional_features_file`, (if that's non-null).
|
|
// For every input that caused new features to be observed:
|
|
// * the input is added to the corpus (corpus_ and fs_ are updated).
|
|
// * the input is written to `corpus_file` (if that's non-null).
|
|
// * its features are written to `features_file` (if that's non-null).
|
|
// Returns true if new features were observed.
|
|
// Post-condition: `batch_result.results.size()` == `input_vec.size()`.
|
|
bool RunBatch(const std::vector<ByteArray> &input_vec,
|
|
BlobFileWriter *absl_nullable corpus_file,
|
|
BlobFileWriter *absl_nullable features_file,
|
|
BlobFileWriter *absl_nullable unconditional_features_file);
|
|
// Loads seed inputs from the user callbacks, execute them, and store them
|
|
// with the corresponding features into `corpus_file` and `features_file`.
|
|
void LoadSeedInputs(BlobFileWriter *absl_nonnull corpus_file,
|
|
BlobFileWriter *absl_nonnull features_file);
|
|
// Loads a shard `shard_index` from `load_env.workdir`.
|
|
// Note: `load_env_` may be different from `env_`.
|
|
// If `rerun` is true, then also re-runs any inputs
|
|
// for which the features are not found in `load_env.workdir`.
|
|
void LoadShard(const Environment &load_env, size_t shard_index, bool rerun);
|
|
// Loads all the shards from corpus files in `load_env.workdir` in random
|
|
// order. If `rerun_my_shard` is true, then also re-runs any inputs found in
|
|
// `load_env.my_shard_index`th shard. Note: `load_env_` may be different from
|
|
// `env_`.
|
|
void LoadAllShardsInRandomOrder(const Environment &load_env,
|
|
bool rerun_my_shard);
|
|
// Runs all inputs from `to_rerun`, adds their features to the features file
|
|
// of env_.my_shard_index, adds interesting inputs to the corpus.
|
|
void Rerun(std::vector<ByteArray> &to_rerun);
|
|
|
|
// Prints one logging line with `log_type` in it
|
|
// if `min_log_level` is not greater than `env_.log_level`.
|
|
void UpdateAndMaybeLogStats(std::string_view log_type, size_t min_log_level);
|
|
// For every feature in `fv`, translates the feature into code coverage
|
|
// (PCIndex), then prints one logging line for every
|
|
// FUNC/EDGE observed for the first time.
|
|
// If symbolization failed, prints a simpler logging line.
|
|
// Uses coverage_logger_.
|
|
void LogFeaturesAsSymbols(const FeatureVec &f);
|
|
|
|
// Generates a coverage report file in workdir.
|
|
void GenerateCoverageReport(std::string_view filename_annotation,
|
|
std::string_view description);
|
|
// Generates a corpus stats file in workdir.
|
|
void GenerateCorpusStats(std::string_view filename_annotation,
|
|
std::string_view description);
|
|
// Generates the clang source-based coverage report in workdir.
|
|
void GenerateSourceBasedCoverageReport(std::string_view filename_annotation,
|
|
std::string_view description);
|
|
// Generates a performance report file in workdir.
|
|
void GenerateRUsageReport(std::string_view filename_annotation,
|
|
std::string_view description);
|
|
// Generates all the report and stats files in workdir if this shard is
|
|
// assigned to do that.
|
|
void MaybeGenerateTelemetry(std::string_view filename_annotation,
|
|
std::string_view description);
|
|
// Generates all the report and stats files in workdir if this shard is
|
|
// assigned to do that and if `batch_index` satisfies the telemetry frequency
|
|
// criteria set via the flags.
|
|
void MaybeGenerateTelemetryAfterBatch(std::string_view filename_annotation,
|
|
size_t batch_index);
|
|
|
|
// Returns true if `input` passes env_.input_filter.
|
|
bool InputPassesFilter(const ByteArray &input);
|
|
// Executes `binary` with `input_vec` and `batch_result` as input/output.
|
|
// If the binary crashes, calls ReportCrash().
|
|
// Returns true iff there were no crashes.
|
|
bool ExecuteAndReportCrash(std::string_view binary,
|
|
const std::vector<ByteArray> &input_vec,
|
|
BatchResult &batch_result);
|
|
// Reports a crash and saves the reproducer to workdir/crashes, if possible.
|
|
// `binary` is the binary causing the crash.
|
|
// Prints the first `env_.max_num_crash_reports` logs.
|
|
// `input_vec` is the batch of inputs that caused a crash.
|
|
// `batch_result` contains the features computed for `input_vec`
|
|
// (batch_result.results().size() == input_vec.size()). `batch_result` is used
|
|
// as a hint when choosing which input to try first.
|
|
// Stops early if `EarlyExitRequested()`.
|
|
void ReportCrash(std::string_view binary,
|
|
const std::vector<ByteArray> &input_vec,
|
|
const BatchResult &batch_result);
|
|
// Merges shard `shard_index_to_merge` of the corpus in `merge_from_dir`
|
|
// into the current corpus.
|
|
// Writes added inputs to the current shard.
|
|
void MergeFromOtherCorpus(std::string_view merge_from_dir,
|
|
size_t shard_index_to_merge);
|
|
// Reloads the entire corpus for all the shards from workdir (as if with
|
|
// `env_.full_sync`) thus distilling it, and saves it to a single file with a
|
|
// shard-hashed name in the workdir.
|
|
void ReloadAllShardsAndWriteDistilledCorpus();
|
|
|
|
// Collects all PCs from `fv`, then adds PC-pair features to `fv`.
|
|
// Returns the number of added features.
|
|
// See more comments in centipede.cc.
|
|
size_t AddPcPairFeatures(FeatureVec &fv);
|
|
|
|
const Environment &env_;
|
|
const WorkDir wd_{env_};
|
|
|
|
CentipedeCallbacks &user_callbacks_;
|
|
Rng rng_;
|
|
|
|
// A timestamp set just before the actual fuzzing begins. Used to measure
|
|
// the fuzzing performance.
|
|
absl::Time fuzz_start_time_ = absl::InfiniteFuture();
|
|
|
|
FeatureSet fs_;
|
|
Corpus corpus_;
|
|
CoverageFrontier coverage_frontier_;
|
|
size_t num_runs_ = 0; // counts executed inputs
|
|
|
|
// Binary-related data, initialized at startup, once per process,
|
|
// by calling the PopulateBinaryInfo callback.
|
|
const BinaryInfo &binary_info_;
|
|
const PCTable &pc_table_; // same as binary_info_.pc_table.
|
|
const SymbolTable &symbols_; // same as binary_info_.symbols.
|
|
|
|
// Derived from env_.function_filter. Currently, duplicated by every thread.
|
|
// In future, threads may have different filters.
|
|
const FunctionFilter function_filter_;
|
|
|
|
// Ensures every coverage location is reported at most once.
|
|
// This object is shared with other threads, it is thread-safe.
|
|
CoverageLogger &coverage_logger_;
|
|
|
|
// Statistics of the current run.
|
|
std::atomic<Stats> &stats_;
|
|
|
|
// Counts the number of crashes reported so far.
|
|
int num_crashes_ = 0;
|
|
|
|
// Scratch object for AddPcPairFeatures.
|
|
std::vector<size_t> add_pc_pair_scratch_;
|
|
|
|
// Path and command for the input_filter.
|
|
std::string input_filter_path_;
|
|
Command input_filter_cmd_;
|
|
|
|
// Resource usage stats collection & reporting.
|
|
RUsageProfiler rusage_profiler_;
|
|
};
|
|
|
|
} // namespace fuzztest::internal
|
|
|
|
#endif // THIRD_PARTY_CENTIPEDE_CENTIPEDE_H_
|
|
|