From 2f1509395b56fe3175b27dc2ac76e8f749c809f7 Mon Sep 17 00:00:00 2001 From: pbos Date: Tue, 30 Jun 2015 08:23:36 -0700 Subject: [PATCH] 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} --- .../rtp_rtcp/source/rtp_format_h264.cc | 22 +++++++++++++++---- .../source/rtp_format_h264_unittest.cc | 18 +++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc b/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc index a5b42ab3e..ba41c620c 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 @@ enum NalDefs { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F }; // 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 @@ void ParseSingleNalu(RtpDepacketizer::ParsedPayload* parsed_payload, 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 @@ void ParseSingleNalu(RtpDepacketizer::ParsedPayload* parsed_payload, 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 @@ void ParseFuaNalu(RtpDepacketizer::ParsedPayload* parsed_payload, &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 @@ bool RtpDepacketizerH264::Parse(ParsedPayload* parsed_payload, 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 8fa83010f..7bf1cf1b6 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 @@ TEST_F(RtpDepacketizerH264Test, TestEmptyPayload) { 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