Use signed integers for limiting packet size in video packetizers

Using signed integers allows to centralize checking of edge cases
in RtpPacketizer::SplitAboutEqually and
reduce chance of hitting issues with size_t underflow

Bug: webrtc:9680
Change-Id: Ic05bf0a9565a277c4608f43061ca46cf44e82d08
Reviewed-on: https://webrtc-review.googlesource.com/98602
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24618}
diff --git a/modules/rtp_rtcp/source/rtp_format.cc b/modules/rtp_rtcp/source/rtp_format.cc
index 8a6e8f1..e966833 100644
--- a/modules/rtp_rtcp/source/rtp_format.cc
+++ b/modules/rtp_rtcp/source/rtp_format.cc
@@ -59,26 +59,42 @@
   }
 }
 
-std::vector<size_t> RtpPacketizer::SplitAboutEqually(
-    size_t payload_len,
+std::vector<int> RtpPacketizer::SplitAboutEqually(
+    int payload_len,
     const PayloadSizeLimits& limits) {
-  RTC_CHECK_GT(limits.max_payload_len, limits.first_packet_reduction_len);
-  RTC_CHECK_GT(limits.max_payload_len, limits.last_packet_reduction_len);
+  RTC_DCHECK_GT(payload_len, 0);
+  // First or last packet larger than normal are unsupported.
+  RTC_DCHECK_GE(limits.first_packet_reduction_len, 0);
+  RTC_DCHECK_GE(limits.last_packet_reduction_len, 0);
 
+  std::vector<int> result;
+  if (limits.max_payload_len - limits.first_packet_reduction_len < 1 ||
+      limits.max_payload_len - limits.last_packet_reduction_len < 1) {
+    // Capacity is not enough to put a single byte into one of the packets.
+    return result;
+  }
   // First and last packet of the frame can be smaller. Pretend that it's
   // the same size, but we must write more payload to it.
   // Assume frame fits in single packet if packet has extra space for sum
   // of first and last packets reductions.
-  size_t total_bytes = payload_len + limits.first_packet_reduction_len +
-                       limits.last_packet_reduction_len;
+  int total_bytes = payload_len + limits.first_packet_reduction_len +
+                    limits.last_packet_reduction_len;
   // Integer divisions with rounding up.
-  size_t num_packets_left =
+  int num_packets_left =
       (total_bytes + limits.max_payload_len - 1) / limits.max_payload_len;
-  size_t bytes_per_packet = total_bytes / num_packets_left;
-  size_t num_larger_packets = total_bytes % num_packets_left;
-  size_t remaining_data = payload_len;
 
-  std::vector<size_t> result;
+  if (payload_len < num_packets_left) {
+    // Edge case where limits force to have more packets than there are payload
+    // bytes. This may happen when there is single byte of payload that can't be
+    // put into single packet if
+    // first_packet_reduction + last_packet_reduction >= max_payload_len.
+    return result;
+  }
+
+  int bytes_per_packet = total_bytes / num_packets_left;
+  int num_larger_packets = total_bytes % num_packets_left;
+  int remaining_data = payload_len;
+
   result.reserve(num_packets_left);
   bool first_packet = true;
   while (remaining_data > 0) {
@@ -86,7 +102,7 @@
     // per-packet payload size when needed.
     if (num_packets_left == num_larger_packets)
       ++bytes_per_packet;
-    size_t current_packet_bytes = bytes_per_packet;
+    int current_packet_bytes = bytes_per_packet;
     if (first_packet) {
       if (current_packet_bytes > limits.first_packet_reduction_len + 1)
         current_packet_bytes -= limits.first_packet_reduction_len;
diff --git a/modules/rtp_rtcp/source/rtp_format.h b/modules/rtp_rtcp/source/rtp_format.h
index 7a1f494..9fad4cf 100644
--- a/modules/rtp_rtcp/source/rtp_format.h
+++ b/modules/rtp_rtcp/source/rtp_format.h
@@ -27,9 +27,9 @@
 class RtpPacketizer {
  public:
   struct PayloadSizeLimits {
-    size_t max_payload_len = 1200;
-    size_t first_packet_reduction_len = 0;
-    size_t last_packet_reduction_len = 0;
+    int max_payload_len = 1200;
+    int first_packet_reduction_len = 0;
+    int last_packet_reduction_len = 0;
   };
   static std::unique_ptr<RtpPacketizer> Create(
       VideoCodecType type,
@@ -51,8 +51,9 @@
   virtual bool NextPacket(RtpPacketToSend* packet) = 0;
 
   // Split payload_len into sum of integers with respect to |limits|.
-  static std::vector<size_t> SplitAboutEqually(size_t payload_len,
-                                               const PayloadSizeLimits& limits);
+  // Returns empty vector on failure.
+  static std::vector<int> SplitAboutEqually(int payload_len,
+                                            const PayloadSizeLimits& limits);
 };
 
 // TODO(sprang): Update the depacketizer to return a std::unqie_ptr with a copy
diff --git a/modules/rtp_rtcp/source/rtp_format_unittest.cc b/modules/rtp_rtcp/source/rtp_format_unittest.cc
index 8fb780e..a79c434 100644
--- a/modules/rtp_rtcp/source/rtp_format_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_format_unittest.cc
@@ -31,7 +31,7 @@
 // adjustement provided by limits,
 // i.e. last packet expected to be smaller than 'average' by reduction_len.
 int EffectivePacketsSizeDifference(
-    std::vector<size_t> sizes,
+    std::vector<int> sizes,
     const RtpPacketizer::PayloadSizeLimits& limits) {
   // Account for larger last packet header.
   sizes.back() += limits.last_packet_reduction_len;
@@ -41,7 +41,7 @@
   return *minmax.second - *minmax.first;
 }
 
-size_t Sum(const std::vector<size_t>& sizes) {
+int Sum(const std::vector<int>& sizes) {
   return std::accumulate(sizes.begin(), sizes.end(), 0);
 }
 
@@ -50,8 +50,7 @@
   limits.max_payload_len = 5;
   limits.last_packet_reduction_len = 2;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(13, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(13, limits);
 
   EXPECT_THAT(Sum(payload_sizes), 13);
 }
@@ -61,8 +60,7 @@
   limits.max_payload_len = 5;
   limits.last_packet_reduction_len = 2;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(13, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(13, limits);
 
   EXPECT_THAT(payload_sizes, Each(Le(limits.max_payload_len)));
 }
@@ -73,8 +71,7 @@
   limits.max_payload_len = 5;
   limits.first_packet_reduction_len = 2;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(13, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(13, limits);
 
   ASSERT_THAT(payload_sizes, Not(IsEmpty()));
   EXPECT_EQ(payload_sizes.front() + limits.first_packet_reduction_len,
@@ -87,8 +84,7 @@
   limits.max_payload_len = 5;
   limits.last_packet_reduction_len = 2;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(13, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(13, limits);
 
   ASSERT_THAT(payload_sizes, Not(IsEmpty()));
   EXPECT_LE(payload_sizes.back() + limits.last_packet_reduction_len,
@@ -100,8 +96,7 @@
   limits.max_payload_len = 5;
   limits.last_packet_reduction_len = 2;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(13, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(13, limits);
 
   EXPECT_EQ(EffectivePacketsSizeDifference(payload_sizes, limits), 0);
 }
@@ -112,8 +107,7 @@
   limits.max_payload_len = 5;
   limits.last_packet_reduction_len = 2;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(13, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(13, limits);
   // Computed by hand. 3 packets would have exactly capacity 3*5-2=13
   // (max length - for each packet minus last packet reduction).
   EXPECT_THAT(payload_sizes, SizeIs(3));
@@ -124,8 +118,7 @@
   limits.max_payload_len = 7;
   limits.last_packet_reduction_len = 5;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(28, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(28, limits);
 
   EXPECT_THAT(Sum(payload_sizes), 28);
 }
@@ -136,8 +129,7 @@
   limits.max_payload_len = 7;
   limits.last_packet_reduction_len = 5;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(28, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(28, limits);
 
   EXPECT_THAT(payload_sizes, Each(Le(limits.max_payload_len)));
 }
@@ -148,8 +140,7 @@
   limits.max_payload_len = 7;
   limits.first_packet_reduction_len = 5;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(28, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(28, limits);
 
   EXPECT_LE(payload_sizes.front() + limits.first_packet_reduction_len,
             limits.max_payload_len);
@@ -161,8 +152,7 @@
   limits.max_payload_len = 7;
   limits.last_packet_reduction_len = 5;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(28, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(28, limits);
 
   EXPECT_LE(payload_sizes.back(),
             limits.max_payload_len - limits.last_packet_reduction_len);
@@ -174,8 +164,7 @@
   limits.max_payload_len = 7;
   limits.last_packet_reduction_len = 5;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(28, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(28, limits);
 
   EXPECT_LE(EffectivePacketsSizeDifference(payload_sizes, limits), 1);
 }
@@ -186,8 +175,7 @@
   limits.max_payload_len = 7;
   limits.last_packet_reduction_len = 5;
 
-  std::vector<size_t> payload_sizes =
-      RtpPacketizer::SplitAboutEqually(24, limits);
+  std::vector<int> payload_sizes = RtpPacketizer::SplitAboutEqually(24, limits);
   // Computed by hand. 4 packets would have capacity 4*7-5=23 (max length -
   // for each packet minus last packet reduction).
   // 5 packets is enough for kPayloadSize.
@@ -203,11 +191,11 @@
   // Naive implementation would split 1450 payload + 1050 reduction bytes into 5
   // packets 500 bytes each, thus leaving first packet zero bytes and even less
   // to last packet.
-  std::vector<size_t> payload_sizes =
+  std::vector<int> payload_sizes =
       RtpPacketizer::SplitAboutEqually(1450, limits);
 
-  EXPECT_EQ(Sum(payload_sizes), 1450u);
-  EXPECT_THAT(payload_sizes, Each(Gt(0u)));
+  EXPECT_EQ(Sum(payload_sizes), 1450);
+  EXPECT_THAT(payload_sizes, Each(Gt(0)));
 }
 
 TEST(RtpPacketizerSplitAboutEqually,
@@ -232,5 +220,55 @@
   EXPECT_THAT(RtpPacketizer::SplitAboutEqually(20, limits), ElementsAre(9, 11));
 }
 
+TEST(RtpPacketizerSplitAboutEqually, RejectsZeroMaxPayloadLen) {
+  RtpPacketizer::PayloadSizeLimits limits;
+  limits.max_payload_len = 0;
+
+  EXPECT_THAT(RtpPacketizer::SplitAboutEqually(20, limits), IsEmpty());
+}
+
+TEST(RtpPacketizerSplitAboutEqually, RejectsZeroFirstPacketLen) {
+  RtpPacketizer::PayloadSizeLimits limits;
+  limits.max_payload_len = 5;
+  limits.first_packet_reduction_len = 5;
+
+  EXPECT_THAT(RtpPacketizer::SplitAboutEqually(20, limits), IsEmpty());
+}
+
+TEST(RtpPacketizerSplitAboutEqually, RejectsZeroLastPacketLen) {
+  RtpPacketizer::PayloadSizeLimits limits;
+  limits.max_payload_len = 5;
+  limits.last_packet_reduction_len = 5;
+
+  EXPECT_THAT(RtpPacketizer::SplitAboutEqually(20, limits), IsEmpty());
+}
+
+TEST(RtpPacketizerSplitAboutEqually, CantPutSinglePayloadByteInTwoPackets) {
+  RtpPacketizer::PayloadSizeLimits limits;
+  limits.max_payload_len = 10;
+  limits.first_packet_reduction_len = 6;
+  limits.last_packet_reduction_len = 4;
+
+  EXPECT_THAT(RtpPacketizer::SplitAboutEqually(1, limits), IsEmpty());
+}
+
+TEST(RtpPacketizerSplitAboutEqually, CanPutTwoPayloadBytesInTwoPackets) {
+  RtpPacketizer::PayloadSizeLimits limits;
+  limits.max_payload_len = 10;
+  limits.first_packet_reduction_len = 6;
+  limits.last_packet_reduction_len = 4;
+
+  EXPECT_THAT(RtpPacketizer::SplitAboutEqually(2, limits), ElementsAre(1, 1));
+}
+
+TEST(RtpPacketizerSplitAboutEqually, CanPutSinglePayloadByteInOnePacket) {
+  RtpPacketizer::PayloadSizeLimits limits;
+  limits.max_payload_len = 11;
+  limits.first_packet_reduction_len = 6;
+  limits.last_packet_reduction_len = 4;
+
+  EXPECT_THAT(RtpPacketizer::SplitAboutEqually(1, limits), ElementsAre(1));
+}
+
 }  // namespace
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_format_video_generic.h b/modules/rtp_rtcp/source/rtp_format_video_generic.h
index 7d14886..03509f5 100644
--- a/modules/rtp_rtcp/source/rtp_format_video_generic.h
+++ b/modules/rtp_rtcp/source/rtp_format_video_generic.h
@@ -52,8 +52,8 @@
   uint8_t header_[3];
   size_t header_size_;
   rtc::ArrayView<const uint8_t> remaining_payload_;
-  std::vector<size_t> payload_sizes_;
-  std::vector<size_t>::const_iterator current_packet_;
+  std::vector<int> payload_sizes_;
+  std::vector<int>::const_iterator current_packet_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(RtpPacketizerGeneric);
 };
diff --git a/modules/rtp_rtcp/source/rtp_format_video_generic_unittest.cc b/modules/rtp_rtcp/source/rtp_format_video_generic_unittest.cc
index 8cf1ffa..aa2829b 100644
--- a/modules/rtp_rtcp/source/rtp_format_video_generic_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_format_video_generic_unittest.cc
@@ -33,10 +33,9 @@
 
 constexpr RtpPacketizer::PayloadSizeLimits kNoSizeLimits;
 
-std::vector<size_t> NextPacketFillPayloadSizes(
-    RtpPacketizerGeneric* packetizer) {
+std::vector<int> NextPacketFillPayloadSizes(RtpPacketizerGeneric* packetizer) {
   RtpPacketToSend packet(nullptr);
-  std::vector<size_t> result;
+  std::vector<int> result;
   while (packetizer->NextPacket(&packet)) {
     result.push_back(packet.payload_size());
   }
@@ -52,7 +51,7 @@
   RtpPacketizerGeneric packetizer(kPayload, limits, RTPVideoHeader(),
                                   kVideoFrameKey);
 
-  std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
+  std::vector<int> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
 
   EXPECT_THAT(payload_sizes, Each(Le(limits.max_payload_len)));
 }
@@ -66,7 +65,7 @@
   RtpPacketizerGeneric packetizer(kPayload, limits, RTPVideoHeader(),
                                   kVideoFrameKey);
 
-  std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
+  std::vector<int> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
 
   // With kPayloadSize > max_payload_len^2, there should be packets that use
   // all the payload, otherwise it is possible to use less packets.
@@ -94,7 +93,7 @@
 }
 
 TEST(RtpPacketizerVideoGeneric, RespectsMaxPayloadSizeWithExtendedHeader) {
-  const size_t kPayloadSize = 50;
+  const int kPayloadSize = 50;
   const uint8_t kPayload[kPayloadSize] = {};
 
   RtpPacketizer::PayloadSizeLimits limits;
@@ -104,13 +103,13 @@
   RtpPacketizerGeneric packetizer(kPayload, limits, rtp_video_header,
                                   kVideoFrameKey);
 
-  std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
+  std::vector<int> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
 
   EXPECT_THAT(payload_sizes, Each(Le(limits.max_payload_len)));
 }
 
 TEST(RtpPacketizerVideoGeneric, UsesMaxPayloadSizeWithExtendedHeader) {
-  const size_t kPayloadSize = 50;
+  const int kPayloadSize = 50;
   const uint8_t kPayload[kPayloadSize] = {};
 
   RtpPacketizer::PayloadSizeLimits limits;
@@ -119,7 +118,7 @@
   rtp_video_header.generic.emplace().frame_id = 37;
   RtpPacketizerGeneric packetizer(kPayload, limits, rtp_video_header,
                                   kVideoFrameKey);
-  std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
+  std::vector<int> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
 
   // With kPayloadSize > max_payload_len^2, there should be packets that use
   // all the payload, otherwise it is possible to use less packets.
@@ -127,7 +126,7 @@
 }
 
 TEST(RtpPacketizerVideoGeneric, FrameIdOver15bitsWrapsAround) {
-  const size_t kPayloadSize = 13;
+  const int kPayloadSize = 13;
   const uint8_t kPayload[kPayloadSize] = {};
 
   RTPVideoHeader rtp_video_header;
@@ -146,7 +145,7 @@
 }
 
 TEST(RtpPacketizerVideoGeneric, NoFrameIdDoesNotWriteExtendedHeader) {
-  const size_t kPayloadSize = 13;
+  const int kPayloadSize = 13;
   const uint8_t kPayload[kPayloadSize] = {};
 
   RtpPacketizerGeneric packetizer(kPayload, kNoSizeLimits, RTPVideoHeader(),
diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.cc b/modules/rtp_rtcp/source/rtp_format_vp8.cc
index b219dac..40bc291 100644
--- a/modules/rtp_rtcp/source/rtp_format_vp8.cc
+++ b/modules/rtp_rtcp/source/rtp_format_vp8.cc
@@ -173,13 +173,6 @@
                                    PayloadSizeLimits limits,
                                    const RTPVideoHeaderVP8& hdr_info)
     : hdr_(BuildHeader(hdr_info)), remaining_payload_(payload) {
-  if (limits.max_payload_len - limits.last_packet_reduction_len <
-      hdr_.size() + 1) {
-    // The provided payload length is not long enough for the payload
-    // descriptor and one payload byte in the last packet.
-    current_packet_ = payload_sizes_.begin();
-    return;
-  }
   limits.max_payload_len -= hdr_.size();
   payload_sizes_ = SplitAboutEqually(payload.size(), limits);
   current_packet_ = payload_sizes_.begin();
diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.h b/modules/rtp_rtcp/source/rtp_format_vp8.h
index 2fdd6c5..e4bc36e 100644
--- a/modules/rtp_rtcp/source/rtp_format_vp8.h
+++ b/modules/rtp_rtcp/source/rtp_format_vp8.h
@@ -61,8 +61,8 @@
 
   RawHeader hdr_;
   rtc::ArrayView<const uint8_t> remaining_payload_;
-  std::vector<size_t> payload_sizes_;
-  std::vector<size_t>::const_iterator current_packet_;
+  std::vector<int> payload_sizes_;
+  std::vector<int>::const_iterator current_packet_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(RtpPacketizerVp8);
 };
diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc
index bf8150d..f72740e9 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video.cc
@@ -335,20 +335,18 @@
     retransmission_settings = retransmission_settings_;
   }
 
-  size_t packet_capacity = rtp_sender_->MaxRtpPacketSize() -
-                           fec_packet_overhead -
-                           (rtp_sender_->RtxStatus() ? kRtxHeaderSize : 0);
+  int packet_capacity = rtp_sender_->MaxRtpPacketSize() - fec_packet_overhead -
+                        (rtp_sender_->RtxStatus() ? kRtxHeaderSize : 0);
   RTC_DCHECK_LE(packet_capacity, rtp_header->capacity());
   RTC_DCHECK_GT(packet_capacity, rtp_header->headers_size());
   RTC_DCHECK_GT(packet_capacity, last_packet->headers_size());
-  size_t max_data_payload_length = packet_capacity - rtp_header->headers_size();
+  RtpPacketizer::PayloadSizeLimits limits;
+  limits.max_payload_len = packet_capacity - rtp_header->headers_size();
+
   RTC_DCHECK_GE(last_packet->headers_size(), rtp_header->headers_size());
-  size_t last_packet_reduction_len =
+  limits.last_packet_reduction_len =
       last_packet->headers_size() - rtp_header->headers_size();
 
-  RtpPacketizer::PayloadSizeLimits limits;
-  limits.max_payload_len = max_data_payload_length;
-  limits.last_packet_reduction_len = last_packet_reduction_len;
   std::unique_ptr<RtpPacketizer> packetizer = RtpPacketizer::Create(
       video_type, rtc::MakeArrayView(payload_data, payload_size), limits,
       *video_header, frame_type, fragmentation);
@@ -368,9 +366,10 @@
                        : absl::make_unique<RtpPacketToSend>(*rtp_header);
     if (!packetizer->NextPacket(packet.get()))
       return false;
-    RTC_DCHECK_LE(packet->payload_size(),
-                  last ? max_data_payload_length - last_packet_reduction_len
-                       : max_data_payload_length);
+    RTC_DCHECK_LE(
+        packet->payload_size(),
+        last ? limits.max_payload_len - limits.last_packet_reduction_len
+             : limits.max_payload_len);
     if (!rtp_sender_->AssignSequenceNumber(packet.get()))
       return false;