Prevent OOB read on truncated H264 headers.

Prevents OOB reads on truncated FU-A NAL units, StapA headers and past
truncation just after StapA headers.

BUG=webrtc:4771
R=stefan@webrtc.org

Review URL: https://codereview.webrtc.org/1218023003

Cr-Commit-Position: refs/heads/master@{#9522}
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc b/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc
index a5b42ab..ba41c62 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc
@@ -40,7 +40,7 @@
 // Bit masks for FU (A and B) headers.
 enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 };
 
-void ParseSingleNalu(RtpDepacketizer::ParsedPayload* parsed_payload,
+bool ParseSingleNalu(RtpDepacketizer::ParsedPayload* parsed_payload,
                      const uint8_t* payload_data,
                      size_t payload_data_length) {
   parsed_payload->type.Video.width = 0;
@@ -55,6 +55,10 @@
   uint8_t nal_type = payload_data[0] & kTypeMask;
   if (nal_type == kStapA) {
     // Skip the StapA header (StapA nal type + length).
+    if (payload_data_length <= kStapAHeaderSize) {
+      LOG(LS_ERROR) << "StapA header truncated.";
+      return false;
+    }
     nal_type = payload_data[kStapAHeaderSize] & kTypeMask;
     nalu_start += kStapAHeaderSize;
     nalu_length -= kStapAHeaderSize;
@@ -82,12 +86,17 @@
       parsed_payload->frame_type = kVideoFrameDelta;
       break;
   }
+  return true;
 }
 
-void ParseFuaNalu(RtpDepacketizer::ParsedPayload* parsed_payload,
+bool ParseFuaNalu(RtpDepacketizer::ParsedPayload* parsed_payload,
                   const uint8_t* payload_data,
                   size_t payload_data_length,
                   size_t* offset) {
+  if (payload_data_length < kFuAHeaderSize) {
+    LOG(LS_ERROR) << "FU-A NAL units truncated.";
+    return false;
+  }
   uint8_t fnri = payload_data[0] & (kFBit | kNriMask);
   uint8_t original_nal_type = payload_data[1] & kTypeMask;
   bool first_fragment = (payload_data[1] & kSBit) > 0;
@@ -114,6 +123,7 @@
       &parsed_payload->type.Video.codecHeader.H264;
   h264_header->packetization_type = kH264FuA;
   h264_header->nalu_type = original_nal_type;
+  return true;
 }
 }  // namespace
 
@@ -326,11 +336,15 @@
   size_t offset = 0;
   if (nal_type == kFuA) {
     // Fragmented NAL units (FU-A).
-    ParseFuaNalu(parsed_payload, payload_data, payload_data_length, &offset);
+    if (!ParseFuaNalu(
+            parsed_payload, payload_data, payload_data_length, &offset)) {
+      return false;
+    }
   } else {
     // We handle STAP-A and single NALU's the same way here. The jitter buffer
     // will depacketize the STAP-A into NAL units later.
-    ParseSingleNalu(parsed_payload, payload_data, payload_data_length);
+    if (!ParseSingleNalu(parsed_payload, payload_data, payload_data_length))
+      return false;
   }
 
   parsed_payload->payload = payload_data + offset;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
index 8fa8301..7bf1cf1 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
@@ -545,4 +545,22 @@
   EXPECT_FALSE(depacketizer_->Parse(&payload, garbage_ptr, 0));
 }
 
+TEST_F(RtpDepacketizerH264Test, TestTruncatedFuaNalu) {
+  const uint8_t kPayload[] = {0x9c};
+  RtpDepacketizer::ParsedPayload payload;
+  EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload)));
+}
+
+TEST_F(RtpDepacketizerH264Test, TestTruncatedSingleStapANalu) {
+  const uint8_t kPayload[] = {0xd8, 0x27};
+  RtpDepacketizer::ParsedPayload payload;
+  EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload)));
+}
+
+TEST_F(RtpDepacketizerH264Test, TestTruncationJustAfterSingleStapANalu) {
+  const uint8_t kPayload[] = {0x38, 0x27, 0x27};
+  RtpDepacketizer::ParsedPayload payload;
+  EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload)));
+}
+
 }  // namespace webrtc