diff --git a/centipede/BUILD b/centipede/BUILD index 2062db85c..108e5e032 100644 --- a/centipede/BUILD +++ b/centipede/BUILD @@ -1074,7 +1074,6 @@ RUNNER_LINKOPTS = [ RUNNER_DEPS = [ ":byte_array_mutator", ":callstack", - ":dispatcher_flag_helper", ":execution_metadata", ":feature", ":foreach_nonzero", @@ -1208,15 +1207,6 @@ cc_library( ], ) -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 = [ @@ -1238,7 +1228,6 @@ cc_library( copts = DISABLE_SANCOV_COPTS, deps = [ ":callstack", - ":dispatcher_flag_helper", ":engine_abi", ":execution_metadata", ":feature", diff --git a/centipede/dispatcher_flag_helper.h b/centipede/dispatcher_flag_helper.h deleted file mode 100644 index d14383143..000000000 --- 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 - -#include -#include - -#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 80281efec..e0f67855c 100644 --- a/centipede/engine_worker.cc +++ b/centipede/engine_worker.cc @@ -826,12 +826,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: ", - GetWorkerFlagsEnv() != nullptr ? GetWorkerFlagsEnv() : "", - LogLnSync{}); + WorkerLog( + "FuzzTest engine worker (", + req == PersistentModeRequest::kExit ? "exiting persistent mode" + : "persistent mode batch", + "); flags: ", GetWorkerFlagsEnv() ? GetWorkerFlagsEnv() : "(unset)", + LogLnSync{}); } if (req == PersistentModeRequest::kExit) break; WorkerCheck(req == PersistentModeRequest::kRunBatch, diff --git a/centipede/runner.cc b/centipede/runner.cc index 40e4f39bf..88a4ce90d 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 @@ static void SetLimits() { // 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,7 +901,7 @@ 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:")) { + flag_helper.HasSwitchFlag("shmem")) { PostProcessSancov(); // TODO(xinhaoyuan): do we know our exit status? SharedMemoryBlobSequence outputs_blobseq(sancov_state->arg2); StartSendingOutputsToEngine(outputs_blobseq); @@ -966,10 +965,11 @@ static int HandlePersistentMode(RunnerCallbacks& callbacks, // to happen when the stdout/stderr are not redirected to a file. (void)ftruncate(fd, 0); } - fprintf(stderr, "Centipede fuzz target runner (%s); flags: %s\n", - req == PersistentModeRequest::kExit ? "exiting persistent mode" - : "persistent mode batch", - state->flag_helper.flags); + fprintf( + stderr, "Centipede fuzz target runner (%s); flags: %s\n", + req == PersistentModeRequest::kExit ? "exiting persistent mode" + : "persistent mode batch", + CentipedeGetRunnerFlags() ? CentipedeGetRunnerFlags() : "(unset)"); } if (req == PersistentModeRequest::kExit) break; RunnerCheck(req == PersistentModeRequest::kRunBatch, @@ -987,7 +987,7 @@ static int HandlePersistentMode(RunnerCallbacks& callbacks, return EXIT_SUCCESS; } -// If HasFlag(:shmem:), state->arg1 and state->arg2 are the names +// If HasSwitchFlag(:shmem:), state->arg1 and state->arg2 are the names // of in/out shared memory locations. // Read inputs and write outputs via shared memory. // @@ -998,22 +998,23 @@ int RunnerMain(int argc, char** argv, RunnerCallbacks& callbacks) { 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() ? CentipedeGetRunnerFlags() : "(unset)"); - 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->flag_helper.HasSwitchFlag("shmem")) { if (!sancov_state->arg1 || !sancov_state->arg2) return EXIT_FAILURE; SharedMemoryBlobSequence inputs_blobseq(sancov_state->arg1); SharedMemoryBlobSequence outputs_blobseq(sancov_state->arg2); @@ -1067,9 +1068,13 @@ extern "C" void CentipedeSetTimeoutPerInput(uint64_t timeout_per_input) { 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 5ddc3b3e5..7cdbe5a2f 100644 --- a/centipede/runner.h +++ b/centipede/runner.h @@ -22,7 +22,6 @@ #include #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" @@ -59,29 +58,28 @@ struct GlobalRunnerState { // 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), }; // 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 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 9b560bddd..7a2208b2c 100644 --- a/centipede/runner_interface.h +++ b/centipede/runner_interface.h @@ -80,7 +80,8 @@ extern "C" void CentipedeSetTimeoutPerInput(uint64_t timeout_per_input); // 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/sancov_callbacks.cc b/centipede/sancov_callbacks.cc index 3e5d0c504..b1c180caf 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 void UpdatePcCounterSetSizeAligned(size_t size) { 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 c5bbaf344..8adedeeaf 100644 --- a/centipede/sancov_state.cc +++ b/centipede/sancov_state.cc @@ -25,7 +25,6 @@ #include #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 @@ void SancovState::CleanUpDetachedTls() { 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(pcs_file_path); sancov_state->reverse_pc_table.SetFromPCs(pc_table); @@ -253,8 +252,8 @@ static void DumpDsoTable(const char *absl_nonnull output_path) { 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 @@ SancovState::SancovState() { } // 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 @@ const ExecutionMetadata& SanCovRuntimeGetExecutionMetadata() { } // 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 bde30e12f..5e105e0fa 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 @@ struct SancovState { 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/fuzztest/internal/centipede_adaptor.cc b/fuzztest/internal/centipede_adaptor.cc index 73f5e3a8a..5885eaa3b 100644 --- a/fuzztest/internal/centipede_adaptor.cc +++ b/fuzztest/internal/centipede_adaptor.cc @@ -1124,16 +1124,19 @@ class CentipedeCallbacksForRunnerFlagsExtraction } // 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; }