Let runner/sancov use the shared EngineFlagHelper in runner_utils.

ALso change the flags-getting functions to be idempotent, which makes more sense.

PiperOrigin-RevId: 967822138
diff --git a/centipede/BUILD b/centipede/BUILD
index dcaabf1..108e5e0 100644
--- a/centipede/BUILD
+++ b/centipede/BUILD
@@ -1074,7 +1074,6 @@
 RUNNER_DEPS = [
     ":byte_array_mutator",
     ":callstack",
-    ":dispatcher_flag_helper",
     ":execution_metadata",
     ":feature",
     ":foreach_nonzero",
@@ -1209,15 +1208,6 @@
 )
 
 cc_library(
-    name = "dispatcher_flag_helper",
-    hdrs = ["dispatcher_flag_helper.h"],
-    copts = DISABLE_SANCOV_COPTS,
-    deps = [
-        "@abseil-cpp//absl/base:nullability",
-    ],
-)
-
-cc_library(
     name = "sancov_runtime",
     srcs = [
         "pc_info.h",
@@ -1238,7 +1228,6 @@
     copts = DISABLE_SANCOV_COPTS,
     deps = [
         ":callstack",
-        ":dispatcher_flag_helper",
         ":engine_abi",
         ":execution_metadata",
         ":feature",
@@ -1730,6 +1719,15 @@
     ],
 )
 
+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 961e935..6a3afa5 100644
--- a/centipede/centipede_callbacks.cc
+++ b/centipede/centipede_callbacks.cc
@@ -399,8 +399,9 @@
   }
   std::vector<std::string> 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/dispatcher_flag_helper.h b/centipede/dispatcher_flag_helper.h
deleted file mode 100644
index d143831..0000000
--- a/centipede/dispatcher_flag_helper.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2022 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.
-
-#ifndef FUZZTEST_CENTIPEDE_DISPATCHER_FLAG_HELPER_H_
-#define FUZZTEST_CENTIPEDE_DISPATCHER_FLAG_HELPER_H_
-
-#include <stdlib.h>
-
-#include <cstdint>
-#include <cstring>
-
-#include "absl/base/nullability.h"
-
-namespace fuzztest::internal {
-
-struct DispatcherFlagHelper {
-  // We don't use flags passed via argv so that argv flags can be passed
-  // directly to LLVMFuzzerInitialize, w/o filtering. The flags are separated
-  // with ':' on both sides, i.e. like this: ":flag1:flag2:flag3=value3".
-  // We do it this way to make the flag parsing code extremely simple. The
-  // interface is private between Centipede and the runner and may change.
-  DispatcherFlagHelper(const char *absl_nullable flags_) : flags(flags_) {}
-
-  const char *absl_nullable flags;
-
-  // Returns true iff `flag` is present.
-  // Typical usage: pass ":some_flag:", i.e. the flag name surrounded with ':'.
-  // TODO(ussuri): Refactor `char *` into a `string_view`.
-  bool HasFlag(const char *absl_nonnull flag) const {
-    if (!flags) return false;
-    return strstr(flags, flag) != nullptr;
-  }
-
-  // If a flag=value pair is present, returns value,
-  // otherwise returns `default_value`.
-  // Typical usage: pass ":some_flag=".
-  // TODO(ussuri): Refactor `char *` into a `string_view`.
-  uint64_t HasIntFlag(const char *absl_nonnull flag,
-                      uint64_t default_value) const {
-    if (!flags) return default_value;
-    const char *beg = strstr(flags, flag);
-    if (!beg) return default_value;
-    return atoll(beg + strlen(flag));  // NOLINT: can't use strto64, etc.
-  }
-
-  // If a :flag=value: pair is present returns value, otherwise returns nullptr.
-  // The result is obtained by calling strndup, so make sure to save
-  // it in `this` to avoid a leak.
-  // Typical usage: pass ":some_flag=".
-  // TODO(ussuri): Refactor `char *` into a `string_view`.
-  const char *absl_nullable GetStringFlag(const char *absl_nonnull flag) const {
-    if (!flags) return nullptr;
-    // Extract "value" from ":flag=value:" inside centipede_runner_flags.
-    const char *beg = strstr(flags, flag);
-    if (!beg) return nullptr;
-    const char *value_beg = beg + strlen(flag);
-    const char *end = strstr(value_beg, ":");
-    if (!end) return nullptr;
-    return strndup(value_beg, end - value_beg);
-  }
-};
-
-}  // namespace fuzztest::internal
-
-#endif  // FUZZTEST_CENTIPEDE_DISPATCHER_FLAG_HELPER_H_
diff --git a/centipede/engine_worker.cc b/centipede/engine_worker.cc
index 3c95801..fb1ab64 100644
--- a/centipede/engine_worker.cc
+++ b/centipede/engine_worker.cc
@@ -99,82 +99,39 @@
     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<char*>(malloc(len + 1));
