#Centipede Run the fuzzing loop in the current thread when num_threads == 1. More context is in the comment. PiperOrigin-RevId: 623475379
diff --git a/centipede/centipede_interface.cc b/centipede/centipede_interface.cc index 0c15224..45f3c3d 100644 --- a/centipede/centipede_interface.cc +++ b/centipede/centipede_interface.cc
@@ -264,8 +264,18 @@ } CoverageLogger coverage_logger(binary_info.pc_table, binary_info.symbols); - auto thread_callback = [&](Environment &my_env, std::atomic<Stats> &stats) { - CreateLocalDirRemovedAtExit(TemporaryLocalDirPath()); // creates temp dir. + std::vector<Environment> envs(env.num_threads, env); + std::vector<std::atomic<Stats>> stats_vec(env.num_threads); + std::atomic<bool> stats_thread_continue_running = true; + + std::thread stats_thread(ReportStatsThread, + std::ref(stats_thread_continue_running), + std::ref(stats_vec), std::ref(envs)); + + auto fuzzing_worker = [&](Environment &my_env, std::atomic<Stats> &stats, + bool create_tmpdir) { + if (create_tmpdir) CreateLocalDirRemovedAtExit(TemporaryLocalDirPath()); + my_env.UpdateForExperiment(); my_env.seed = GetRandomSeed(env.seed); // uses TID, call in this thread. my_env.pcs_file_path = pcs_file_path; // same for all threads. @@ -277,28 +287,29 @@ centipede.FuzzingLoop(); }; - std::vector<Environment> envs(env.num_threads, env); - std::vector<std::atomic<Stats>> stats_vec(env.num_threads); - std::vector<std::thread> threads(env.num_threads); - std::atomic<bool> stats_thread_continue_running(true); - - // Create threads. - for (size_t thread_idx = 0; thread_idx < env.num_threads; thread_idx++) { - Environment &my_env = envs[thread_idx]; - my_env.my_shard_index = env.my_shard_index + thread_idx; - my_env.UpdateForExperiment(); - threads[thread_idx] = std::thread(thread_callback, std::ref(my_env), - std::ref(stats_vec[thread_idx])); + if (env.num_threads == 1) { + // When fuzzing with one thread, run fuzzing loop in the current + // thread. This is because FuzzTest/Centipede's single-process + // fuzzing requires the test body, which is invoked by the fuzzing + // loop, to run in the main thread. + // + // Here, the fuzzing worker should not re-create the tmpdir since the path + // is thread-local and it has been created in the current function. + fuzzing_worker(envs[0], stats_vec[0], /*create_tmpdir=*/false); + } else { + std::vector<std::thread> fuzzing_worker_threads(env.num_threads); + for (size_t thread_idx = 0; thread_idx < env.num_threads; thread_idx++) { + Environment &my_env = envs[thread_idx]; + my_env.my_shard_index = env.my_shard_index + thread_idx; + fuzzing_worker_threads[thread_idx] = + std::thread(fuzzing_worker, std::ref(my_env), + std::ref(stats_vec[thread_idx]), /*create_tmpdir=*/true); + } + for (size_t thread_idx = 0; thread_idx < env.num_threads; thread_idx++) { + fuzzing_worker_threads[thread_idx].join(); + } } - std::thread stats_thread(ReportStatsThread, - std::ref(stats_thread_continue_running), - std::ref(stats_vec), std::ref(envs)); - - // Join threads. - for (size_t thread_idx = 0; thread_idx < env.num_threads; thread_idx++) { - threads[thread_idx].join(); - } stats_thread_continue_running = false; stats_thread.join();
diff --git a/centipede/centipede_test.cc b/centipede/centipede_test.cc index 1bfa1cd..cfdea96 100644 --- a/centipede/centipede_test.cc +++ b/centipede/centipede_test.cc
@@ -22,6 +22,7 @@ #include <set> #include <string> #include <string_view> +#include <thread> // NOLINT(build/c++11) #include <vector> #include "gmock/gmock.h" @@ -893,4 +894,50 @@ EXPECT_FALSE(found_startup_cmp_entry); } +namespace { + +class FakeCentipedeCallbacksForThreadChecking : public CentipedeCallbacks { + public: + FakeCentipedeCallbacksForThreadChecking(const Environment &env, + std::thread::id execute_thread_id) + : CentipedeCallbacks(env), execute_thread_id_(execute_thread_id) {} + + bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs, + BatchResult &batch_result) override { + batch_result.ClearAndResize(inputs.size()); + thread_check_passed_ = thread_check_passed_ && + std::this_thread::get_id() == execute_thread_id_; + return true; + } + + void Mutate(const std::vector<MutationInputRef> &inputs, size_t num_mutants, + std::vector<ByteArray> &mutants) override { + mutants.resize(num_mutants, {0}); + } + + bool thread_check_passed() { return thread_check_passed_; } + + private: + std::thread::id execute_thread_id_; + bool thread_check_passed_ = true; +}; + +} // namespace + +TEST(Centipede, RunsExecuteCallbackInTheCurrentThreadWhenFuzzingWithOneThread) { + TempDir temp_dir{test_info_->name()}; + Environment env; + env.workdir = temp_dir.path(); + env.require_pc_table = false; + ASSERT_EQ(env.num_threads, 1); + FakeCentipedeCallbacksForThreadChecking callbacks(env, + std::this_thread::get_id()); + BatchResult batch_result; + const std::vector<ByteArray> inputs = {{0}}; + env.num_runs = 100; + MockFactory factory(callbacks); + EXPECT_EQ(CentipedeMain(env, factory), EXIT_SUCCESS); + EXPECT_TRUE(callbacks.thread_check_passed()); +} + } // namespace centipede