Add better checks for temporal/spatial bounds in EncoderBitrateAdjuster.

Bug: webrtc:514671098
Change-Id: If3cfb55bdc03d57760a5d30fc63ca63ed321fad1
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/474681
Commit-Queue: Erik Språng <sprang@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Auto-Submit: Erik Språng <sprang@webrtc.org>
Commit-Queue: Philip Eliasson <philipel@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47775}
diff --git a/video/encoder_bitrate_adjuster.cc b/video/encoder_bitrate_adjuster.cc
index 10c7334..d8aa226 100644
--- a/video/encoder_bitrate_adjuster.cc
+++ b/video/encoder_bitrate_adjuster.cc
@@ -76,9 +76,16 @@
   if (codec_settings.codecType == VideoCodecType::kVideoCodecAV1 &&
       codec_settings.numberOfSimulcastStreams <= 1 &&
       codec_settings.GetScalabilityMode().has_value()) {
-    for (int si = 0; si < ScalabilityModeToNumSpatialLayers(
-                              *(codec_settings.GetScalabilityMode()));
-         ++si) {
+    const int num_spatial_layers = ScalabilityModeToNumSpatialLayers(
+        *(codec_settings.GetScalabilityMode()));
+    for (int si = 0; si < num_spatial_layers; ++si) {
+      if (si >= static_cast<int>(kMaxSpatialLayers)) {
+        RTC_LOG(LS_WARNING)
+            << "AV1 scalability mode specifies " << num_spatial_layers
+            << " spatial layers, which exceeds kMaxSpatialLayers ("
+            << kMaxSpatialLayers << ")";
+        break;
+      }
       if (codec_settings.spatialLayers[si].active) {
         min_bitrates_bps_[si] =
             std::max(codec_settings.minBitrate * 1000,
@@ -88,6 +95,13 @@
   } else if (codec_settings.codecType == VideoCodecType::kVideoCodecVP9 &&
              codec_settings.numberOfSimulcastStreams <= 1) {
     for (size_t si = 0; si < codec_settings.VP9().numberOfSpatialLayers; ++si) {
+      if (si >= kMaxSpatialLayers) {
+        RTC_LOG(LS_WARNING)
+            << "VP9 specifies " << codec_settings.VP9().numberOfSpatialLayers
+            << " spatial layers, which exceeds kMaxSpatialLayers ("
+            << kMaxSpatialLayers << ")";
+        break;
+      }
       if (codec_settings.spatialLayers[si].active) {
         min_bitrates_bps_[si] =
             std::max(codec_settings.minBitrate * 1000,
@@ -96,6 +110,13 @@
     }
   } else {
     for (size_t si = 0; si < codec_settings.numberOfSimulcastStreams; ++si) {
+      if (si >= kMaxSpatialLayers) {
+        RTC_LOG(LS_WARNING)
+            << "Codec specifies " << codec_settings.numberOfSimulcastStreams
+            << " simulcast streams, which exceeds kMaxSpatialLayers ("
+            << kMaxSpatialLayers << ")";
+        break;
+      }
       if (codec_settings.simulcastStream[si].active) {
         min_bitrates_bps_[si] =
             std::max(codec_settings.minBitrate * 1000,
@@ -372,6 +393,12 @@
   // Copy allocation into current state and re-allocate.
   for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
     current_fps_allocation_[si] = encoder_info.fps_allocation[si];
+    if (current_fps_allocation_[si].size() > kMaxTemporalStreams) {
+      RTC_LOG(LS_WARNING) << "fps_allocation has more than "
+                          << kMaxTemporalStreams
+                          << " temporal streams. Truncating.";
+      current_fps_allocation_[si].resize(kMaxTemporalStreams);
+    }
   }
 
   // Trigger re-allocation so that overshoot detectors have correct targets.
@@ -381,6 +408,15 @@
 void EncoderBitrateAdjuster::OnEncodedFrame(DataSize size,
                                             int stream_index,
                                             int temporal_index) {
+  if (stream_index < 0 || stream_index >= static_cast<int>(kMaxSpatialLayers) ||
+      temporal_index < 0 ||
+      temporal_index >= static_cast<int>(kMaxTemporalStreams)) {
+    RTC_LOG(LS_WARNING) << "OnEncodedFrame called with invalid layer: "
+                        << "stream_index = " << stream_index
+                        << ", temporal_index = " << temporal_index;
+    return;
+  }
+
   ++frames_since_layout_change_;
   // Detectors may not exist, for instance if ScreenshareLayers is used.
   auto& detector = overshoot_detectors_[stream_index][temporal_index];
diff --git a/video/encoder_bitrate_adjuster_unittest.cc b/video/encoder_bitrate_adjuster_unittest.cc
index 7514405..14bb11e 100644
--- a/video/encoder_bitrate_adjuster_unittest.cc
+++ b/video/encoder_bitrate_adjuster_unittest.cc
@@ -581,6 +581,40 @@
   ExpectNear(expected_input_allocation, current_adjusted_allocation_, 0.01);
 }
 
+TEST_P(EncoderBitrateAdjusterTest, OnEncodedFrameInvalidLayers) {
+  current_input_allocation_.SetBitrate(0, 0, 300000);
+  target_framerate_fps_ = 30;
+  SetUpAdjuster(1, 1, false);
+
+  // Call OnEncodedFrame with invalid stream indices and make sure it doesn't
+  // crash.
+  adjuster_->OnEncodedFrame(DataSize::Bytes(1000), -1, 0);
+  adjuster_->OnEncodedFrame(DataSize::Bytes(1000), kMaxSpatialLayers, 0);
+
+  // Call OnEncodedFrame with invalid temporal indices and make sure it doesn't
+  // crash.
+  adjuster_->OnEncodedFrame(DataSize::Bytes(1000), 0, -1);
+  adjuster_->OnEncodedFrame(DataSize::Bytes(1000), 0, kMaxTemporalStreams);
+}
+
+TEST_P(EncoderBitrateAdjusterTest,
+       OnEncoderInfoTruncatesTooManyTemporalStreams) {
+  current_input_allocation_.SetBitrate(0, 0, 300000);
+  target_framerate_fps_ = 30;
+  SetUpAdjuster(1, 1, false);
+
+  // Create an EncoderInfo with a temporal allocation larger than
+  // kMaxTemporalStreams.
+  VideoEncoder::EncoderInfo encoder_info;
+  encoder_info.fps_allocation[0].resize(kMaxTemporalStreams + 2);
+  for (size_t ti = 0; ti < kMaxTemporalStreams + 2; ++ti) {
+    encoder_info.fps_allocation[0][ti] = 255;
+  }
+
+  // This should truncate to kMaxTemporalStreams and not crash.
+  adjuster_->OnEncoderInfo(encoder_info);
+}
+
 INSTANTIATE_TEST_SUITE_P(
     AdjustWithHeadroomVariations,
     EncoderBitrateAdjusterTest,