Skip to content
Merged
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
2 changes: 2 additions & 0 deletions centipede/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ cc_library(
"@abseil-cpp//absl/strings",
"@abseil-cpp//absl/strings:str_format",
"@abseil-cpp//absl/synchronization",
"@abseil-cpp//absl/time",
"@abseil-cpp//absl/types:span",
"@com_google_fuzztest//common:defs",
"@com_google_fuzztest//common:hash",
Expand Down Expand Up @@ -1394,6 +1395,7 @@ cc_test(
":thread_pool",
":util",
"@abseil-cpp//absl/container:flat_hash_map",
"@abseil-cpp//absl/time",
"@com_google_fuzztest//common:defs",
"@com_google_fuzztest//common:hash",
"@com_google_fuzztest//common:logging",
Expand Down
21 changes: 15 additions & 6 deletions centipede/centipede_callbacks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@

namespace fuzztest::internal {

constexpr auto kPollMinimalTimeout = absl::Milliseconds(1);

class CentipedeCallbacks::PersistentModeServer {
public:
explicit PersistentModeServer(std::string server_path)
Expand Down Expand Up @@ -194,9 +192,7 @@ class CentipedeCallbacks::PersistentModeServer {
int poll_ret = -1;
do {
poll_fd = {fd, static_cast<short>(event)};
const int poll_timeout_ms = static_cast<int>(absl::ToInt64Milliseconds(
std::max(deadline - absl::Now(), kPollMinimalTimeout)));
poll_ret = poll(&poll_fd, 1, poll_timeout_ms);
poll_ret = poll(&poll_fd, 1, PollTimeoutMs(deadline - absl::Now()));
} while (poll_ret < 0 && errno == EINTR);
if (poll_ret == 1 && (poll_fd.revents & (event | POLLHUP)) == event) {
return true;
Expand Down Expand Up @@ -343,7 +339,6 @@ std::string CentipedeCallbacks::ConstructRunnerFlags(
std::vector<std::string> flags = {
"CENTIPEDE_RUNNER_FLAGS=",
absl::StrCat("timeout_per_input=", env_.timeout_per_input),
absl::StrCat("timeout_per_batch=", env_.timeout_per_batch),
absl::StrCat("address_space_limit_mb=", env_.address_space_limit_mb),
absl::StrCat("rss_limit_mb=", env_.rss_limit_mb),
absl::StrCat("stack_limit_kb=", env_.stack_limit_kb),
Expand Down Expand Up @@ -565,8 +560,12 @@ int CentipedeCallbacks::ExecuteCentipedeSancovBinaryWithShmem(
}

// Run.
const auto batch_start_time = absl::Now();
const int exit_code = RunBatchForBinary(binary);
inputs_blobseq_.ReleaseSharedMemory(); // Inputs are already consumed.
const bool batch_timed_out =
env_.timeout_per_batch > 0 &&
absl::Now() - batch_start_time > absl::Seconds(env_.timeout_per_batch);

// Get results.
batch_result.exit_code() = exit_code;
Expand All @@ -591,6 +590,16 @@ int CentipedeCallbacks::ExecuteCentipedeSancovBinaryWithShmem(

if (env_.print_runner_log) PrintExecutionLog();

if (batch_timed_out) {
FUZZTEST_LOG(INFO)
<< "Batch timeout detected. Replacing the original exit code: "
<< exit_code
<< ", failure description: " << batch_result.failure_description();
batch_result.failure_description() = kExecutionFailurePerBatchTimeout;
batch_result.exit_code() = EXIT_FAILURE;
return EXIT_FAILURE;
}

// TODO: b/467103298 - Handle failures when the exit code is zero, e.g., when
// the target exits via `std::_Exit(0)`.
if (exit_code != EXIT_SUCCESS) {
Expand Down
4 changes: 1 addition & 3 deletions centipede/command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,7 @@ struct Command::ForkServerProps {
/*fd=*/pipe_[1],
/*events=*/POLLIN,
};
const int poll_timeout_ms = static_cast<int>(absl::ToInt64Milliseconds(
std::max(deadline - absl::Now(), absl::Milliseconds(1))));
poll_ret = poll(&poll_fd, 1, poll_timeout_ms);
poll_ret = poll(&poll_fd, 1, PollTimeoutMs(deadline - absl::Now()));
// The `poll()` syscall can get interrupted: it sets errno==EINTR in that
// case. We should tolerate that.
} while (poll_ret < 0 && errno == EINTR);
Expand Down
1 change: 0 additions & 1 deletion centipede/puzzles/run_puzzle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ function ExpectPerInputTimeout() {
# Expects that Centipede found a per-batch timeout.
function ExpectPerBatchTimeout() {
echo "======= ${FUNCNAME[0]}"
fuzztest::internal::assert_regex_in_file "Per-batch timeout exceeded" "${log}"
fuzztest::internal::assert_regex_in_file "Failure.*: per-batch-timeout-exceeded" "${log}"
fuzztest::internal::assert_regex_in_file \
"Failure applies to entire batch: not executing inputs one-by-one" "${log}"
Expand Down
24 changes: 4 additions & 20 deletions centipede/runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,7 @@ static void CheckWatchdogLimits() {
const char *failure;
};
const uint64_t input_start_time = state->input_start_time;
const uint64_t batch_start_time = state->batch_start_time;
if (input_start_time == 0 || batch_start_time == 0) return;
if (input_start_time == 0) return;
const Resource resources[] = {
{Resource{
/*what=*/"Per-input timeout",
Expand All @@ -144,15 +143,6 @@ static void CheckWatchdogLimits() {
state->run_time_flags.ignore_timeout_reports != 0,
/*failure=*/kExecutionFailurePerInputTimeout.data(),
}},
{Resource{
/*what=*/"Per-batch timeout",
/*units=*/"sec",
/*value=*/curr_time - batch_start_time,
/*limit=*/state->run_time_flags.timeout_per_batch,
/*ignore_report=*/
state->run_time_flags.ignore_timeout_reports != 0,
/*failure=*/kExecutionFailurePerBatchTimeout.data(),
}},
{Resource{
/*what=*/"RSS limit",
/*units=*/"MB",
Expand Down Expand Up @@ -240,10 +230,10 @@ __attribute__((noinline)) void CheckStackLimit(size_t stack_usage,
void GlobalRunnerState::StartWatchdogThread() {
fprintf(stderr,
"Starting watchdog thread: timeout_per_input: %" PRIu64
" sec; timeout_per_batch: %" PRIu64 " sec; rss_limit_mb: %" PRIu64
" MB; stack_limit_kb: %" PRIu64 " KB\n",
" sec; rss_limit_mb: %" PRIu64 " MB; stack_limit_kb: %" PRIu64
" KB\n",
run_time_flags.timeout_per_input.load(),
run_time_flags.timeout_per_batch, run_time_flags.rss_limit_mb.load(),
run_time_flags.rss_limit_mb.load(),
state->run_time_flags.stack_limit_kb.load());
pthread_t watchdog_thread;
pthread_create(&watchdog_thread, nullptr, WatchdogThread, nullptr);
Expand All @@ -257,11 +247,6 @@ void GlobalRunnerState::StartWatchdogThread() {
void GlobalRunnerState::ResetTimers() {
const auto curr_time = time(nullptr);
state->input_start_time = curr_time;
// batch_start_time is set only once -- just before the first input of the
// batch is about to start running.
if (batch_start_time == 0) {
batch_start_time = curr_time;
}
}

// Byte array mutation fallback for a custom mutator, as defined here:
Expand Down Expand Up @@ -994,7 +979,6 @@ extern "C" void CentipedeEndExecutionBatch() {
}
in_execution_batch = false;
fuzztest::internal::state->input_start_time = 0;
fuzztest::internal::state->batch_start_time = 0;
}

extern "C" void CentipedePrepareProcessing() {
Expand Down
6 changes: 0 additions & 6 deletions centipede/runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ namespace fuzztest::internal {
// Flags derived from CENTIPEDE_RUNNER_FLAGS.
struct RunTimeFlags {
std::atomic<uint64_t> timeout_per_input;
uint64_t timeout_per_batch;
std::atomic<uint64_t> rss_limit_mb;
uint64_t crossover_level;
uint64_t ignore_timeout_reports : 1;
Expand Down Expand Up @@ -67,7 +66,6 @@ struct GlobalRunnerState {
// flags can change later (if wrapped with std::atomic).
RunTimeFlags run_time_flags = {
/*timeout_per_input=*/flag_helper.HasIntFlag(":timeout_per_input=", 0),
/*timeout_per_batch=*/flag_helper.HasIntFlag(":timeout_per_batch=", 0),
/*rss_limit_mb=*/flag_helper.HasIntFlag(":rss_limit_mb=", 0),
/*crossover_level=*/flag_helper.HasIntFlag(":crossover_level=", 50),
/*ignore_timeout_reports=*/
Expand Down Expand Up @@ -112,10 +110,6 @@ struct GlobalRunnerState {
// time.
std::atomic<time_t> input_start_time;

// Per-batch timer. Initially, zero. ResetInputTimer() sets it to the current
// time before the first input and never resets it.
std::atomic<time_t> batch_start_time;

// The Watchdog thread sets this to true.
std::atomic<bool> watchdog_thread_started;
};
Expand Down
14 changes: 14 additions & 0 deletions centipede/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <fstream>
#include <functional>
#include <ios>
#include <limits>
#include <queue>
#include <random>
#include <sstream>
Expand All @@ -50,6 +51,7 @@
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "./centipede/feature.h"
#include "./common/defs.h"
Expand Down Expand Up @@ -369,4 +371,16 @@ void Munmap(uint8_t *ptr, size_t size) {
FUZZTEST_CHECK_EQ(result, 0);
}

int PollTimeoutMs(absl::Duration timeout) {
if (timeout == absl::InfiniteDuration()) {
return -1;
}
const auto ms = absl::ToInt64Milliseconds(absl::Ceil(
std::max(timeout, absl::Milliseconds(1)), absl::Milliseconds(1)));
if (ms > std::numeric_limits<int>::max()) {
return std::numeric_limits<int>::max();
}
return static_cast<int>(ms);
}

} // namespace fuzztest::internal
4 changes: 4 additions & 0 deletions centipede/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <vector>

#include "absl/base/nullability.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "./centipede/feature.h"
#include "./common/defs.h"
Expand Down Expand Up @@ -192,6 +193,9 @@ class MmapNoReserveArray {
uint8_t *array_;
};

// Converts `timeout` to an integer value of milliseconds suitable for `poll()`.
int PollTimeoutMs(absl::Duration timeout);

} // namespace fuzztest::internal

#endif // THIRD_PARTY_CENTIPEDE_UTIL_H_
13 changes: 13 additions & 0 deletions centipede/util_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
#include <cstdint>
#include <cstdlib>
#include <filesystem> // NOLINT
#include <limits>
#include <map>
#include <string>
#include <vector>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_map.h"
#include "absl/time/time.h"
#include "./centipede/feature.h"
#include "./centipede/thread_pool.h"
#include "./common/defs.h"
Expand Down Expand Up @@ -291,4 +293,15 @@ TEST(UtilTest, RemoveSubset) {
testing::ElementsAre(std::vector<int>{1}, std::vector<int>{3}));
}

TEST(UtilTest, PollTimeoutMsWorks) {
EXPECT_GT(PollTimeoutMs(absl::ZeroDuration()), 0);
EXPECT_GT(PollTimeoutMs(-absl::InfiniteDuration()), 0);
EXPECT_EQ(PollTimeoutMs(absl::InfiniteDuration()), -1);
EXPECT_EQ(PollTimeoutMs(absl::Milliseconds(123)), 123);
const auto long_finite_duration =
absl::Seconds(std::numeric_limits<int64_t>::max());
FUZZTEST_CHECK_LT(long_finite_duration, absl::InfiniteDuration());
EXPECT_GT(PollTimeoutMs(long_finite_duration), 0);
}

} // namespace fuzztest::internal
Loading