Refactor AddCertificateReports to prevent crash When processing certificate stats with duplicate fingerprints, ReplaceOrAddNew would delete the existing stats report that was previously added and pointed to by first_report or prev_report. By adding tracking for duplicate fingerprints we can find the existing report and avoid replacing it. Bug: chromium:486495143 Change-Id: Iabc41ae064476c1e5853cdff1dbbcab449f8df27 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/459320 Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Reviewed-by: Henrik Boström <hbos@webrtc.org> Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org> Cr-Commit-Position: refs/heads/main@{#47242}
diff --git a/pc/BUILD.gn b/pc/BUILD.gn index c07cbd3..f2045cb 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn
@@ -1461,6 +1461,7 @@ "../rtc_base/system:plan_b_only", "../system_wrappers", "//third_party/abseil-cpp/absl/base:nullability", + "//third_party/abseil-cpp/absl/container:flat_hash_set", "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view",
diff --git a/pc/legacy_stats_collector.cc b/pc/legacy_stats_collector.cc index f41de1c..6978f1b 100644 --- a/pc/legacy_stats_collector.cc +++ b/pc/legacy_stats_collector.cc
@@ -21,6 +21,7 @@ #include <vector> #include "absl/base/nullability.h" +#include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -792,8 +793,13 @@ StatsReport* first_report = nullptr; StatsReport* prev_report = nullptr; + absl::flat_hash_set<std::string> visited_fingerprints; for (SSLCertificateStats* stats = cert_stats.get(); stats; stats = stats->issuer.get()) { + if (!visited_fingerprints.insert(stats->fingerprint).second) { + break; + } + StatsReport::Id id(StatsReport::NewTypedId( StatsReport::kStatsReportTypeCertificate, stats->fingerprint));
diff --git a/pc/legacy_stats_collector.h b/pc/legacy_stats_collector.h index 25e61ed..f36e509 100644 --- a/pc/legacy_stats_collector.h +++ b/pc/legacy_stats_collector.h
@@ -114,6 +114,11 @@ bool UseStandardBytesStats() const { return use_standard_bytes_stats_; } + StatsReport* AddCertificateReportsForTest( + std::unique_ptr<SSLCertificateStats> cert_stats) { + return AddCertificateReports(std::move(cert_stats)); + } + private: // Struct that's populated on the network thread and carries the values to // the signaling thread where the stats are added to the stats reports.
diff --git a/pc/legacy_stats_collector_unittest.cc b/pc/legacy_stats_collector_unittest.cc index 63c33ec..f039115 100644 --- a/pc/legacy_stats_collector_unittest.cc +++ b/pc/legacy_stats_collector_unittest.cc
@@ -57,6 +57,7 @@ #include "rtc_base/null_socket_server.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/socket_address.h" +#include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/system/plan_b_only.h" @@ -1446,6 +1447,21 @@ remote_ders); } +TEST_F(LegacyStatsCollectorTest, + AddCertificateReports_DuplicateFingerprintDoesNotCrash) { + auto pc = CreatePeerConnection(); + auto stats = CreateStatsCollector(pc.get()); + + // Create duplicate fingerprints chain + auto issuer_stats = std::make_unique<SSLCertificateStats>( + "same_fingerprint", "sha-1", "cert_issuer", nullptr); + auto cert_stats = std::make_unique<SSLCertificateStats>( + "same_fingerprint", "sha-1", "cert_leaf", std::move(issuer_stats)); + + // This should not crash (Use-After-Free) + stats->AddCertificateReportsForTest(std::move(cert_stats)); +} + // This test verifies that all certificates without chains are correctly // reported. TEST_F(LegacyStatsCollectorTest, ChainlessCertificateReportsCreated) {