Skip to content

Commit 4e53ed8

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 4e53ed8

19 files changed

+120
-103
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/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;

centipede/minimize_crash.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ static void MinimizeCrash(const Environment &env,
122122
}
123123

124124
// Execute all mutants. If a new crasher is found, add it to `queue`.
125-
if (!callbacks->Execute(env.binary, smaller_mutants, batch_result)) {
125+
if (!callbacks->Execute(env.binary, smaller_mutants, batch_result,
126+
absl::InfiniteFuture())) {
126127
size_t crash_inputs_idx = batch_result.num_outputs_read();
127128
FUZZTEST_CHECK_LT(crash_inputs_idx, smaller_mutants.size());
128129
const auto &new_crasher = smaller_mutants[crash_inputs_idx];
@@ -142,7 +143,8 @@ int MinimizeCrash(ByteSpan crashy_input, const Environment &env,
142143

143144
BatchResult batch_result;
144145
ByteArray original_crashy_input(crashy_input.begin(), crashy_input.end());
145-
if (callbacks->Execute(env.binary, {original_crashy_input}, batch_result)) {
146+
if (callbacks->Execute(env.binary, {original_crashy_input}, batch_result,
147+
absl::InfiniteFuture())) {
146148
FUZZTEST_LOG(INFO) << "The original crashy input did not crash; exiting";
147149
return EXIT_FAILURE;
148150
}

0 commit comments

Comments
 (0)