// 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 #include #include #include #include #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); 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 &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 &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 &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 &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_; // Counts the number of crashes reported so far. int num_crashes_ = 0; // Scratch object for AddPcPairFeatures. std::vector 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_