Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion centipede/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ cc_binary(
":centipede_interface",
":config_file",
":environment_flags",
":stop",
"@abseil-cpp//absl/base:nullability",
"@abseil-cpp//absl/log:flags",
],
)

Expand Down Expand Up @@ -898,6 +898,7 @@ cc_library(
":crash_summary",
":environment",
":runner_result",
":stop",
":workdir",
"@abseil-cpp//absl/container:flat_hash_map",
"@abseil-cpp//absl/container:flat_hash_set",
Expand Down Expand Up @@ -1281,6 +1282,7 @@ cc_library(
":feature",
":mutation_data",
":runner_result",
":stop",
":util",
"@abseil-cpp//absl/types:span",
"@com_google_fuzztest//common:defs",
Expand Down Expand Up @@ -1327,6 +1329,7 @@ cc_test(
":centipede_callbacks",
":environment",
":runner_result",
":stop",
"@abseil-cpp//absl/types:span",
"@com_google_fuzztest//common:defs",
"@googletest//:gtest_main",
Expand Down Expand Up @@ -1502,6 +1505,7 @@ cc_test(
":environment",
":minimize_crash",
":runner_result",
":stop",
":util",
":workdir",
"@abseil-cpp//absl/base:nullability",
Expand Down Expand Up @@ -1919,6 +1923,7 @@ cc_test(
":crash_summary",
":environment",
":runner_result",
":stop",
":util",
":workdir",
"@abseil-cpp//absl/container:flat_hash_map",
Expand Down
38 changes: 20 additions & 18 deletions centipede/centipede.cc
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,10 @@ std::vector<MutantRef> InputsToMutantRefs(const std::vector<ByteSpan>& inputs) {

Centipede::Centipede(const Environment& env, CentipedeCallbacks& user_callbacks,
const BinaryInfo& binary_info,
CoverageLogger& coverage_logger, std::atomic<Stats>& stats)
CoverageLogger& coverage_logger, std::atomic<Stats>& stats,
StopCondition& stop_condition)
: env_(env),
stop_condition_(stop_condition),
user_callbacks_(user_callbacks),
rng_(env_.seed),
// TODO(kcc): [impl] find a better way to compute frequency_threshold.
Expand Down Expand Up @@ -379,7 +381,7 @@ void Centipede::LogFeaturesAsSymbols(const FeatureVec &fv) {
bool Centipede::InputPassesFilter(ByteSpan input) {
if (env_.input_filter.empty()) return true;
WriteToLocalFile(input_filter_path_, input);
bool result = input_filter_cmd_.Execute() == EXIT_SUCCESS;
bool result = input_filter_cmd_.Execute(&stop_condition_) == EXIT_SUCCESS;
std::filesystem::remove(input_filter_path_);
return result;
}
Expand All @@ -394,7 +396,7 @@ bool Centipede::ExecuteAndReportCrash(std::string_view binary,
<< batch_result.failure_description();
return true;
}
if (ShouldStop()) {
if (stop_condition_.ShouldStop()) {
FUZZTEST_LOG_FIRST_N(WARNING, 1)
<< "Crash found but the stop condition is met - not reporting further "
"possibly related crashes.";
Expand Down Expand Up @@ -460,20 +462,20 @@ bool Centipede::RunBatch(
FUZZTEST_CHECK_EQ(mutants.size(), batch_result.results().size());

for (const auto &extra_binary : env_.extra_binaries) {
if (ShouldStop()) break;
if (stop_condition_.ShouldStop()) break;
BatchResult extra_batch_result;
success = ExecuteAndReportCrash(extra_binary, inputs, extra_batch_result) &&
success;
}
if (EarlyStopRequested()) return false;
if (stop_condition_.EarlyStopRequested()) return false;
if (!success && env_.exit_on_crash) {
FUZZTEST_LOG(INFO) << "--exit_on_crash is enabled; exiting soon";
RequestEarlyStop(EXIT_FAILURE);
stop_condition_.RequestEarlyStop(EXIT_FAILURE);
return false;
}
bool batch_gained_new_coverage = false;
for (size_t i = 0; i < mutants.size(); i++) {
if (ShouldStop()) break;
if (stop_condition_.ShouldStop()) break;
FeatureVec &fv = batch_result.results()[i].mutable_features();
bool function_filter_passed = function_filter_.filter(fv);
bool input_gained_new_coverage = fs_.PruneFeaturesAndCountUnseen(fv) != 0;
Expand Down Expand Up @@ -555,7 +557,7 @@ void Centipede::LoadShard(const Environment &load_env, size_t shard_index,
std::vector<ByteArray> inputs_to_rerun;
auto input_features_callback = [&](ByteArray input,
FeatureVec input_features) {
if (ShouldStop()) return;
if (stop_condition_.ShouldStop()) return;
if (input_features.empty()) {
if (rerun) {
inputs_to_rerun.emplace_back(std::move(input));
Expand Down Expand Up @@ -627,7 +629,7 @@ void Centipede::Rerun(std::vector<ByteArray> &to_rerun) {
// Re-run all inputs for which we don't know their features.
// Run in batches of at most env_.batch_size inputs each.
while (!to_rerun.empty()) {
if (ShouldStop()) break;
if (stop_condition_.ShouldStop()) break;
size_t batch_size = std::min(to_rerun.size(), env_.batch_size);
if (RunBatch(
InputsToMutantRefs({to_rerun.end() - batch_size, to_rerun.end()}),
Expand Down Expand Up @@ -903,7 +905,7 @@ void Centipede::FuzzingLoop() {
size_t new_runs = 0;
size_t corpus_size_at_last_prune = corpus_.NumActive();
for (size_t batch_index = 0; batch_index < number_of_batches; batch_index++) {
if (ShouldStop()) break;
if (stop_condition_.ShouldStop()) break;
FUZZTEST_CHECK_LT(new_runs, env_.num_runs);
auto remaining_runs = env_.num_runs - new_runs;
auto batch_size = std::min(env_.batch_size, remaining_runs);
Expand All @@ -923,7 +925,7 @@ void Centipede::FuzzingLoop() {

std::vector<Mutant> mutants =
user_callbacks_.Mutate(mutation_inputs, batch_size);
if (ShouldStop()) break;
if (stop_condition_.ShouldStop()) break;
new_runs += mutants.size();

std::vector<MutantRef> mutant_refs;
Expand Down Expand Up @@ -1015,21 +1017,21 @@ void Centipede::ReportCrash(std::string_view binary,
if (batch_result.IsSkippedTest()) {
log_execution_failure("Skipped Test: ");
FUZZTEST_LOG(INFO) << "Requesting early stop due to skipped test.";
RequestEarlyStop(EXIT_SUCCESS);
stop_condition_.RequestEarlyStop(EXIT_SUCCESS);
return;
}

if (batch_result.IsSetupFailure()) {
log_execution_failure("Test Setup Failure: ");
FUZZTEST_LOG(INFO)
<< "Requesting early stop due to setup failure in the test.";
RequestEarlyStop(EXIT_FAILURE);
stop_condition_.RequestEarlyStop(EXIT_FAILURE);
return;
}

// Skip reporting only if RequestEarlyStop is called - still reporting if time
// runs out.
if (EarlyStopRequested()) return;
// limit is reached.
if (stop_condition_.EarlyStopRequested()) return;

if (++num_crashes_ > env_.max_num_crash_reports) return;

Expand Down Expand Up @@ -1073,14 +1075,14 @@ void Centipede::ReportCrash(std::string_view binary,
<< log_prefix
<< "Executing inputs one-by-one, trying to find the reproducer";
for (auto input_idx : input_idxs_to_try) {
if (ShouldStop()) break;
if (stop_condition_.ShouldStop()) break;
const auto one_input = input_vec[input_idx];
BatchResult one_input_batch_result;
if (!user_callbacks_.Execute(binary, {one_input}, one_input_batch_result) &&
one_input_batch_result.IsInputFailure() &&
one_input_batch_result.failure_signature() ==
batch_result.failure_signature() &&
!ShouldStop()) {
!stop_condition_.ShouldStop()) {
auto hash = Hash(one_input);
auto crash_dir = wd_.CrashReproducerDirPaths().MyShard();
FUZZTEST_CHECK_OK(RemoteMkdir(crash_dir));
Expand Down Expand Up @@ -1113,7 +1115,7 @@ void Centipede::ReportCrash(std::string_view binary,
}
}

if (ShouldStop()) {
if (stop_condition_.ShouldStop()) {
FUZZTEST_LOG(INFO)
<< log_prefix
<< "Stop condition is on - skipping triaging the reproducer";
Expand Down
8 changes: 5 additions & 3 deletions centipede/centipede.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "./centipede/runner_result.h"
#include "./centipede/rusage_profiler.h"
#include "./centipede/stats.h"
#include "./centipede/stop.h"
#include "./centipede/symbol_table.h"
#include "./centipede/workdir.h"
#include "./common/blob_file.h"
Expand All @@ -48,9 +49,9 @@ 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);
Centipede(const Environment& env, CentipedeCallbacks& user_callbacks,
const BinaryInfo& binary_info, CoverageLogger& coverage_logger,
std::atomic<Stats>& stats, StopCondition& stop_condition);
virtual ~Centipede() = default;

// Non-copyable and non-movable.
Expand Down Expand Up @@ -175,6 +176,7 @@ class Centipede {
size_t AddPcPairFeatures(FeatureVec &fv);

const Environment &env_;
StopCondition& stop_condition_;
const WorkDir wd_{env_};

CentipedeCallbacks &user_callbacks_;
Expand Down
7 changes: 4 additions & 3 deletions centipede/centipede_callbacks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ int CentipedeCallbacks::RunBatchForBinary(std::string_view binary) {
? absl::InfiniteDuration()
: absl::Seconds(env_.timeout_per_batch) + absl::Seconds(5);
const auto deadline =
std::min(absl::Now() + amortized_timeout, GetStopTime());
std::min(absl::Now() + amortized_timeout, stop_condition_.GetStopTime());
int exit_code = EXIT_SUCCESS;
const bool should_clean_up = [&] {
if (!cmd.is_executing()) {
Expand All @@ -491,7 +491,7 @@ int CentipedeCallbacks::RunBatchForBinary(std::string_view binary) {
}
return false;
}
const std::optional<int> ret = cmd.Wait(deadline);
const std::optional<int> ret = cmd.Wait(deadline, &stop_condition_);
if (!ret.has_value()) return true;
exit_code = *ret;
return false;
Expand Down Expand Up @@ -660,7 +660,8 @@ bool CentipedeCallbacks::GetSeedsViaExternalBinary(
<< cmd.ToString();
return EXIT_FAILURE;
}
const auto wait_result = cmd.Wait(GetStopTime());
const auto wait_result =
cmd.Wait(stop_condition_.GetStopTime(), &stop_condition_);
if (!wait_result.has_value()) {
FUZZTEST_LOG(ERROR) << "Failed to wait for the seeding command "
<< cmd.ToString();
Expand Down
24 changes: 15 additions & 9 deletions centipede/centipede_callbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "./centipede/mutation_data.h"
#include "./centipede/runner_result.h"
#include "./centipede/shared_memory_blob_sequence.h"
#include "./centipede/stop.h"
#include "./centipede/util.h"
#include "./common/defs.h"
#include "./common/logging.h"
Expand All @@ -47,9 +48,9 @@ namespace fuzztest::internal {
// 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)
CentipedeCallbacks(const Environment& env, StopCondition& stop_condition)
: env_(env),
stop_condition_(stop_condition),
byte_array_mutator_(env.knobs, GetRandomSeed(env.seed)),
fuzztest_mutator_(env.knobs, GetRandomSeed(env.seed)),
inputs_blobseq_(shmem_name1_.c_str(), env.shmem_size_mb << 20,
Expand Down Expand Up @@ -164,6 +165,7 @@ class CentipedeCallbacks {
void PrintExecutionLog() const;

const Environment &env_;
StopCondition& stop_condition_;
ByteArrayMutator byte_array_mutator_;
FuzzTestMutator fuzztest_mutator_;

Expand Down Expand Up @@ -216,7 +218,8 @@ class CentipedeCallbacks {
// and not actually delete it.
class CentipedeCallbacksFactory {
public:
virtual CentipedeCallbacks *create(const Environment &env) = 0;
virtual CentipedeCallbacks* create(const Environment& env,
StopCondition& stop_condition) = 0;
virtual void destroy(CentipedeCallbacks *callbacks) = 0;
virtual ~CentipedeCallbacksFactory() {}
};
Expand All @@ -225,8 +228,9 @@ class CentipedeCallbacksFactory {
template <typename Type>
class DefaultCallbacksFactory : public CentipedeCallbacksFactory {
public:
CentipedeCallbacks *create(const Environment &env) override {
return new Type(env);
CentipedeCallbacks* create(const Environment& env,
StopCondition& stop_condition) override {
return new Type(env, stop_condition);
}
void destroy(CentipedeCallbacks *callbacks) override { delete callbacks; }
};
Expand All @@ -239,7 +243,8 @@ class NonOwningCallbacksFactory : public CentipedeCallbacksFactory {
public:
explicit NonOwningCallbacksFactory(CentipedeCallbacks& callbacks)
: callbacks_(callbacks) {}
CentipedeCallbacks* absl_nonnull create(const Environment& env) override {
CentipedeCallbacks* absl_nonnull create(
const Environment& env, StopCondition& stop_condition) override {
const bool was_already_created =
is_created_.exchange(true, std::memory_order_acq_rel);
FUZZTEST_CHECK(!was_already_created)
Expand All @@ -263,9 +268,10 @@ class NonOwningCallbacksFactory : public CentipedeCallbacksFactory {
// 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(CentipedeCallbacksFactory& factory,
const Environment& env,
StopCondition& stop_condition)
: factory_(factory), callbacks_(factory_.create(env, stop_condition)) {}
~ScopedCentipedeCallbacks() { factory_.destroy(callbacks_); }
CentipedeCallbacks *absl_nonnull callbacks() { return callbacks_; }

Expand Down
19 changes: 14 additions & 5 deletions centipede/centipede_callbacks_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,41 @@
#include "absl/types/span.h"
#include "./centipede/environment.h"
#include "./centipede/runner_result.h"
#include "./centipede/stop.h"
#include "./common/defs.h"

namespace fuzztest::internal {
namespace {

class FakeCallbacks : public CentipedeCallbacks {
public:
explicit FakeCallbacks(const Environment& env) : CentipedeCallbacks(env) {}
explicit FakeCallbacks(const Environment& env)
: CentipedeCallbacks(env, mock_stop_condition_) {}
bool Execute(std::string_view binary, absl::Span<const ByteSpan> inputs,
BatchResult& batch_result) override {
return true;
}

private:
StopCondition mock_stop_condition_;
};

TEST(NonOwningCallbacksFactoryTest, CreateReturnsUnderlyingCallbacks) {
Environment env;
FakeCallbacks callbacks(env);
NonOwningCallbacksFactory factory(callbacks);
EXPECT_EQ(factory.create(env), &callbacks);
StopCondition stop_condition;
EXPECT_EQ(factory.create(env, stop_condition), &callbacks);
}

TEST(NonOwningCallbacksFactoryTest, CannotCreateTwice) {
Environment env;
FakeCallbacks callbacks(env);
NonOwningCallbacksFactory factory(callbacks);
factory.create(env);
EXPECT_DEATH(factory.create(env), "create\\(\\) called before destroy\\(\\)");
StopCondition stop_condition;
factory.create(env, stop_condition);
EXPECT_DEATH(factory.create(env, stop_condition),
"create\\(\\) called before destroy\\(\\)");
}

TEST(NonOwningCallbacksFactoryTest, CannotDestroyBeforeCreate) {
Expand All @@ -61,7 +69,8 @@ TEST(NonOwningCallbacksFactoryTest, CannotDestroyTwice) {
Environment env;
FakeCallbacks callbacks(env);
NonOwningCallbacksFactory factory(callbacks);
factory.create(env);
StopCondition stop_condition;
factory.create(env, stop_condition);
factory.destroy(&callbacks);
EXPECT_DEATH(factory.destroy(&callbacks),
"destroy\\(\\) called before the matching create\\(\\)");
Expand Down
Loading
Loading