-    if (str == nullptr) {
-      WorkerLog("Cannot allocate the worker flags", LogLnSync{});
+const EngineFlagHelper& GetWorkerFlags() {
+  static ExplicitLifetime<EngineFlagHelper> 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 <typename... C>
@@ -241,6 +198,7 @@
     "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<bool> has_failure_output = false;
@@ -277,8 +235,8 @@
                              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 @@
   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 @@
 
 __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 @@
             LogLnSync{});
 }
 
+size_t GetShmemSize() {
+  static auto result = []() -> size_t {
+    const uint64_t shmem_size_mb =
+        GetWorkerFlags().GetIntFlag(kWorkerShmemSizeMbFlagHeader, 0);
+    return static_cast<size_t>(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().GetIntFlag(kWorkerCrossOverLevel, 50);
+    if (cross_over_level <= 100) return static_cast<int>(cross_over_level);
     // Default
     return 50;
   }();
@@ -449,16 +415,16 @@
 
 std::optional<WorkerAction> GetWorkerAction() {
   static auto worker_action = []() -> std::optional<WorkerAction> {
-    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 @@
 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<uint8_t> binary_id;
@@ -512,7 +478,7 @@
 
 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 @@
   }
 
   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 @@
 
 const char* FuzzTestWorkerGetTestName() {
   static auto test_name = []() -> const char* {
-    return GetWorkerFlag(kWorkerTestNameFlagHeader);
+    return GetWorkerFlags().GetStringFlag(kWorkerTestNameFlagHeader);
   }();
   return test_name;
 }
@@ -872,15 +838,12 @@
         // 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 @@
 }
 
 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 @@
 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/runner.cc b/centipede/runner.cc
index 40e4f39..0403dbe 100644
--- a/centipede/runner.cc
+++ b/centipede/runner.cc
@@ -51,7 +51,6 @@
 #include "absl/base/optimization.h"
 #include "absl/types/span.h"
 #include "./centipede/byte_array_mutator.h"
-#include "./centipede/dispatcher_flag_helper.h"
 #include "./centipede/execution_metadata.h"
 #include "./centipede/feature.h"
 #include "./centipede/mutation_data.h"
@@ -810,7 +809,7 @@
   // No-op under ASAN/TSAN/MSAN - those may still rely on rss_limit_mb.
   if (vm_size_in_bytes < one_tb) {
     size_t address_space_limit_mb =
-        state->flag_helper.HasIntFlag(":address_space_limit_mb=", 0);
+        state->flag_helper.GetIntFlag("address_space_limit_mb=", 0);
     if (address_space_limit_mb > 0) {
       size_t limit_in_bytes = address_space_limit_mb << 20;
       struct rlimit rlimit_as = {limit_in_bytes, limit_in_bytes};
@@ -902,9 +901,10 @@
   // 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);
   }
@@ -969,7 +969,7 @@
       fprintf(stderr, "Centipede fuzz target runner (%s); flags: %s\n",
               req == PersistentModeRequest::kExit ? "exiting persistent mode"
                                                   : "persistent mode batch",
-              state->flag_helper.flags);
+              CentipedeGetRunnerFlags());
     }
     if (req == PersistentModeRequest::kExit) break;
     RunnerCheck(req == PersistentModeRequest::kRunBatch,
@@ -987,9 +987,9 @@
   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.//
 //
@@ -998,25 +998,27 @@
   state->centipede_runner_main_executed = true;
 
   fprintf(stderr, "Centipede fuzz target runner; argv[0]: %s flags: %s\n",
-          argv[0], state->flag_helper.flags);
+          argv[0], CentipedeGetRunnerFlags());
 
-  if (state->flag_helper.HasFlag(":dump_configuration:")) {
+  if (state->flag_helper.HasSwitchFlag("dump_configuration")) {
     DumpSerializedTargetConfigToFile(callbacks,
                                      /*output_file_path=*/sancov_state->arg1);
     return EXIT_SUCCESS;
   }
 
-  if (state->flag_helper.HasFlag(":dump_seed_inputs:")) {
+  if (state->flag_helper.HasSwitchFlag("dump_seed_inputs")) {
     // Seed request.
     DumpSeedsToDir(callbacks, /*output_dir=*/sancov_state->arg1);
     return EXIT_SUCCESS;
   }
 
   // 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);
@@ -1067,9 +1069,13 @@
 
 extern "C" __attribute__((weak)) const char* absl_nullable
 CentipedeGetRunnerFlags() {
-  if (const char* runner_flags_env = getenv("CENTIPEDE_RUNNER_FLAGS"))
-    return strdup(runner_flags_env);
-  return nullptr;
+  static const char* flags = []() -> const char* {
+    if (const char* runner_flags_env = getenv("CENTIPEDE_RUNNER_FLAGS")) {
+      return strdup(runner_flags_env);
+    }
+    return nullptr;
+  }();
+  return flags;
 }
 
 // TODO: xinhaoyuan - write test for this.
diff --git a/centipede/runner.h b/centipede/runner.h
index 5ddc3b3..b64f732 100644
--- a/centipede/runner.h
+++ b/centipede/runner.h
@@ -19,10 +19,10 @@
 #include <time.h>
 
 #include <atomic>
+#include <cstddef>
 #include <cstdint>
 
 #include "./centipede/byte_array_mutator.h"
-#include "./centipede/dispatcher_flag_helper.h"
 #include "./centipede/knobs.h"
 #include "./centipede/runner_interface.h"
 #include "./centipede/runner_result.h"
@@ -38,6 +38,7 @@
   uint64_t ignore_timeout_reports : 1;
   uint64_t max_len;
   std::atomic<uint64_t> stack_limit_kb;
+  size_t shmem_size_mb;
 };
 
 // One global object of this type is created by the runner at start up.
@@ -59,29 +60,30 @@
   // Performs necessary cleanup on process termination.
   void OnTermination();
 
-  DispatcherFlagHelper flag_helper =
-      DispatcherFlagHelper(CentipedeGetRunnerFlags());
+  EngineFlagHelper flag_helper = EngineFlagHelper(CentipedeGetRunnerFlags());
 
   // Note that this field reflects the initial runner flags. But some
   // flags can change later (if wrapped with std::atomic).
   RunTimeFlags run_time_flags = {
-      /*timeout_per_input=*/flag_helper.HasIntFlag(":timeout_per_input=", 0),
-      /*rss_limit_mb=*/flag_helper.HasIntFlag(":rss_limit_mb=", 0),
-      /*crossover_level=*/flag_helper.HasIntFlag(":crossover_level=", 50),
+      /*timeout_per_input=*/flag_helper.GetIntFlag("timeout_per_input=", 0),
+      /*rss_limit_mb=*/flag_helper.GetIntFlag("rss_limit_mb=", 0),
+      /*crossover_level=*/flag_helper.GetIntFlag("crossover_level=", 50),
       /*ignore_timeout_reports=*/
-      flag_helper.HasFlag(":ignore_timeout_reports:"),
-      /*max_len=*/flag_helper.HasIntFlag(":max_len=", 4000),
-      /*stack_limit_kb=*/flag_helper.HasIntFlag(":stack_limit_kb=", 0),
+      flag_helper.HasSwitchFlag("ignore_timeout_reports"),
+      /*max_len=*/flag_helper.GetIntFlag("max_len=", 4000),
+      /*stack_limit_kb=*/flag_helper.GetIntFlag("stack_limit_kb=", 0),
+      /*shmem_size_mb=*/
+      static_cast<size_t>(flag_helper.GetIntFlag("shmem_size_mb=", 0)),
   };
 
   // The path to a file where the runner may write the description of failure.
-  const char *failure_description_path =
-      flag_helper.GetStringFlag(":failure_description_path=");
+  const char* failure_description_path =
+      flag_helper.GetStringFlag("failure_description_path=");
 
   std::atomic<bool> has_failure_description;
 
   const char* persistent_mode_socket_path =
-      flag_helper.GetStringFlag(":persistent_mode_socket=");
+      flag_helper.GetStringFlag("persistent_mode_socket=");
   int persistent_mode_socket = 0;
 
   pthread_mutex_t execution_result_override_mu = PTHREAD_MUTEX_INITIALIZER;
diff --git a/centipede/runner_interface.h b/centipede/runner_interface.h
index 9b560bd..7a2208b 100644
--- a/centipede/runner_interface.h
+++ b/centipede/runner_interface.h
@@ -80,7 +80,8 @@
 // gets the flags from CENTIPEDE_RUNNER_FLAGS env var.
 //
 // It should return either a nullptr or a constant string that is valid
-// throughout the entire process life-time.
+// throughout the entire process life-time. Multiple calls should always return
+// the same value.
 extern "C" const char* absl_nullable CentipedeGetRunnerFlags();
 
 // An overridable function to override `LLVMFuzzerMutate` behavior.
diff --git a/centipede/runner_utils.h b/centipede/runner_utils.h
index 4b85afa..fb28cb5 100644
--- a/centipede/runner_utils.h
+++ b/centipede/runner_utils.h
@@ -17,9 +17,13 @@
 
 #include <sys/stat.h>
 
+#include <cstddef>
 #include <cstdint>
 #include <cstdio>
+#include <cstdlib>
+#include <cstring>
 #include <new>
+#include <string_view>
 #include <vector>
 
 #include "absl/base/nullability.h"
@@ -125,6 +129,78 @@
   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(const EngineFlagHelper&) = delete;
+  EngineFlagHelper& operator=(const EngineFlagHelper&) = delete;
+
+  ~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 GetIntFlag(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 0000000..39367f4
--- /dev/null
+++ b/centipede/runner_utils_test.cc
@@ -0,0 +1,47 @@
+// Copyright 2026 Google LLC
+//
+// 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 <string_view>
+
+#include "gtest/gtest.h"
+
+namespace fuzztest::internal {
+namespace {
+
+TEST(RunnerUtilsTest, EngineFlagHelperWorksWithoutFlags) {
+  EngineFlagHelper helper(nullptr);
+  EXPECT_FALSE(helper.HasSwitchFlag("foo"));
+  EXPECT_EQ(helper.GetIntFlag("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.GetIntFlag("flag2=", 0), 123);
+  EXPECT_EQ(helper.GetIntFlag("missing=", 999), 999);
+
+  EXPECT_STREQ(helper.GetStringFlag("str="), "hello");
+  EXPECT_EQ(helper.GetStringFlag("missing="), nullptr);
+}
+
+}  // namespace
+}  // namespace fuzztest::internal
diff --git a/centipede/sancov_callbacks.cc b/centipede/sancov_callbacks.cc
index 3e5d0c5..b1c180c 100644
--- a/centipede/sancov_callbacks.cc
+++ b/centipede/sancov_callbacks.cc
@@ -23,7 +23,6 @@
 
 #include "absl/base/nullability.h"
 #include "absl/base/optimization.h"
-#include "./centipede/dispatcher_flag_helper.h"
 #include "./centipede/feature.h"
 #include "./centipede/int_utils.h"
 #include "./centipede/pc_info.h"
@@ -356,7 +355,7 @@
 static pthread_once_t main_object_lazy_init_once = PTHREAD_ONCE_INIT;
 static void MainObjectLazyInitOnceCallback() {
   sancov_state->main_object = fuzztest::internal::GetDlInfo(
-      sancov_state->flag_helper.GetStringFlag(":dl_path_suffix="));
+      sancov_state->flag_helper.GetStringFlag("dl_path_suffix="));
   fprintf(stderr, "MainObjectLazyInitOnceCallback %zx\n",
           sancov_state->main_object.start_address);
   UpdatePcCounterSetSizeAligned(sancov_state->reverse_pc_table.NumPcs());
diff --git a/centipede/sancov_state.cc b/centipede/sancov_state.cc
index c5bbaf3..8adedee 100644
--- a/centipede/sancov_state.cc
+++ b/centipede/sancov_state.cc
@@ -25,7 +25,6 @@
 #include <vector>
 
 #include "absl/base/nullability.h"
-#include "./centipede/dispatcher_flag_helper.h"
 #include "./centipede/engine_abi.h"
 #include "./centipede/execution_metadata.h"
 #include "./centipede/feature.h"
@@ -197,7 +196,7 @@
 
 static void MaybePopulateReversePcTable() {
   const char* pcs_file_path =
-      sancov_state->flag_helper.GetStringFlag(":pcs_file_path=");
+      sancov_state->flag_helper.GetStringFlag("pcs_file_path=");
   if (!pcs_file_path) return;
   const auto pc_table = ReadBytesFromFilePath<PCInfo>(pcs_file_path);
   sancov_state->reverse_pc_table.SetFromPCs(pc_table);
@@ -253,8 +252,8 @@
 SancovState::SancovState() {
   tls.OnThreadStart();
   // Compute main_object.
-  main_object = GetDlInfo(flag_helper.GetStringFlag(":dl_path_suffix="));
-  if (!sancov_state->main_object.IsSet()) {
+  main_object = GetDlInfo(flag_helper.GetStringFlag("dl_path_suffix="));
+  if (!main_object.IsSet()) {
     fprintf(
         stderr,
         "Failed to compute main_object. This may happen"
@@ -262,7 +261,7 @@
   }
 
   // Dump the binary info tables.
-  if (flag_helper.HasFlag(":dump_binary_info:")) {
+  if (flag_helper.HasSwitchFlag("dump_binary_info")) {
     RunnerCheck(arg1 && arg2 && arg3, "dump_binary_info requires 3 arguments");
     if (!arg1 || !arg2 || !arg3) _exit(EXIT_FAILURE);
     DumpPcTable(arg1);
@@ -561,10 +560,14 @@
 }  // namespace fuzztest::internal
 
 // Can be overridden to not depend explicitly on CENTIPEDE_RUNNER_FLAGS.
-extern "C" __attribute__((weak)) const char *absl_nullable GetSancovFlags() {
-  if (const char *sancov_flags_env = getenv("CENTIPEDE_RUNNER_FLAGS"))
-    return strdup(sancov_flags_env);
-  return nullptr;
+extern "C" __attribute__((weak)) const char* absl_nullable GetSancovFlags() {
+  static const char* flags = []() -> const char* {
+    if (const char* sancov_flags_env = getenv("CENTIPEDE_RUNNER_FLAGS")) {
+      return strdup(sancov_flags_env);
+    }
+    return nullptr;
+  }();
+  return flags;
 }
 
 void SanCovRuntimeClearCoverage(bool full_clear) {
diff --git a/centipede/sancov_state.h b/centipede/sancov_state.h
index bde30e1..5e105e0 100644
--- a/centipede/sancov_state.h
+++ b/centipede/sancov_state.h
@@ -31,7 +31,6 @@
 #include "./centipede/callstack.h"
 #include "./centipede/concurrent_bitset.h"
 #include "./centipede/concurrent_byteset.h"
-#include "./centipede/dispatcher_flag_helper.h"
 #include "./centipede/execution_metadata.h"
 #include "./centipede/feature.h"
 #include "./centipede/hashed_ring_buffer.h"
@@ -42,7 +41,7 @@
 #include "./centipede/sancov_object_array.h"
 #include "./centipede/sancov_runtime.h"
 
-extern "C" const char *absl_nullable GetSancovFlags();
+extern "C" const char* absl_nullable GetSancovFlags();
 
 namespace fuzztest::internal {
 
@@ -150,27 +149,27 @@
   SancovState();
   ~SancovState();
 
-  DispatcherFlagHelper flag_helper = DispatcherFlagHelper(GetSancovFlags());
+  EngineFlagHelper flag_helper = EngineFlagHelper(GetSancovFlags());
 
   // TODO(xinhaoyuan): Change to use meaningful flag names instead of the
   // generic names arg1/2/3.
-  const char *arg1 = flag_helper.GetStringFlag(":arg1=");
-  const char *arg2 = flag_helper.GetStringFlag(":arg2=");
-  const char *arg3 = flag_helper.GetStringFlag(":arg3=");
+  const char* arg1 = flag_helper.GetStringFlag("arg1=");
+  const char* arg2 = flag_helper.GetStringFlag("arg2=");
+  const char* arg3 = flag_helper.GetStringFlag("arg3=");
 
   SancovFlags flags = {
       /*path_level=*/std::min(ThreadLocalSancovState::kBoundedPathLength,
-                              flag_helper.HasIntFlag(":path_level=", 0)),
-      /*use_pc_features=*/flag_helper.HasFlag(":use_pc_features:"),
+                              flag_helper.GetIntFlag("path_level=", 0)),
+      /*use_pc_features=*/flag_helper.HasSwitchFlag("use_pc_features"),
       /*use_dataflow_features=*/
-      flag_helper.HasFlag(":use_dataflow_features:"),
-      /*use_cmp_features=*/flag_helper.HasFlag(":use_cmp_features:"),
-      /*callstack_level=*/flag_helper.HasIntFlag(":callstack_level=", 0),
+      flag_helper.HasSwitchFlag("use_dataflow_features"),
+      /*use_cmp_features=*/flag_helper.HasSwitchFlag("use_cmp_features"),
+      /*callstack_level=*/flag_helper.GetIntFlag("callstack_level=", 0),
       /*use_counter_features=*/
-      flag_helper.HasFlag(":use_counter_features:"),
+      flag_helper.HasSwitchFlag("use_counter_features"),
       /*use_auto_dictionary=*/
-      flag_helper.HasFlag(":use_auto_dictionary:"),
-      /*skip_seen_features=*/flag_helper.HasFlag(":skip_seen_features:"),
+      flag_helper.HasSwitchFlag("use_auto_dictionary"),
+      /*skip_seen_features=*/flag_helper.HasSwitchFlag("skip_seen_features"),
   };
 
   // Computed by DlInfo().
diff --git a/centipede/shared_memory_blob_sequence.cc b/centipede/shared_memory_blob_sequence.cc
index 3a24126..51415df 100644
--- a/centipede/shared_memory_blob_sequence.cc
+++ b/centipede/shared_memory_blob_sequence.cc
@@ -135,7 +135,10 @@
   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 @@
   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 fc69c10..b5d781e 100644
--- a/centipede/shared_memory_blob_sequence.h
+++ b/centipede/shared_memory_blob_sequence.h
@@ -134,7 +134,7 @@
 //
 //  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 @@
   // 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 2b9f557..f5dd4fc 100644
--- a/centipede/shared_memory_blob_sequence_test.cc
+++ b/centipede/shared_memory_blob_sequence_test.cc
@@ -112,7 +112,7 @@
   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 @@
   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);
   }
 }
diff --git a/fuzztest/internal/centipede_adaptor.cc b/fuzztest/internal/centipede_adaptor.cc
index c74427e..1f460e5 100644
--- a/fuzztest/internal/centipede_adaptor.cc
+++ b/fuzztest/internal/centipede_adaptor.cc
@@ -1085,16 +1085,19 @@
 }  // namespace
 
 extern "C" const char* CentipedeGetRunnerFlags() {
-  if (const char* runner_flags_env = std::getenv("CENTIPEDE_RUNNER_FLAGS")) {
-    // Runner mode. Use the existing flags.
-    return strdup(runner_flags_env);
-  }
+  static const char* flags = []() -> const char* {
+    if (const char* runner_flags_env = std::getenv("CENTIPEDE_RUNNER_FLAGS")) {
+      // Runner mode. Use the existing flags.
+      return strdup(runner_flags_env);
+    }
 
-  // Set the runner flags according to the FuzzTest default environment.
-  const auto env = fuzztest::internal::CreateDefaultCentipedeEnvironment();
-  CentipedeCallbacksForRunnerFlagsExtraction callbacks(
-      env, fuzztest::internal::global_stop_condition);
-  const std::string runner_flags = callbacks.GetRunnerFlagsContent();
-  ABSL_VLOG(1) << "[.] Centipede runner flags: " << runner_flags;
-  return strdup(runner_flags.c_str());
+    // Set the runner flags according to the FuzzTest default environment.
+    const auto env = fuzztest::internal::CreateDefaultCentipedeEnvironment();
+    CentipedeCallbacksForRunnerFlagsExtraction callbacks(
+        env, fuzztest::internal::global_stop_condition);
+    const char* flags = strdup(callbacks.GetRunnerFlagsContent().c_str());
+    FUZZTEST_VLOG(1) << "[.] Centipede runner flags: " << flags;
+    return flags;
+  }();
+  return flags;
 }