Fix crash that occurs if GetStats is called from within OnStatsDelivered

This was resulting in the currently executing stats callback to be
invoked *again*, possibly ad infinitum, resulting in stack overflow.

Bug: webrtc:8973
Change-Id: Ib3bcc8e6bdd991728214fa242dda2010efc919a7
Reviewed-on: https://webrtc-review.googlesource.com/59464
Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22313}
diff --git a/pc/rtcstatscollector.cc b/pc/rtcstatscollector.cc
index 20cbc91..53a6158 100644
--- a/pc/rtcstatscollector.cc
+++ b/pc/rtcstatscollector.cc
@@ -780,11 +780,16 @@
   RTC_DCHECK(signaling_thread_->IsCurrent());
   RTC_DCHECK(!callbacks_.empty());
   RTC_DCHECK(cached_report_);
+
+  // Swap the list of callbacks, in case one of them recursively calls
+  // GetStatsReport again and modifies the callback list.
+  std::vector<rtc::scoped_refptr<RTCStatsCollectorCallback>> callbacks;
+  callbacks.swap(callbacks_);
+
   for (const rtc::scoped_refptr<RTCStatsCollectorCallback>& callback :
-       callbacks_) {
+       callbacks) {
     callback->OnStatsDelivered(cached_report_);
   }
-  callbacks_.clear();
 }
 
 void RTCStatsCollector::ProduceCertificateStats_n(
diff --git a/pc/rtcstatscollector_unittest.cc b/pc/rtcstatscollector_unittest.cc
index f180471..5240e62 100644
--- a/pc/rtcstatscollector_unittest.cc
+++ b/pc/rtcstatscollector_unittest.cc
@@ -1910,6 +1910,37 @@
   EXPECT_EQ(1, track_stats.size());
 }
 
+// Used for test below, to test calling GetStatsReport during a callback.
+class ReentrantCallback : public RTCStatsCollectorCallback {
+ public:
+  explicit ReentrantCallback(RTCStatsCollectorWrapper* stats) : stats_(stats) {}
+
+  void OnStatsDelivered(
+      const rtc::scoped_refptr<const RTCStatsReport>& report) override {
+    stats_->GetStatsReport();
+    called_ = true;
+  }
+
+  bool called() const { return called_; }
+
+ private:
+  RTCStatsCollectorWrapper* stats_;
+  bool called_ = false;
+};
+
+// Test that nothing bad happens if a callback causes GetStatsReport to be
+// called again recursively. Regression test for crbug.com/webrtc/8973.
+TEST_F(RTCStatsCollectorTest, DoNotCrashOnReentrantInvocation) {
+  rtc::scoped_refptr<ReentrantCallback> callback1(
+      new rtc::RefCountedObject<ReentrantCallback>(stats_.get()));
+  rtc::scoped_refptr<ReentrantCallback> callback2(
+      new rtc::RefCountedObject<ReentrantCallback>(stats_.get()));
+  stats_->stats_collector()->GetStatsReport(callback1);
+  stats_->stats_collector()->GetStatsReport(callback2);
+  EXPECT_TRUE_WAIT(callback1->called(), kGetStatsReportTimeoutMs);
+  EXPECT_TRUE_WAIT(callback2->called(), kGetStatsReportTimeoutMs);
+}
+
 class RTCTestStats : public RTCStats {
  public:
   WEBRTC_RTCSTATS_DECL();