From 47313a11d9790244244e48b9dd3789d2aa5d7122 Mon Sep 17 00:00:00 2001 From: Xinhao Yuan Date: Wed, 19 Aug 2026 14:45:31 -0700 Subject: [PATCH] Move flag reading from worker to runner_utils with some tests. This is to prepare for consolidating the flag reading with runner/sancov libraries. PiperOrigin-RevId: 967420293 --- centipede/BUILD | 9 + centipede/centipede_callbacks.cc | 5 +- centipede/engine_worker.cc | 179 +++++++----------- centipede/minimize_crash.cc | 2 + centipede/runner.cc | 19 +- centipede/runner.h | 4 + centipede/runner_utils.h | 73 +++++++ centipede/runner_utils_test.cc | 47 +++++ centipede/shared_memory_blob_sequence.cc | 8 +- centipede/shared_memory_blob_sequence.h | 6 +- centipede/shared_memory_blob_sequence_test.cc | 6 +- 11 files changed, 230 insertions(+), 128 deletions(-) create mode 100644 centipede/runner_utils_test.cc diff --git a/centipede/BUILD b/centipede/BUILD index dcaabf1c8..2062db85c 100644 --- a/centipede/BUILD +++ b/centipede/BUILD @@ -1730,6 +1730,15 @@ cc_test( ], ) +cc_test( + name = "runner_utils_test", + srcs = ["runner_utils_test.cc"], + deps = [ + ":runner_utils", + "@googletest//:gtest_main", + ], +) + cc_binary( name = "command_test_helper", srcs = ["command_test_helper.cc"], diff --git a/centipede/centipede_callbacks.cc b/centipede/centipede_callbacks.cc index 961e93535..6a3afa52b 100644 --- a/centipede/centipede_callbacks.cc +++ b/centipede/centipede_callbacks.cc @@ -399,8 +399,9 @@ CentipedeCallbacks::GetOrCreateCommandContextForBinary( } std::vector env_diff = env_.env_diff_for_binaries; env_diff.push_back(ConstructRunnerFlags( - absl::StrCat(":shmem:test=", env_.test_name, ":arg1=", - inputs_blobseq_.path(), ":arg2=", outputs_blobseq_.path(), + absl::StrCat(":shmem_size_mb=", env_.shmem_size_mb, + ":test=", env_.test_name, ":arg1=", inputs_blobseq_.path(), + ":arg2=", outputs_blobseq_.path(), ":failure_description_path=", failure_description_path_, ":failure_signature_path=", failure_signature_path_, persistent_mode_server == nullptr diff --git a/centipede/engine_worker.cc b/centipede/engine_worker.cc index 3c95801fd..b37c91269 100644 --- a/centipede/engine_worker.cc +++ b/centipede/engine_worker.cc @@ -99,82 +99,39 @@ inline void WorkerCheck(bool condition, std::string_view error) { std::_Exit(1); } } - -struct WorkerFlags { - bool present; - // length of the flags string, excluding the ending '\0'. - size_t len; - const char* str; -}; +const char* absl_nullable GetWorkerFlagsEnv() { + static const char* flags = []() -> const char* { + // TODO(xinhaoyuan): Rename the env name to FUZZTEST_WORKER_FLAGS. + if (const char* env = std::getenv("CENTIPEDE_RUNNER_FLAGS")) { + WorkerLog("Worker flags: ", env); + char* env_copy = strdup(env); + if (env_copy == nullptr) { + // This should rarely happen. + WorkerLog("Failed to copy the flags env due to allocation failure"); + std::_Exit(1); + } + return env_copy; + } + return nullptr; + }(); + return flags; +} // The first call of this function must be outside of signal handlers since it // allocates memory (enforced by `WorkerInitEarly`). After that it would be // signal-safe. -// -// The worker flags format is `:(NAME=VALUE|SWITCH:)+`. `GetWorkerFlags` -// replaces `:` with '\0' so that we can get null-terminated strings of VALUE -// without copying them, which is important for signal-safety. -const WorkerFlags& GetWorkerFlags() { - static auto worker_flags = []() -> WorkerFlags { - // TODO(xinhaoyuan): Rename the env name to FUZZTEST_WORKER_FLAGS. - const char* env_flags = std::getenv("CENTIPEDE_RUNNER_FLAGS"); - if (env_flags == nullptr) { - return {}; - } - const size_t len = strlen(env_flags); - char* str = reinterpret_cast(malloc(len + 1)); - if (str == nullptr) { - WorkerLog("Cannot allocate the worker flags", LogLnSync{}); +const EngineFlagHelper& GetWorkerFlags() { + static ExplicitLifetime worker_flags; + [[maybe_unused]] static bool construct_once = [] { + worker_flags.Construct(GetWorkerFlagsEnv()); + if (worker_flags->HasAllocationFailure()) { + // This should rarely happen. + WorkerLog("Failed to process the flags due to allocation failure."); std::_Exit(1); } - memcpy(str, env_flags, len); - str[len] = 0; - WorkerLog("Got worker flags ", std::string_view{str, len}, LogLnSync{}); - // Post-processing to make '\0' as the separator, making each item as a - // null-terminating string to be used without copying it. - for (size_t i = 0; i < len; ++i) { - if (str[i] == ':') str[i] = 0; - } - return WorkerFlags{true, len, str}; + return true; }(); - return worker_flags; -} - -// `header` should be in the form of `FLAG_NAME=`. -// -// Extracts "value" as a null-terminated string from "\0FLAG_NAME=value\0" in -// the flags. Returns nullptr if it is not found. -const char* GetWorkerFlag(std::string_view header) { - if (header.empty()) return nullptr; - const auto& worker_flags = GetWorkerFlags(); - if (!worker_flags.present) return nullptr; - const auto flags = std::string_view{worker_flags.str, worker_flags.len}; - size_t pos = 0; - while (pos = flags.find(header, pos), - pos != flags.npos && pos + header.size() < flags.size()) { - if (pos > 0 && flags[pos - 1] == '\0') { - return worker_flags.str + pos + header.size(); - } - pos += header.size(); - } - return nullptr; -} - -// Checks whether "\0{name}\0" exists in the flags. -bool HasWorkerSwitchFlag(std::string_view name) { - if (name.empty()) return false; - const auto& worker_flags = GetWorkerFlags(); - if (!worker_flags.present) return false; - const auto flags = std::string_view{worker_flags.str, worker_flags.len}; - size_t pos = 0; - while (pos = flags.find(name, pos), - pos != flags.npos && pos + name.size() < flags.size()) { - if (pos > 0 && flags[pos - 1] == '\0' && flags[pos + name.size()] == '\0') { - return true; - } - pos += name.size(); - } - return false; + return *worker_flags; } template @@ -241,6 +198,7 @@ constexpr std::string_view kWorkerPersistentModeSocketPathFlagHeader = "persistent_mode_socket="; // TODO: Use better flag names when // standardizing the protocol. constexpr std::string_view kWorkerCrossOverLevel = "crossover_level="; +constexpr std::string_view kWorkerShmemSizeMbFlagHeader = "shmem_size_mb="; struct WorkerState { std::atomic has_failure_output = false; @@ -277,8 +235,8 @@ bool WorkerEmitFailureOutput(std::string_view prefix, std::string_view message) { bool ignored = GetWorkerState().has_failure_output.exchange(true); if (!ignored) { - if (const char* failure_description_path = - GetWorkerFlag(kWorkerFailureDescriptionPathFlagHeader); + if (const char* failure_description_path = GetWorkerFlags().GetStringFlag( + kWorkerFailureDescriptionPathFlagHeader); failure_description_path != nullptr) { TrySetFileContents(failure_description_path, /*append=*/false, prefix, message); @@ -322,8 +280,8 @@ void WorkerEmitFinding(std::string_view description, if (!ignored) { WorkerCheck(WorkerEmitFailureOutput(/*prefix=*/"", description), "Failed to emit failure output for the finding"); - if (const char* finding_signature_path = - GetWorkerFlag(kWorkerFailureSignaturePathFlagHeader); + if (const char* finding_signature_path = GetWorkerFlags().GetStringFlag( + kWorkerFailureSignaturePathFlagHeader); finding_signature_path != nullptr) { TrySetFileContents(finding_signature_path, /*append=*/false, signature); @@ -351,7 +309,7 @@ static int persistent_mode_socket; __attribute__((constructor(200))) void WorkerInitEarly() { const char* persistent_mode_socket_path = - GetWorkerFlag(kWorkerPersistentModeSocketPathFlagHeader); + GetWorkerFlags().GetStringFlag(kWorkerPersistentModeSocketPathFlagHeader); if (persistent_mode_socket_path == nullptr) return; persistent_mode_socket = socket(AF_UNIX, SOCK_STREAM, 0); if (persistent_mode_socket < 0) { @@ -407,40 +365,48 @@ __attribute__((constructor(200))) void WorkerInitEarly() { LogLnSync{}); } +size_t GetShmemSize() { + static auto result = []() -> size_t { + const uint64_t shmem_size_mb = + GetWorkerFlags().HasIntFlag(kWorkerShmemSizeMbFlagHeader, 0); + return static_cast(shmem_size_mb) << 20; + }(); + return result; +} + BlobSequence* GetInputsBlobSequence() { static auto result = []() -> BlobSequence* { - if (!HasWorkerSwitchFlag("shmem")) { + const size_t shmem_size = GetShmemSize(); + if (shmem_size == 0) { return nullptr; } const char* input_path = - GetWorkerFlag(kWorkerInputsBlobSequencePathFlagHeader); + GetWorkerFlags().GetStringFlag(kWorkerInputsBlobSequencePathFlagHeader); WorkerCheck(input_path != nullptr, "inputs blob sequence is missing"); - return new SharedMemoryBlobSequence(input_path); + return new SharedMemoryBlobSequence(input_path, shmem_size); }(); return result; } BlobSequence* GetOutputsBlobSequence() { static auto result = []() -> BlobSequence* { - if (!HasWorkerSwitchFlag("shmem")) { + const size_t shmem_size = GetShmemSize(); + if (shmem_size == 0) { return nullptr; } - const char* output_path = - GetWorkerFlag(kWorkerOutputsBlobSequencePathFlagHeader); + const char* output_path = GetWorkerFlags().GetStringFlag( + kWorkerOutputsBlobSequencePathFlagHeader); WorkerCheck(output_path != nullptr, "outputs blob sequence is missing"); - return new SharedMemoryBlobSequence(output_path); + return new SharedMemoryBlobSequence(output_path, shmem_size); }(); return result; } int GetCrossOverLevel() { static int result = []() { - const char* cross_over_level_str = GetWorkerFlag(kWorkerCrossOverLevel); - if (cross_over_level_str != nullptr) { - const int parsed = - atoi(cross_over_level_str); // NOLINT: can't use strto64, etc. - if (0 <= parsed && parsed <= 100) return parsed; - } + const uint64_t cross_over_level = + GetWorkerFlags().HasIntFlag(kWorkerCrossOverLevel, 50); + if (cross_over_level <= 100) return static_cast(cross_over_level); // Default return 50; }(); @@ -449,16 +415,16 @@ int GetCrossOverLevel() { std::optional GetWorkerAction() { static auto worker_action = []() -> std::optional { - if (HasWorkerSwitchFlag("dump_configuration")) { + if (GetWorkerFlags().HasSwitchFlag("dump_configuration")) { return WorkerAction::kNoOp; } - if (HasWorkerSwitchFlag("dump_binary_id")) { + if (GetWorkerFlags().HasSwitchFlag("dump_binary_id")) { return WorkerAction::kGetBinaryId; } - if (HasWorkerSwitchFlag("list_tests")) { + if (GetWorkerFlags().HasSwitchFlag("list_tests")) { return WorkerAction::kListTests; } - if (HasWorkerSwitchFlag("dump_seed_inputs")) { + if (GetWorkerFlags().HasSwitchFlag("dump_seed_inputs")) { return WorkerAction::kTestGetSeeds; } auto* inputs_blobseq = GetInputsBlobSequence(); @@ -499,7 +465,7 @@ FuzzTestInputSink GetInputSinkTo(std::vector& inputs) { void WorkerDoGetBinaryId(const FuzzTestAdapterManager& manager) { if (GetWorkerState().saved_binary_id.exchange(true)) return; const char* binary_id_output_path = - GetWorkerFlag(kWorkerBinaryIdOutputFlagHeader); + GetWorkerFlags().GetStringFlag(kWorkerBinaryIdOutputFlagHeader); WorkerCheck(binary_id_output_path != nullptr, "binary ID output path is not set"); std::vector binary_id; @@ -512,7 +478,7 @@ void WorkerDoGetBinaryId(const FuzzTestAdapterManager& manager) { void WorkerDoListCurrentTest(std::string_view test_name) { const char* test_listing_output_path = - GetWorkerFlag(kWorkerTestListingOutputFlagHeader); + GetWorkerFlags().GetStringFlag(kWorkerTestListingOutputFlagHeader); WorkerCheck(test_listing_output_path != nullptr, "binary ID output path is not set"); TrySetFileContents(test_listing_output_path, @@ -536,7 +502,7 @@ void WorkerDoGetSeeds(const FuzzTestAdapter& adapter) { } static const char* output_dir = - GetWorkerFlag(kWorkerTestGetSeedsOutputDirFlagHeader); + GetWorkerFlags().GetStringFlag(kWorkerTestGetSeedsOutputDirFlagHeader); WorkerCheck(output_dir != nullptr, "seeds output path must be specified"); for (size_t i = 0; i < seed_handles.size(); ++i) { @@ -845,7 +811,7 @@ void WorkerDoExecute(const FuzzTestAdapter& adapter) { const char* FuzzTestWorkerGetTestName() { static auto test_name = []() -> const char* { - return GetWorkerFlag(kWorkerTestNameFlagHeader); + return GetWorkerFlags().GetStringFlag(kWorkerTestNameFlagHeader); }(); return test_name; } @@ -872,15 +838,12 @@ void HandlePersistentMode(const FuzzTestAdapter& adapter) { // to happen when the stdout/stderr are not redirected to a file. (void)ftruncate(fd, 0); } - WorkerLog( - "FuzzTest engine worker (", - req == PersistentModeRequest::kExit ? "exiting persistent mode" - : "persistent mode batch", - "); flags: ", - GetWorkerFlags().present - ? std::string_view{GetWorkerFlags().str, GetWorkerFlags().len} - : "", - LogLnSync{}); + WorkerLog("FuzzTest engine worker (", + req == PersistentModeRequest::kExit ? "exiting persistent mode" + : "persistent mode batch", + "); flags: ", + GetWorkerFlagsEnv() != nullptr ? GetWorkerFlagsEnv() : "", + LogLnSync{}); } if (req == PersistentModeRequest::kExit) break; WorkerCheck(req == PersistentModeRequest::kRunBatch, @@ -917,10 +880,9 @@ void HandlePersistentMode(const FuzzTestAdapter& adapter) { } FuzzTestWorkerStatus WorkerRun(const FuzzTestAdapterManager& manager) { - const auto& flags = GetWorkerFlags(); - WorkerCheck(flags.present, "worker flags must present"); + WorkerCheck(GetWorkerFlagsEnv() != nullptr, "worker flags must present"); - if (HasWorkerSwitchFlag("dump_configuration")) { + if (GetWorkerFlags().HasSwitchFlag("dump_configuration")) { return kFuzzTestWorkerSuccess; } @@ -1018,13 +980,14 @@ FuzzTestWorkerStatus WorkerRun(const FuzzTestAdapterManager& manager) { namespace { using ::fuzztest::internal::GetWorkerFlags; +using ::fuzztest::internal::GetWorkerFlagsEnv; using ::fuzztest::internal::WorkerCheck; using ::fuzztest::internal::WorkerRun; } // namespace int FuzzTestWorkerIsRequired() { - static int result = GetWorkerFlags().present && + static int result = GetWorkerFlagsEnv() != nullptr && fuzztest::internal::GetWorkerAction().has_value(); return result; } diff --git a/centipede/minimize_crash.cc b/centipede/minimize_crash.cc index 0e1238269..4399dcb0b 100644 --- a/centipede/minimize_crash.cc +++ b/centipede/minimize_crash.cc @@ -143,6 +143,7 @@ void MinimizeCrash(ByteSpan crashy_input, const Environment& env, auto callbacks = scoped_callback.callbacks(); FUZZTEST_LOG(INFO) << "MinimizeCrash: trying the original crashy input"; + CreateLocalDirRemovedAtExit(TemporaryLocalDirPath()); BatchResult batch_result; ByteArray original_crashy_input(crashy_input.begin(), crashy_input.end()); @@ -163,6 +164,7 @@ void MinimizeCrash(ByteSpan crashy_input, const Environment& env, ThreadPool threads{static_cast(env.num_threads)}; for (size_t i = 0; i < env.num_threads; ++i) { threads.Schedule([&env, &callbacks_factory, &queue, &stop_condition]() { + CreateLocalDirRemovedAtExit(TemporaryLocalDirPath()); MinimizeCrash(env, callbacks_factory, queue, stop_condition); }); } diff --git a/centipede/runner.cc b/centipede/runner.cc index 40e4f39bf..847f6d8b8 100644 --- a/centipede/runner.cc +++ b/centipede/runner.cc @@ -902,9 +902,10 @@ void GlobalRunnerState::OnTermination() { // This means, the binary is standalone with its own main(), and we need to // report the coverage now. if (!state->centipede_runner_main_executed && - flag_helper.HasFlag(":shmem:")) { + state->run_time_flags.shmem_size_mb != 0) { PostProcessSancov(); // TODO(xinhaoyuan): do we know our exit status? - SharedMemoryBlobSequence outputs_blobseq(sancov_state->arg2); + SharedMemoryBlobSequence outputs_blobseq( + sancov_state->arg2, state->run_time_flags.shmem_size_mb << 20); StartSendingOutputsToEngine(outputs_blobseq); FinishSendingOutputsToEngine(outputs_blobseq); } @@ -987,9 +988,9 @@ static int HandlePersistentMode(RunnerCallbacks& callbacks, return EXIT_SUCCESS; } -// If HasFlag(:shmem:), state->arg1 and state->arg2 are the names -// of in/out shared memory locations. -// Read inputs and write outputs via shared memory. +// If state->run_time_flags.shmem_size_mb is non-zero, state->arg1 and +// state->arg2 are the names of in/out shared memory locations. Read inputs and +// write outputs via shared memory. // // Default: Execute ReadOneInputExecuteItAndDumpCoverage() for all inputs.// // @@ -1013,10 +1014,12 @@ int RunnerMain(int argc, char** argv, RunnerCallbacks& callbacks) { } // Inputs / outputs from shmem. - if (state->flag_helper.HasFlag(":shmem:")) { + if (state->run_time_flags.shmem_size_mb != 0) { if (!sancov_state->arg1 || !sancov_state->arg2) return EXIT_FAILURE; - SharedMemoryBlobSequence inputs_blobseq(sancov_state->arg1); - SharedMemoryBlobSequence outputs_blobseq(sancov_state->arg2); + SharedMemoryBlobSequence inputs_blobseq( + sancov_state->arg1, state->run_time_flags.shmem_size_mb << 20); + SharedMemoryBlobSequence outputs_blobseq( + sancov_state->arg2, state->run_time_flags.shmem_size_mb << 20); // Persistent mode loop. if (state->persistent_mode_socket > 0) { return HandlePersistentMode(callbacks, inputs_blobseq, outputs_blobseq); diff --git a/centipede/runner.h b/centipede/runner.h index 5ddc3b3e5..2a5d23b3a 100644 --- a/centipede/runner.h +++ b/centipede/runner.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "./centipede/byte_array_mutator.h" @@ -38,6 +39,7 @@ struct RunTimeFlags { uint64_t ignore_timeout_reports : 1; uint64_t max_len; std::atomic stack_limit_kb; + size_t shmem_size_mb; }; // One global object of this type is created by the runner at start up. @@ -72,6 +74,8 @@ struct GlobalRunnerState { flag_helper.HasFlag(":ignore_timeout_reports:"), /*max_len=*/flag_helper.HasIntFlag(":max_len=", 4000), /*stack_limit_kb=*/flag_helper.HasIntFlag(":stack_limit_kb=", 0), + /*shmem_size_mb=*/ + static_cast(flag_helper.HasIntFlag(":shmem_size_mb=", 0)), }; // The path to a file where the runner may write the description of failure. diff --git a/centipede/runner_utils.h b/centipede/runner_utils.h index 4b85afa6d..b8c5d673c 100644 --- a/centipede/runner_utils.h +++ b/centipede/runner_utils.h @@ -17,9 +17,13 @@ #include +#include #include #include +#include +#include #include +#include #include #include "absl/base/nullability.h" @@ -125,6 +129,75 @@ class ExplicitLifetime { alignas(T) unsigned char space_[sizeof(T)]; }; +// Helper class for processing and reading the engine flags. +class EngineFlagHelper { + public: + // Constructs the helper for a C-string `flags` with the format of :(ENTRY:)+. + explicit EngineFlagHelper(const char* absl_nullable flags) + : flags_(nullptr), size_(0), has_allocation_failure_(false) { + if (flags == nullptr) return; + flags_ = strdup(flags); + if (flags_ == nullptr) { + has_allocation_failure_ = true; + return; + } + size_ = strlen(flags_); + // Post-processing to make '\0' as the separator, making each item as a + // null-terminating string to be used without copying it. + for (size_t i = 0; i < size_; ++i) { + if (flags_[i] == ':') flags_[i] = 0; + } + } + + ~EngineFlagHelper() { + if (flags_) { + free(flags_); + } + } + + bool HasAllocationFailure() const { return has_allocation_failure_; } + + bool HasSwitchFlag(std::string_view name) const { + if (name.empty() || flags_ == nullptr) return false; + const auto flags = std::string_view{flags_, size_}; + size_t pos = 0; + while (pos = flags.find(name, pos), + pos != flags.npos && pos + name.size() < flags.size()) { + if (pos > 0 && flags[pos - 1] == '\0' && + flags[pos + name.size()] == '\0') { + return true; + } + pos += name.size(); + } + return false; + } + + uint64_t HasIntFlag(std::string_view header, uint64_t default_value) const { + const char* absl_nullable flag = GetStringFlag(header); + if (flag == nullptr) return default_value; + return atoll(flag); // NOLINT: can't use strto64, etc. + } + + const char* absl_nullable GetStringFlag(std::string_view header) const { + if (header.empty() || flags_ == nullptr) return nullptr; + const auto flags = std::string_view{flags_, size_}; + size_t pos = 0; + while (pos = flags.find(header, pos), + pos != flags.npos && pos + header.size() < flags.size()) { + if (pos > 0 && flags[pos - 1] == '\0') { + return flags.data() + pos + header.size(); + } + pos += header.size(); + } + return nullptr; + } + + private: + char* absl_nullable flags_; + size_t size_; + bool has_allocation_failure_; +}; + } // namespace fuzztest::internal #endif // THIRD_PARTY_CENTIPEDE_RUNNER_UTILS_H_ diff --git a/centipede/runner_utils_test.cc b/centipede/runner_utils_test.cc new file mode 100644 index 000000000..ac7166186 --- /dev/null +++ b/centipede/runner_utils_test.cc @@ -0,0 +1,47 @@ +// Copyright 2026 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. + +#include "./centipede/runner_utils.h" + +#include + +#include "gtest/gtest.h" + +namespace fuzztest::internal { +namespace { + +TEST(RunnerUtilsTest, EngineFlagHelperWorksWithoutFlags) { + EngineFlagHelper helper(nullptr); + EXPECT_FALSE(helper.HasSwitchFlag("foo")); + EXPECT_EQ(helper.HasIntFlag("bar=", 42), 42); + EXPECT_EQ(helper.GetStringFlag("baz="), nullptr); +} + +TEST(RunnerUtilsTest, EngineFlagHelperWorksWithFlags) { + EngineFlagHelper helper(":flag1:flag2=123:str=hello:"); + EXPECT_TRUE(helper.HasSwitchFlag("flag1")); + EXPECT_FALSE(helper.HasSwitchFlag("flag")); + EXPECT_FALSE(helper.HasSwitchFlag("flag1_extra")); + EXPECT_FALSE(helper.HasSwitchFlag("flag2")); + EXPECT_FALSE(helper.HasSwitchFlag("missing")); + + EXPECT_EQ(helper.HasIntFlag("flag2=", 0), 123); + EXPECT_EQ(helper.HasIntFlag("missing=", 999), 999); + + EXPECT_STREQ(helper.GetStringFlag("str="), "hello"); + EXPECT_EQ(helper.GetStringFlag("missing="), nullptr); +} + +} // namespace +} // namespace fuzztest::internal diff --git a/centipede/shared_memory_blob_sequence.cc b/centipede/shared_memory_blob_sequence.cc index 3a2412649..51415df13 100644 --- a/centipede/shared_memory_blob_sequence.cc +++ b/centipede/shared_memory_blob_sequence.cc @@ -135,7 +135,10 @@ SharedMemoryBlobSequence::SharedMemoryBlobSequence(const char *name, MmapData(); } -SharedMemoryBlobSequence::SharedMemoryBlobSequence(const char *path) { +SharedMemoryBlobSequence::SharedMemoryBlobSequence(const char* path, + size_t size) { + ErrorOnFailure(size < sizeof(Blob::size), "Size too small"); + size_ = size; // This is a quick way to tell shm-allocated paths from memfd paths without // requiring the caller to specify. if (strncmp(path, "/proc/", 6) == 0) { @@ -146,9 +149,6 @@ SharedMemoryBlobSequence::SharedMemoryBlobSequence(const char *path) { ErrorOnFailure(fd_ < 0, "open() failed"); strncpy(path_, path, PATH_MAX); ErrorOnFailure(path_[PATH_MAX - 1] != 0, "path length exceeds PATH_MAX."); - struct stat statbuf = {}; - ErrorOnFailure(fstat(fd_, &statbuf), "fstat() failed"); - size_ = statbuf.st_size; MmapData(); } diff --git a/centipede/shared_memory_blob_sequence.h b/centipede/shared_memory_blob_sequence.h index fc69c10dd..b5d781e3c 100644 --- a/centipede/shared_memory_blob_sequence.h +++ b/centipede/shared_memory_blob_sequence.h @@ -134,7 +134,7 @@ class BlobSequence { // // void Child() { // // Open an existing blob sequence. -// SharedMemoryBlobSequence child("/foo"); +// SharedMemoryBlobSequence child("/foo", 1000); // // // Read the data written by parent. // while (true) { @@ -155,9 +155,9 @@ class SharedMemoryBlobSequence : public BlobSequence { // memfd_create(2). SharedMemoryBlobSequence(const char *name, size_t size, bool use_posix_shmem); - // Opens an existing shared blob sequence with the file `path`. + // Opens an existing shared blob sequence with the file `path` and `size`. // Aborts on any failure. - explicit SharedMemoryBlobSequence(const char *path); + SharedMemoryBlobSequence(const char* path, size_t size); // Releases all resources. ~SharedMemoryBlobSequence(); diff --git a/centipede/shared_memory_blob_sequence_test.cc b/centipede/shared_memory_blob_sequence_test.cc index 2b9f55799..f5dd4fc45 100644 --- a/centipede/shared_memory_blob_sequence_test.cc +++ b/centipede/shared_memory_blob_sequence_test.cc @@ -112,7 +112,7 @@ TEST_P(SharedMemoryBlobSequenceTest, ParentChild) { EXPECT_TRUE(parent.Write(BlobFromVec(kTestData2, 456))); // Child created. - SharedMemoryBlobSequence child(parent.path()); + SharedMemoryBlobSequence child(parent.path(), 1000); // Child reads data. auto blob1 = child.Read(); EXPECT_EQ(kTestData1, Vec(blob1)); @@ -141,14 +141,14 @@ TEST_P(SharedMemoryBlobSequenceTest, CheckForResourceLeaks) { for (int iter = 0; iter < kNumIters; iter++) { SharedMemoryBlobSequence parent(ShmemName().c_str(), kBlobSize, GetParam()); parent.Write(BlobFromVec({1, 2, 3})); - SharedMemoryBlobSequence child(parent.path()); + SharedMemoryBlobSequence child(parent.path(), kBlobSize); EXPECT_EQ(child.Read().size, 3); } // Create a parent blob, then create and destroy lots of child blobs. SharedMemoryBlobSequence parent(ShmemName().c_str(), kBlobSize, GetParam()); parent.Write(BlobFromVec({1, 2, 3, 4})); for (int iter = 0; iter < kNumIters; iter++) { - SharedMemoryBlobSequence child(parent.path()); + SharedMemoryBlobSequence child(parent.path(), kBlobSize); EXPECT_EQ(child.Read().size, 4); } }