Skip to content

Commit 13e75af

Browse files
xinhaoyuancopybara-github
authored andcommitted
Use the stop time to cap command deadlines when running batches for fuzzing.
Do not report crashes on stop conditions. Exit early in batch condition when termination is requested. Prolong the unit testing duration to 10s to reduce flakiness. This allows the stop time to be enforced more precisely instead of having to wait for the entire batch, without reporting the potential crashes due to interrupting the command. After this we don't need to set batch timeout to the test time limit - it was a quick workaround for the same requirement. PiperOrigin-RevId: 791875139
1 parent e9b49f3 commit 13e75af

23 files changed

+154
-133
lines changed

centipede/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ cc_library(
665665
":runner_request",
666666
":runner_result",
667667
":shared_memory_blob_sequence",
668+
":stop",
668669
":util",
669670
":workdir",
670671
"@abseil-cpp//absl/base:nullability",

centipede/centipede.cc

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,17 @@ bool Centipede::InputPassesFilter(const ByteArray &input) {
362362
bool Centipede::ExecuteAndReportCrash(std::string_view binary,
363363
const std::vector<ByteArray> &input_vec,
364364
BatchResult &batch_result) {
365-
bool success = user_callbacks_.Execute(binary, input_vec, batch_result);
366-
if (!success) ReportCrash(binary, input_vec, batch_result);
367-
return success || batch_result.IsIgnoredFailure();
365+
bool success =
366+
user_callbacks_.Execute(binary, input_vec, batch_result, GetStopTime());
367+
if (success) return true;
368+
if (ShouldStop()) {
369+
FUZZTEST_LOG_FIRST_N(WARNING, 1)
370+
<< "Stop condition met - not reporting further crashes possibly "
371+
"related to the stop condition.";
372+
return true;
373+
}
374+
ReportCrash(binary, input_vec, batch_result);
375+
return batch_result.IsIgnoredFailure();
368376
}
369377

370378
// *** Highly experimental and risky. May not scale well for large targets. ***
@@ -976,7 +984,8 @@ void Centipede::ReportCrash(std::string_view binary,
976984
if (ShouldStop()) return;
977985
const auto &one_input = input_vec[input_idx];
978986
BatchResult one_input_batch_result;
979-
if (!user_callbacks_.Execute(binary, {one_input}, one_input_batch_result)) {
987+
if (!user_callbacks_.Execute(binary, {one_input}, one_input_batch_result,
988+
absl::InfiniteFuture())) {
980989
auto hash = Hash(one_input);
981990
auto crash_dir = wd_.CrashReproducerDirPaths().MyShard();
982991
FUZZTEST_CHECK_OK(RemoteMkdir(crash_dir));

centipede/centipede_callbacks.cc

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "./centipede/mutation_input.h"
5151
#include "./centipede/runner_request.h"
5252
#include "./centipede/runner_result.h"
53+
#include "./centipede/stop.h"
5354
#include "./centipede/util.h"
5455
#include "./centipede/workdir.h"
5556
#include "./common/blob_file.h"
@@ -472,14 +473,15 @@ void CentipedeCallbacks::CleanUpPersistentMode() {
472473
command_contexts_.end());
473474
}
474475

475-
int CentipedeCallbacks::RunBatchForBinary(std::string_view binary) {
476+
int CentipedeCallbacks::RunBatchForBinary(std::string_view binary,
477+
absl::Time deadline) {
476478
auto& command_context = GetOrCreateCommandContextForBinary(binary);
477479
auto& cmd = command_context.cmd;
478480
const absl::Duration amortized_timeout =
479481
env_.timeout_per_batch == 0
480482
? absl::InfiniteDuration()
481483
: absl::Seconds(env_.timeout_per_batch) + absl::Seconds(5);
482-
const auto deadline = absl::Now() + amortized_timeout;
484+
deadline = std::min(absl::Now() + amortized_timeout, deadline);
483485
int exit_code = EXIT_SUCCESS;
484486
const bool should_clean_up = [&] {
485487
if (!cmd.is_executing() && !cmd.ExecuteAsync()) {
@@ -497,11 +499,12 @@ int CentipedeCallbacks::RunBatchForBinary(std::string_view binary) {
497499
if (should_clean_up) {
498500
exit_code = [&] {
499501
if (!cmd.is_executing()) return EXIT_FAILURE;
500-
FUZZTEST_LOG(ERROR) << "Cleaning up the batch execution.";
502+
FUZZTEST_LOG(ERROR) << "Cleaning up the batch execution with timeout "
503+
<< kCommandCleanupTimeout;
501504
cmd.RequestStop();
502505
const auto ret = cmd.Wait(absl::Now() + kCommandCleanupTimeout);
503506
if (ret.has_value()) return *ret;
504-
FUZZTEST_LOG(ERROR) << "Batch execution cleanup failed to end in 60s.";
507+
FUZZTEST_LOG(ERROR) << "Failed to wait for the batch execution cleanup.";
505508
return EXIT_FAILURE;
506509
}();
507510
command_contexts_.erase(
@@ -515,7 +518,7 @@ int CentipedeCallbacks::RunBatchForBinary(std::string_view binary) {
515518

516519
int CentipedeCallbacks::ExecuteCentipedeSancovBinaryWithShmem(
517520
std::string_view binary, const std::vector<ByteArray>& inputs,
518-
BatchResult& batch_result) {
521+
BatchResult& batch_result, absl::Time deadline) {
519522
auto start_time = absl::Now();
520523
batch_result.ClearAndResize(inputs.size());
521524

@@ -541,7 +544,7 @@ int CentipedeCallbacks::ExecuteCentipedeSancovBinaryWithShmem(
541544
}
542545

543546
// Run.
544-
const int exit_code = RunBatchForBinary(binary);
547+
const int exit_code = RunBatchForBinary(binary, deadline);
545548
inputs_blobseq_.ReleaseSharedMemory(); // Inputs are already consumed.
546549

547550
// Get results.
@@ -699,7 +702,8 @@ MutationResult CentipedeCallbacks::MutateViaExternalBinary(
699702
<< VV(num_inputs_written) << VV(inputs.size());
700703

701704
// Execute.
702-
const int exit_code = RunBatchForBinary(binary);
705+
const int exit_code =
706+
RunBatchForBinary(binary, /*deadline=*/absl::InfiniteFuture());
703707
inputs_blobseq_.ReleaseSharedMemory(); // Inputs are already consumed.
704708

705709
if (exit_code != EXIT_SUCCESS) {

centipede/centipede_callbacks.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ class CentipedeCallbacks {
6565
// Post-condition:
6666
// `batch_result` has results for every `input`, even on failure.
6767
virtual bool Execute(std::string_view binary,
68-
const std::vector<ByteArray> &inputs,
69-
BatchResult &batch_result) = 0;
68+
const std::vector<ByteArray>& inputs,
69+
BatchResult& batch_result, absl::Time deadline) = 0;
7070

7171
// Takes non-empty `inputs` and returns at most `num_mutants` mutated inputs.
7272
virtual std::vector<ByteArray> Mutate(
@@ -103,8 +103,8 @@ class CentipedeCallbacks {
103103
// Same as ExecuteCentipedeSancovBinary, but uses shared memory.
104104
// Much faster for fast targets since it uses fewer system calls.
105105
int ExecuteCentipedeSancovBinaryWithShmem(
106-
std::string_view binary, const std::vector<ByteArray> &inputs,
107-
BatchResult &batch_result);
106+
std::string_view binary, const std::vector<ByteArray>& inputs,
107+
BatchResult& batch_result, absl::Time deadline);
108108

109109
// Constructs a string CENTIPEDE_RUNNER_FLAGS=":flag1:flag2:...",
110110
// where the flags are determined by `env` and also include `extra_flags`.
@@ -173,7 +173,7 @@ class CentipedeCallbacks {
173173
// Returns a CommandContext with matching `binary`. Creates one if needed.
174174
CommandContext& GetOrCreateCommandContextForBinary(std::string_view binary);
175175
// Runs a batch with the command `binary` and returns the exit code.
176-
int RunBatchForBinary(std::string_view binary);
176+
int RunBatchForBinary(std::string_view binary, absl::Time deadline);
177177

178178
// Prints the execution log from the last executed binary.
179179
void PrintExecutionLog() const;

centipede/centipede_default_callbacks.cc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ CentipedeDefaultCallbacks::CentipedeDefaultCallbacks(const Environment &env)
4646
}
4747

4848
bool CentipedeDefaultCallbacks::Execute(std::string_view binary,
49-
const std::vector<ByteArray> &inputs,
50-
BatchResult &batch_result) {
51-
return ExecuteCentipedeSancovBinaryWithShmem(binary, inputs, batch_result) ==
52-
0;
49+
const std::vector<ByteArray>& inputs,
50+
BatchResult& batch_result,
51+
absl::Time deadline) {
52+
return ExecuteCentipedeSancovBinaryWithShmem(binary, inputs, batch_result,
53+
deadline) == 0;
5354
}
5455

5556
size_t CentipedeDefaultCallbacks::GetSeeds(size_t num_seeds,

centipede/centipede_default_callbacks.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ class CentipedeDefaultCallbacks : public CentipedeCallbacks {
4040
explicit CentipedeDefaultCallbacks(const Environment &env);
4141
size_t GetSeeds(size_t num_seeds, std::vector<ByteArray> &seeds) override;
4242
absl::StatusOr<std::string> GetSerializedTargetConfig() override;
43-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
44-
BatchResult &batch_result) override;
43+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
44+
BatchResult& batch_result, absl::Time deadline) override;
4545
std::vector<ByteArray> Mutate(const std::vector<MutationInputRef> &inputs,
4646
size_t num_mutants) override;
4747

centipede/centipede_interface.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ absl::flat_hash_set<std::string> PruneOldCrashesAndGetRemainingCrashSignatures(
305305
FUZZTEST_CHECK_OK(
306306
RemoteFileGetContents(crashing_input_file, crashing_input));
307307
const bool is_reproducible = !scoped_callbacks.callbacks()->Execute(
308-
env.binary, {crashing_input}, batch_result);
308+
env.binary, {crashing_input}, batch_result, absl::InfiniteFuture());
309309
const bool is_duplicate =
310310
is_reproducible && !batch_result.IsSetupFailure() &&
311311
!remaining_crash_signatures.insert(batch_result.failure_signature())
@@ -686,6 +686,8 @@ int UpdateCorpusDatabaseForFuzzTests(
686686
<< "Skip updating corpus database due to early stop requested.";
687687
continue;
688688
}
689+
// The test time limit does not apply for the rest of the steps.
690+
ClearEarlyStopRequestAndSetStopTime(/*stop_time=*/absl::InfiniteFuture());
689691

690692
// TODO(xinhaoyuan): Have a separate flag to skip corpus updating instead
691693
// of checking whether workdir is specified or not.

centipede/centipede_test.cc

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ class CentipedeMock : public CentipedeCallbacks {
6868
// Doesn't execute anything
6969
// Sets `batch_result.results()` based on the values of `inputs`:
7070
// Collects various stats about the inputs, to be checked in tests.
71-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
72-
BatchResult &batch_result) override {
71+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
72+
BatchResult& batch_result, absl::Time deadline) override {
7373
batch_result.results().clear();
7474
// For every input, we create a 256-element array `counters`, where
7575
// i-th element is the number of bytes with the value 'i' in the input.
@@ -356,8 +356,8 @@ class MutateCallbacks : public CentipedeCallbacks {
356356
public:
357357
explicit MutateCallbacks(const Environment &env) : CentipedeCallbacks(env) {}
358358
// Will not be called.
359-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
360-
BatchResult &batch_result) override {
359+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
360+
BatchResult& batch_result, absl::Time deadline) override {
361361
FUZZTEST_LOG(FATAL);
362362
return false;
363363
}
@@ -499,8 +499,8 @@ class MergeMock : public CentipedeCallbacks {
499499
// Doesn't execute anything.
500500
// All inputs are 1-byte long.
501501
// For an input {X}, the feature output is {X}.
502-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
503-
BatchResult &batch_result) override {
502+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
503+
BatchResult& batch_result, absl::Time deadline) override {
504504
batch_result.results().resize(inputs.size());
505505
for (size_t i = 0, n = inputs.size(); i < n; ++i) {
506506
FUZZTEST_CHECK_EQ(inputs[i].size(), 1);
@@ -588,10 +588,10 @@ class FunctionFilterMock : public CentipedeCallbacks {
588588
}
589589

590590
// Executes the target in the normal way.
591-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
592-
BatchResult &batch_result) override {
593-
return ExecuteCentipedeSancovBinaryWithShmem(env_.binary, inputs,
594-
batch_result) == EXIT_SUCCESS;
591+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
592+
BatchResult& batch_result, absl::Time deadline) override {
593+
return ExecuteCentipedeSancovBinaryWithShmem(
594+
env_.binary, inputs, batch_result, deadline) == EXIT_SUCCESS;
595595
}
596596

597597
// Sets the inputs to one of 3 pre-defined values.
@@ -696,8 +696,8 @@ class ExtraBinariesMock : public CentipedeCallbacks {
696696

697697
// Doesn't execute anything.
698698
// On certain combinations of {binary,input} returns false.
699-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
700-
BatchResult &batch_result) override {
699+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
700+
BatchResult& batch_result, absl::Time deadline) override {
701701
bool res = true;
702702
for (const auto &input : inputs) {
703703
if (input.size() != 1) continue;
@@ -814,8 +814,8 @@ class UndetectedCrashingInputMock : public CentipedeCallbacks {
814814
// Doesn't execute anything.
815815
// Crash when 0th char of input to binary b1 equals `crashing_input_idx_`, but
816816
// only on 1st exec.
817-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
818-
BatchResult &batch_result) override {
817+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
818+
BatchResult& batch_result, absl::Time deadline) override {
819819
batch_result.ClearAndResize(inputs.size());
820820
bool res = true;
821821
if (!first_pass_) {
@@ -982,7 +982,8 @@ TEST_F(CentipedeWithTemporaryLocalDir, CleansUpMetadataAfterStartup) {
982982

983983
BatchResult batch_result;
984984
const std::vector<ByteArray> inputs = {{0}};
985-
ASSERT_TRUE(callbacks.Execute(env.binary, inputs, batch_result));
985+
ASSERT_TRUE(callbacks.Execute(env.binary, inputs, batch_result,
986+
/*deadline=*/absl::InfiniteFuture()));
986987
ASSERT_EQ(batch_result.results().size(), 1);
987988
bool found_startup_cmp_entry = false;
988989
batch_result.results()[0].metadata().ForEachCmpEntry(
@@ -999,8 +1000,8 @@ class FakeCentipedeCallbacksForThreadChecking : public CentipedeCallbacks {
9991000
std::thread::id execute_thread_id)
10001001
: CentipedeCallbacks(env), execute_thread_id_(execute_thread_id) {}
10011002

1002-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
1003-
BatchResult &batch_result) override {
1003+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
1004+
BatchResult& batch_result, absl::Time deadline) override {
10041005
batch_result.ClearAndResize(inputs.size());
10051006
thread_check_passed_ = thread_check_passed_ &&
10061007
std::this_thread::get_id() == execute_thread_id_;
@@ -1044,7 +1045,8 @@ TEST_F(CentipedeWithTemporaryLocalDir, DetectsStackOverflow) {
10441045
BatchResult batch_result;
10451046
const std::vector<ByteArray> inputs = {ByteArray{'s', 't', 'k'}};
10461047

1047-
ASSERT_FALSE(callbacks.Execute(env.binary, inputs, batch_result));
1048+
ASSERT_FALSE(callbacks.Execute(env.binary, inputs, batch_result,
1049+
/*deadline=*/absl::InfiniteFuture()));
10481050
EXPECT_THAT(batch_result.log(), HasSubstr("Stack limit exceeded"));
10491051
EXPECT_EQ(batch_result.failure_description(), "stack-limit-exceeded");
10501052
}
@@ -1053,8 +1055,8 @@ class SetupFailureCallbacks : public CentipedeCallbacks {
10531055
public:
10541056
using CentipedeCallbacks::CentipedeCallbacks;
10551057

1056-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
1057-
BatchResult &batch_result) override {
1058+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
1059+
BatchResult& batch_result, absl::Time deadline) override {
10581060
++execute_count_;
10591061
batch_result.ClearAndResize(inputs.size());
10601062
batch_result.exit_code() = EXIT_FAILURE;
@@ -1090,8 +1092,8 @@ class SkippedTestCallbacks : public CentipedeCallbacks {
10901092
public:
10911093
using CentipedeCallbacks::CentipedeCallbacks;
10921094

1093-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
1094-
BatchResult &batch_result) override {
1095+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
1096+
BatchResult& batch_result, absl::Time deadline) override {
10951097
++execute_count_;
10961098
batch_result.ClearAndResize(inputs.size());
10971099
batch_result.exit_code() = EXIT_FAILURE;
@@ -1128,8 +1130,8 @@ class IgnoredFailureCallbacks : public CentipedeCallbacks {
11281130
public:
11291131
using CentipedeCallbacks::CentipedeCallbacks;
11301132

1131-
bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
1132-
BatchResult &batch_result) override {
1133+
bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
1134+
BatchResult& batch_result, absl::Time daedline) override {
11331135
++execute_count_;
11341136
batch_result.ClearAndResize(inputs.size());
11351137
batch_result.exit_code() = EXIT_FAILURE;
@@ -1224,7 +1226,8 @@ TEST_F(CentipedeWithTemporaryLocalDir, HangingFuzzTargetExitsAfterTimeout) {
12241226
env.fork_server = false;
12251227

12261228
// Test that the process does not get stuck and exits promptly.
1227-
EXPECT_FALSE(callbacks.Execute(env.binary, {{0}}, batch_result));
1229+
EXPECT_FALSE(callbacks.Execute(env.binary, {{0}}, batch_result,
1230+
/*deadline=*/absl::InfiniteFuture()));
12281231
}
12291232

12301233
} // namespace

centipede/environment.cc

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -285,17 +285,6 @@ void Environment::UpdateWithTargetConfig(
285285
timeout_per_input = time_limit_per_input_sec;
286286
UpdateTimeoutPerBatchIfEqualTo(autocomputed_timeout_per_batch);
287287

288-
// Adjust `timeout_per_batch` to never exceed the test time limit.
289-
if (const auto test_time_limit = config.GetTimeLimitPerTest();
290-
test_time_limit < absl::InfiniteDuration()) {
291-
const size_t test_time_limit_seconds =
292-
convert_to_seconds(test_time_limit, "Test time limit");
293-
timeout_per_batch =
294-
timeout_per_batch == 0
295-
? test_time_limit_seconds
296-
: std::min(timeout_per_batch, test_time_limit_seconds);
297-
}
298-
299288
// Convert bytes to MB by rounding up.
300289
constexpr auto bytes_to_mb = [](size_t bytes) {
301290
return bytes == 0 ? 0 : (bytes - 1) / 1024 / 1024 + 1;

centipede/environment_test.cc

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -147,30 +147,6 @@ TEST(Environment,
147147
EXPECT_EQ(env.timeout_per_batch, 0);
148148
}
149149

150-
TEST(Environment, UpdatesTimeoutPerBatchFromTargetConfigTimeLimit) {
151-
Environment env;
152-
fuzztest::internal::Configuration config;
153-
config.time_limit = absl::Seconds(123);
154-
config.time_budget_type = fuzztest::internal::TimeBudgetType::kPerTest;
155-
FUZZTEST_CHECK(config.GetTimeLimitPerTest() == absl::Seconds(123));
156-
env.UpdateWithTargetConfig(config);
157-
EXPECT_EQ(env.timeout_per_batch, 123)
158-
<< "`timeout_per_batch` should be set to the test time limit when it was "
159-
"previously unset";
160-
161-
env.timeout_per_batch = 456;
162-
env.UpdateWithTargetConfig(config);
163-
EXPECT_EQ(env.timeout_per_batch, 123)
164-
<< "`timeout_per_batch` should be set to test time limit when it is "
165-
"shorter than the previous value";
166-
167-
env.timeout_per_batch = 56;
168-
env.UpdateWithTargetConfig(config);
169-
EXPECT_EQ(env.timeout_per_batch, 56)
170-
<< "`timeout_per_batch` should not be updated with the test time limit "
171-
"when it is longer than the previous value";
172-
}
173-
174150
TEST(Environment, UpdatesRssLimitMbFromTargetConfigRssLimit) {
175151
Environment env;
176152
env.rss_limit_mb = Environment::Default().rss_limit_mb;

0 commit comments

Comments
 (0)