From 77defd3a24396b057dfa08283040303c87242faa Mon Sep 17 00:00:00 2001 From: Filip Niksic Date: Wed, 11 Feb 2026 14:05:08 -0800 Subject: [PATCH] No public description PiperOrigin-RevId: 868834597 --- centipede/crash_deduplication.cc | 31 +++++++++++++-- centipede/crash_deduplication_test.cc | 56 +++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/centipede/crash_deduplication.cc b/centipede/crash_deduplication.cc index 417a0b61..545eb2dc 100644 --- a/centipede/crash_deduplication.cc +++ b/centipede/crash_deduplication.cc @@ -15,6 +15,7 @@ #include "./centipede/crash_deduplication.h" #include +#include #include // NOLINT #include #include @@ -53,6 +54,8 @@ std::string GetInputFileName(std::string_view bug_id, absl::flat_hash_map GetCrashesFromWorkdir( const WorkDir& workdir, size_t total_shards) { + const bool fail_on_empty_crash_metadata = + std::getenv("FUZZTEST_FAIL_ON_EMPTY_CRASH_METADATA") != nullptr; absl::flat_hash_map crashes; for (size_t shard_idx = 0; shard_idx < total_shards; ++shard_idx) { std::vector crashing_input_paths = @@ -79,6 +82,15 @@ absl::flat_hash_map GetCrashesFromWorkdir( << " due to failure to read the crash signature: " << status; continue; } + if (crash_signature.empty()) { + FUZZTEST_LOG_IF(FATAL, fail_on_empty_crash_metadata) + << "Empty crash signature for " << crashing_input_file_name; + FUZZTEST_LOG(ERROR) + << "Ignoring crashing input " << crashing_input_file_name + << " due to empty crash signature. This is an internal error; " + "please report it to the FuzzTest team!"; + continue; + } if (crashes.contains(crash_signature)) continue; const std::string crash_description_path = @@ -86,9 +98,22 @@ absl::flat_hash_map GetCrashesFromWorkdir( std::string crash_description; const absl::Status description_status = RemoteFileGetContents(crash_description_path, crash_description); - FUZZTEST_LOG_IF(WARNING, !description_status.ok()) - << "Failed to read crash description for " << crashing_input_file_name - << ".Status: " << description_status; + if (!description_status.ok()) { + FUZZTEST_LOG(WARNING) + << "Ignoring crashing input " << crashing_input_file_name + << " due to failure to read the crash description: " + << description_status; + continue; + } + if (crash_description.empty()) { + FUZZTEST_LOG_IF(FATAL, fail_on_empty_crash_metadata) + << "Empty crash description for " << crashing_input_file_name; + FUZZTEST_LOG(ERROR) + << "Ignoring crashing input " << crashing_input_file_name + << " due to empty crash description. This is an internal error; " + "please report it to the FuzzTest team!"; + continue; + } crashes.insert( {std::move(crash_signature), // Centipede uses the input signature (i.e., the hash of the input) diff --git a/centipede/crash_deduplication_test.cc b/centipede/crash_deduplication_test.cc index e73e7c54..bd1966e4 100644 --- a/centipede/crash_deduplication_test.cc +++ b/centipede/crash_deduplication_test.cc @@ -91,6 +91,16 @@ TEST(GetCrashesFromWorkdirTest, ReturnsOneCrashPerCrashSignature) { // `isig4` lacks `.sig` and `.desc` files and should be ignored. SetContentsAndGetPath(crashes1, "isig4", "input4"); + // `isig5` has empty crash signature and should be ignored. + auto input5_path = SetContentsAndGetPath(crashes1, "isig5", "input5"); + SetContentsAndGetPath(crash_metadata1, "isig5.sig", ""); + SetContentsAndGetPath(crash_metadata1, "isig5.desc", "desc5"); + + // `isig6` has empty crash description and should be ignored. + auto input6_path = SetContentsAndGetPath(crashes1, "isig6", "input6"); + SetContentsAndGetPath(crash_metadata1, "isig6.sig", "csig6"); + SetContentsAndGetPath(crash_metadata1, "isig6.desc", ""); + const auto crashes = GetCrashesFromWorkdir(workdir, /*total_shards=*/2); EXPECT_THAT( crashes, @@ -100,6 +110,52 @@ TEST(GetCrashesFromWorkdirTest, ReturnsOneCrashPerCrashSignature) { Pair("csig2", FieldsAre("isig2", "desc2", input2_path)))); } +TEST(GetCrashesFromWorkdirTest, FailsOnEmptyCrashSignatureIfEnvVarSet) { + TempDir test_dir; + const std::string workdir_path = test_dir.path(); + WorkDir workdir{workdir_path, "binary_name", "binary_hash", + /*my_shard_index=*/0}; + + const std::filesystem::path crashes = + workdir.CrashReproducerDirPaths().Shard(0); + const std::filesystem::path crash_metadata = + workdir.CrashMetadataDirPaths().Shard(0); + std::filesystem::create_directories(crashes); + std::filesystem::create_directories(crash_metadata); + + auto input_path = SetContentsAndGetPath(crashes, "isig", "input"); + SetContentsAndGetPath(crash_metadata, "isig.sig", ""); + SetContentsAndGetPath(crash_metadata, "isig.desc", "desc"); + + setenv("FUZZTEST_FAIL_ON_EMPTY_CRASH_METADATA", "1", /*overwrite=*/1); + EXPECT_DEATH(GetCrashesFromWorkdir(workdir, /*total_shards=*/1), + "Empty crash signature"); + unsetenv("FUZZTEST_FAIL_ON_EMPTY_CRASH_METADATA"); +} + +TEST(GetCrashesFromWorkdirTest, FailsOnEmptyCrashDescriptionIfEnvVarSet) { + TempDir test_dir; + const std::string workdir_path = test_dir.path(); + WorkDir workdir{workdir_path, "binary_name", "binary_hash", + /*my_shard_index=*/0}; + + const std::filesystem::path crashes = + workdir.CrashReproducerDirPaths().Shard(0); + const std::filesystem::path crash_metadata = + workdir.CrashMetadataDirPaths().Shard(0); + std::filesystem::create_directories(crashes); + std::filesystem::create_directories(crash_metadata); + + auto input_path = SetContentsAndGetPath(crashes, "isig", "input"); + SetContentsAndGetPath(crash_metadata, "isig.sig", "csig"); + SetContentsAndGetPath(crash_metadata, "isig.desc", ""); + + setenv("FUZZTEST_FAIL_ON_EMPTY_CRASH_METADATA", "1", /*overwrite=*/1); + EXPECT_DEATH(GetCrashesFromWorkdir(workdir, /*total_shards=*/1), + "Empty crash description"); + unsetenv("FUZZTEST_FAIL_ON_EMPTY_CRASH_METADATA"); +} + class FakeCentipedeCallbacks : public CentipedeCallbacks { public: struct Crash {