Fix STAP-A bug where we might overflow the packet buffer due to not accounting for the length of the length field.
BUG=3679
R=mflodman@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/17079004
git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@6881 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/modules/rtp_rtcp/source/rtp_format_h264.cc b/modules/rtp_rtcp/source/rtp_format_h264.cc
index 01ca426..85bd946 100644
--- a/modules/rtp_rtcp/source/rtp_format_h264.cc
+++ b/modules/rtp_rtcp/source/rtp_format_h264.cc
@@ -29,6 +29,7 @@
static const size_t kNalHeaderSize = 1;
static const size_t kFuAHeaderSize = 2;
+static const size_t kLengthFieldSize = 2;
// Bit masks for FU (A and B) indicators.
enum NalDefs { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F };
@@ -157,30 +158,34 @@
// Aggregate fragments into one packet (STAP-A).
size_t payload_size_left = max_payload_len_;
int aggregated_fragments = 0;
+ size_t fragment_headers_length = 0;
assert(payload_size_left >= fragment_length);
- while (payload_size_left >= fragment_length) {
- if (fragment_length > 0) {
- assert(fragment_length > 0);
- uint8_t header = payload_data_[fragment_offset];
- packets_.push(Packet(fragment_offset,
- fragment_length,
- aggregated_fragments == 0,
- false,
- true,
- header));
- payload_size_left -= fragment_length;
- // If we are going to try to aggregate more fragments into this packet
- // we need to add the STAP-A NALU header.
- if (aggregated_fragments == 0 && payload_size_left > 0)
- payload_size_left -= kNalHeaderSize;
- ++aggregated_fragments;
- }
+ while (payload_size_left >= fragment_length + fragment_headers_length) {
+ assert(fragment_length > 0);
+ uint8_t header = payload_data_[fragment_offset];
+ packets_.push(Packet(fragment_offset,
+ fragment_length,
+ aggregated_fragments == 0,
+ false,
+ true,
+ header));
+ payload_size_left -= fragment_length;
+ payload_size_left -= fragment_headers_length;
+
// Next fragment.
++fragment_index;
if (fragment_index == fragmentation_.fragmentationVectorSize)
break;
fragment_offset = fragmentation_.fragmentationOffset[fragment_index];
fragment_length = fragmentation_.fragmentationLength[fragment_index];
+
+ fragment_headers_length = kLengthFieldSize;
+ // If we are going to try to aggregate more fragments into this packet
+ // we need to add the STAP-A NALU header and a length field for the first
+ // NALU of this packet.
+ if (aggregated_fragments == 0)
+ fragment_headers_length += kNalHeaderSize + kLengthFieldSize;
+ ++aggregated_fragments;
}
packets_.back().last_fragment = true;
return fragment_index;
@@ -203,13 +208,15 @@
*bytes_to_send = packet.size;
memcpy(buffer, &payload_data_[packet.offset], packet.size);
packets_.pop();
+ assert(*bytes_to_send <= max_payload_len_);
} else if (packet.aggregated) {
NextAggregatePacket(buffer, bytes_to_send);
+ assert(*bytes_to_send <= max_payload_len_);
} else {
NextFragmentPacket(buffer, bytes_to_send);
+ assert(*bytes_to_send <= max_payload_len_);
}
*last_packet = packets_.empty();
- assert(*bytes_to_send <= max_payload_len_);
return true;
}
@@ -224,8 +231,8 @@
while (packet.aggregated) {
// Add NAL unit length field.
RtpUtility::AssignUWord16ToBuffer(&buffer[index], packet.size);
- index += 2;
- *bytes_to_send += 2;
+ index += kLengthFieldSize;
+ *bytes_to_send += kLengthFieldSize;
// Add NAL unit.
memcpy(&buffer[index], &payload_data_[packet.offset], packet.size);
index += packet.size;
diff --git a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
index bf9d381..55b9712 100644
--- a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
@@ -211,7 +211,8 @@
}
TEST(RtpPacketizerH264Test, TestStapA) {
- const size_t kFrameSize = kMaxPayloadSize - 100;
+ const size_t kFrameSize = kMaxPayloadSize - 3 * kLengthFieldLength -
+ kNalHeaderSize;
uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7.
0x08, 0xFF, // F=0, NRI=0, Type=8.
0x05}; // F=0, NRI=0, Type=5.
@@ -245,6 +246,50 @@
EXPECT_FALSE(packetizer->NextPacket(packet, &length, &last));
}
+TEST(RtpPacketizerH264Test, TestTooSmallForStapAHeaders) {
+ const size_t kFrameSize = kMaxPayloadSize - 1;
+ uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7.
+ 0x08, 0xFF, // F=0, NRI=0, Type=8.
+ 0x05}; // F=0, NRI=0, Type=5.
+ const size_t kPayloadOffset = 5;
+ for (size_t i = 0; i < kFrameSize - kPayloadOffset; ++i)
+ frame[i + kPayloadOffset] = i;
+ RTPFragmentationHeader fragmentation;
+ fragmentation.VerifyAndAllocateFragmentationHeader(3);
+ fragmentation.fragmentationOffset[0] = 0;
+ fragmentation.fragmentationLength[0] = 2;
+ fragmentation.fragmentationOffset[1] = 2;
+ fragmentation.fragmentationLength[1] = 2;
+ fragmentation.fragmentationOffset[2] = 4;
+ fragmentation.fragmentationLength[2] =
+ kNalHeaderSize + kFrameSize - kPayloadOffset;
+ scoped_ptr<RtpPacketizer> packetizer(
+ RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize));
+ packetizer->SetPayloadData(frame, kFrameSize, &fragmentation);
+
+ uint8_t packet[kMaxPayloadSize] = {0};
+ size_t length = 0;
+ bool last = false;
+ ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last));
+ size_t expected_packet_size = kNalHeaderSize;
+ for (size_t i = 0; i < 2; ++i) {
+ expected_packet_size += kLengthFieldLength +
+ fragmentation.fragmentationLength[i];
+ }
+ ASSERT_EQ(expected_packet_size, length);
+ EXPECT_FALSE(last);
+ for (size_t i = 0; i < 2; ++i)
+ VerifyStapAPayload(fragmentation, 0, i, frame, kFrameSize, packet, length);
+
+ ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last));
+ expected_packet_size = fragmentation.fragmentationLength[2];
+ ASSERT_EQ(expected_packet_size, length);
+ EXPECT_TRUE(last);
+ VerifySingleNaluPayload(fragmentation, 2, frame, kFrameSize, packet, length);
+
+ EXPECT_FALSE(packetizer->NextPacket(packet, &length, &last));
+}
+
TEST(RtpPacketizerH264Test, TestMixedStapA_FUA) {
const size_t kFuaNaluSize = 2 * (kMaxPayloadSize - 100);
const size_t kStapANaluSize = 100;