From f752bca4a5a9dcf6218e820f50623201a71cb0a7 Mon Sep 17 00:00:00 2001 From: kthelgason Date: Thu, 3 Nov 2016 17:30:34 -0700 Subject: [PATCH] Allow H264 bitstream parser to fail gracefully This CL allows the H264 bitstream parser to abort and report an error on invalid input rather than crashing, and it fixes several crashes found when fuzzing. BUG=webrtc:6454 R=magjed@webrtc.org,pbos@webrtc.org Review-Url: https://codereview.webrtc.org/2471973003 Cr-Commit-Position: refs/heads/master@{#14929} --- .../h264/h264_bitstream_parser.cc | 124 +++++++++--------- .../common_video/h264/h264_bitstream_parser.h | 12 +- webrtc/common_video/h264/h264_common.cc | 1 + 3 files changed, 73 insertions(+), 64 deletions(-) diff --git a/webrtc/common_video/h264/h264_bitstream_parser.cc b/webrtc/common_video/h264/h264_bitstream_parser.cc index 28b92e1241..5f89c50af1 100644 --- a/webrtc/common_video/h264/h264_bitstream_parser.cc +++ b/webrtc/common_video/h264/h264_bitstream_parser.cc @@ -21,23 +21,30 @@ namespace webrtc { -#define RETURN_FALSE_ON_FAIL(x) \ +#define RETURN_ON_FAIL(x, res) \ if (!(x)) { \ LOG_F(LS_ERROR) << "FAILED: " #x; \ - return false; \ + return res; \ } +#define RETURN_INV_ON_FAIL(x) RETURN_ON_FAIL(x, kInvalidStream) + H264BitstreamParser::H264BitstreamParser() {} H264BitstreamParser::~H264BitstreamParser() {} -bool H264BitstreamParser::ParseNonParameterSetNalu(const uint8_t* source, - size_t source_length, - uint8_t nalu_type) { - RTC_CHECK(sps_); - RTC_CHECK(pps_); +H264BitstreamParser::Result H264BitstreamParser::ParseNonParameterSetNalu( + const uint8_t* source, + size_t source_length, + uint8_t nalu_type) { + if (!sps_ || !pps_) + return kInvalidStream; + last_slice_qp_delta_ = rtc::Optional(); std::unique_ptr slice_rbsp( H264::ParseRbsp(source, source_length)); + if (slice_rbsp->size() < H264::kNaluTypeSize) + return kInvalidStream; + rtc::BitBuffer slice_reader(slice_rbsp->data() + H264::kNaluTypeSize, slice_rbsp->size() - H264::kNaluTypeSize); // Check to see if this is an IDR slice, which has an extra field to parse @@ -48,64 +55,64 @@ bool H264BitstreamParser::ParseNonParameterSetNalu(const uint8_t* source, uint32_t bits_tmp; // first_mb_in_slice: ue(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); // slice_type: ue(v) uint32_t slice_type; - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&slice_type)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&slice_type)); // slice_type's 5..9 range is used to indicate that all slices of a picture // have the same value of slice_type % 5, we don't care about that, so we map // to the corresponding 0..4 range. slice_type %= 5; // pic_parameter_set_id: ue(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); if (sps_->separate_colour_plane_flag == 1) { // colour_plane_id - RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 2)); + RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 2)); } // frame_num: u(v) // Represented by log2_max_frame_num_minus4 + 4 bits. - RETURN_FALSE_ON_FAIL( + RETURN_INV_ON_FAIL( slice_reader.ReadBits(&bits_tmp, sps_->log2_max_frame_num_minus4 + 4)); uint32_t field_pic_flag = 0; if (sps_->frame_mbs_only_flag == 0) { // field_pic_flag: u(1) - RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(&field_pic_flag, 1)); + RETURN_INV_ON_FAIL(slice_reader.ReadBits(&field_pic_flag, 1)); if (field_pic_flag != 0) { // bottom_field_flag: u(1) - RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1)); + RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1)); } } if (is_idr) { // idr_pic_id: ue(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } // pic_order_cnt_lsb: u(v) // Represented by sps_.log2_max_pic_order_cnt_lsb_minus4 + 4 bits. if (sps_->pic_order_cnt_type == 0) { - RETURN_FALSE_ON_FAIL(slice_reader.ReadBits( + RETURN_INV_ON_FAIL(slice_reader.ReadBits( &bits_tmp, sps_->log2_max_pic_order_cnt_lsb_minus4 + 4)); if (pps_->bottom_field_pic_order_in_frame_present_flag && field_pic_flag == 0) { // delta_pic_order_cnt_bottom: se(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } } if (sps_->pic_order_cnt_type == 1 && !sps_->delta_pic_order_always_zero_flag) { // delta_pic_order_cnt[0]: se(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); if (pps_->bottom_field_pic_order_in_frame_present_flag && !field_pic_flag) { // delta_pic_order_cnt[1]: se(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } } if (pps_->redundant_pic_cnt_present_flag) { // redundant_pic_cnt: ue(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } if (slice_type == H264::SliceType::kB) { // direct_spatial_mv_pred_flag: u(1) - RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1)); + RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1)); } switch (slice_type) { case H264::SliceType::kP: @@ -113,14 +120,14 @@ bool H264BitstreamParser::ParseNonParameterSetNalu(const uint8_t* source, case H264::SliceType::kSp: uint32_t num_ref_idx_active_override_flag; // num_ref_idx_active_override_flag: u(1) - RETURN_FALSE_ON_FAIL( + RETURN_INV_ON_FAIL( slice_reader.ReadBits(&num_ref_idx_active_override_flag, 1)); if (num_ref_idx_active_override_flag != 0) { // num_ref_idx_l0_active_minus1: ue(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); if (slice_type == H264::SliceType::kB) { // num_ref_idx_l1_active_minus1: ue(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } } break; @@ -128,8 +135,10 @@ bool H264BitstreamParser::ParseNonParameterSetNalu(const uint8_t* source, break; } // assume nal_unit_type != 20 && nal_unit_type != 21: - RTC_CHECK_NE(nalu_type, 20); - RTC_CHECK_NE(nalu_type, 21); + if (nalu_type == 20 || nalu_type == 21) { + LOG(LS_ERROR) << "Unsupported nal unit type."; + return kUnsupportedStream; + } // if (nal_unit_type == 20 || nal_unit_type == 21) // ref_pic_list_mvc_modification() // else @@ -141,23 +150,21 @@ bool H264BitstreamParser::ParseNonParameterSetNalu(const uint8_t* source, if (slice_type % 5 != 2 && slice_type % 5 != 4) { // ref_pic_list_modification_flag_l0: u(1) uint32_t ref_pic_list_modification_flag_l0; - RETURN_FALSE_ON_FAIL( + RETURN_INV_ON_FAIL( slice_reader.ReadBits(&ref_pic_list_modification_flag_l0, 1)); if (ref_pic_list_modification_flag_l0) { uint32_t modification_of_pic_nums_idc; do { // modification_of_pic_nums_idc: ue(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb( + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb( &modification_of_pic_nums_idc)); if (modification_of_pic_nums_idc == 0 || modification_of_pic_nums_idc == 1) { // abs_diff_pic_num_minus1: ue(v) - RETURN_FALSE_ON_FAIL( - slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } else if (modification_of_pic_nums_idc == 2) { // long_term_pic_num: ue(v) - RETURN_FALSE_ON_FAIL( - slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } } while (modification_of_pic_nums_idc != 3); } @@ -165,34 +172,33 @@ bool H264BitstreamParser::ParseNonParameterSetNalu(const uint8_t* source, if (slice_type % 5 == 1) { // ref_pic_list_modification_flag_l1: u(1) uint32_t ref_pic_list_modification_flag_l1; - RETURN_FALSE_ON_FAIL( + RETURN_INV_ON_FAIL( slice_reader.ReadBits(&ref_pic_list_modification_flag_l1, 1)); if (ref_pic_list_modification_flag_l1) { uint32_t modification_of_pic_nums_idc; do { // modification_of_pic_nums_idc: ue(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb( + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb( &modification_of_pic_nums_idc)); if (modification_of_pic_nums_idc == 0 || modification_of_pic_nums_idc == 1) { // abs_diff_pic_num_minus1: ue(v) - RETURN_FALSE_ON_FAIL( - slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } else if (modification_of_pic_nums_idc == 2) { // long_term_pic_num: ue(v) - RETURN_FALSE_ON_FAIL( - slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } } while (modification_of_pic_nums_idc != 3); } } } // TODO(pbos): Do we need support for pred_weight_table()? - RTC_CHECK( - !((pps_->weighted_pred_flag && (slice_type == H264::SliceType::kP || - slice_type == H264::SliceType::kSp)) || - (pps_->weighted_bipred_idc != 0 && slice_type == H264::SliceType::kB))) - << "Missing support for pred_weight_table()."; + if ((pps_->weighted_pred_flag && (slice_type == H264::SliceType::kP || + slice_type == H264::SliceType::kSp)) || + (pps_->weighted_bipred_idc == 1 && slice_type == H264::SliceType::kB)) { + LOG(LS_ERROR) << "Streams with pred_weight_table unsupported."; + return kUnsupportedStream; + } // if ((weighted_pred_flag && (slice_type == P || slice_type == SP)) || // (weighted_bipred_idc == 1 && slice_type == B)) { // pred_weight_table() @@ -202,39 +208,35 @@ bool H264BitstreamParser::ParseNonParameterSetNalu(const uint8_t* source, if (is_idr) { // no_output_of_prior_pics_flag: u(1) // long_term_reference_flag: u(1) - RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 2)); + RETURN_INV_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 2)); } else { // adaptive_ref_pic_marking_mode_flag: u(1) uint32_t adaptive_ref_pic_marking_mode_flag; - RETURN_FALSE_ON_FAIL( + RETURN_INV_ON_FAIL( slice_reader.ReadBits(&adaptive_ref_pic_marking_mode_flag, 1)); if (adaptive_ref_pic_marking_mode_flag) { uint32_t memory_management_control_operation; do { // memory_management_control_operation: ue(v) - RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb( + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb( &memory_management_control_operation)); if (memory_management_control_operation == 1 || memory_management_control_operation == 3) { // difference_of_pic_nums_minus1: ue(v) - RETURN_FALSE_ON_FAIL( - slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } if (memory_management_control_operation == 2) { // long_term_pic_num: ue(v) - RETURN_FALSE_ON_FAIL( - slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } if (memory_management_control_operation == 3 || memory_management_control_operation == 6) { // long_term_frame_idx: ue(v) - RETURN_FALSE_ON_FAIL( - slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } if (memory_management_control_operation == 4) { // max_long_term_frame_idx_plus1: ue(v) - RETURN_FALSE_ON_FAIL( - slice_reader.ReadExponentialGolomb(&golomb_tmp)); + RETURN_INV_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp)); } } while (memory_management_control_operation != 0); } @@ -244,10 +246,10 @@ bool H264BitstreamParser::ParseNonParameterSetNalu(const uint8_t* source, // if (entropy_coding_mode_flag && slice_type != I && slice_type != SI) // cabac_init_idc int32_t last_slice_qp_delta; - RETURN_FALSE_ON_FAIL( + RETURN_INV_ON_FAIL( slice_reader.ReadSignedExponentialGolomb(&last_slice_qp_delta)); last_slice_qp_delta_ = rtc::Optional(last_slice_qp_delta); - return true; + return kOk; } void H264BitstreamParser::ParseSlice(const uint8_t* slice, size_t length) { @@ -257,19 +259,20 @@ void H264BitstreamParser::ParseSlice(const uint8_t* slice, size_t length) { sps_ = SpsParser::ParseSps(slice + H264::kNaluTypeSize, length - H264::kNaluTypeSize); if (!sps_) - FATAL() << "Unable to parse SPS from H264 bitstream."; + LOG(LS_WARNING) << "Unable to parse SPS from H264 bitstream."; break; } case H264::NaluType::kPps: { pps_ = PpsParser::ParsePps(slice + H264::kNaluTypeSize, length - H264::kNaluTypeSize); if (!pps_) - FATAL() << "Unable to parse PPS from H264 bitstream."; + LOG(LS_WARNING) << "Unable to parse PPS from H264 bitstream."; break; } default: - RTC_CHECK(ParseNonParameterSetNalu(slice, length, nalu_type)) - << "Failed to parse picture slice."; + Result res = ParseNonParameterSetNalu(slice, length, nalu_type); + if (res != kOk) + LOG(LS_INFO) << "Failed to parse bitstream. Error: " << res; break; } } @@ -278,7 +281,6 @@ void H264BitstreamParser::ParseBitstream(const uint8_t* bitstream, size_t length) { std::vector nalu_indices = H264::FindNaluIndices(bitstream, length); - RTC_CHECK(!nalu_indices.empty()); for (const H264::NaluIndex& index : nalu_indices) ParseSlice(&bitstream[index.payload_start_offset], index.payload_size); } diff --git a/webrtc/common_video/h264/h264_bitstream_parser.h b/webrtc/common_video/h264/h264_bitstream_parser.h index 0b84e40234..aa0155239a 100644 --- a/webrtc/common_video/h264/h264_bitstream_parser.h +++ b/webrtc/common_video/h264/h264_bitstream_parser.h @@ -31,6 +31,12 @@ namespace webrtc { // bitstreams. class H264BitstreamParser { public: + enum Result { + kOk, + kInvalidStream, + kUnsupportedStream, + }; + H264BitstreamParser(); virtual ~H264BitstreamParser(); @@ -42,9 +48,9 @@ class H264BitstreamParser { protected: void ParseSlice(const uint8_t* slice, size_t length); - bool ParseNonParameterSetNalu(const uint8_t* source, - size_t source_length, - uint8_t nalu_type); + Result ParseNonParameterSetNalu(const uint8_t* source, + size_t source_length, + uint8_t nalu_type); // SPS/PPS state, updated when parsing new SPS/PPS, used to parse slices. rtc::Optional sps_; diff --git a/webrtc/common_video/h264/h264_common.cc b/webrtc/common_video/h264/h264_common.cc index fe55b02d2c..a9cc6a25e6 100644 --- a/webrtc/common_video/h264/h264_common.cc +++ b/webrtc/common_video/h264/h264_common.cc @@ -24,6 +24,7 @@ std::vector FindNaluIndices(const uint8_t* buffer, std::vector sequences; if (buffer_size < kNaluShortStartSequenceSize) return sequences; + const size_t end = buffer_size - kNaluShortStartSequenceSize; for (size_t i = 0; i < end;) { if (buffer[i + 2] > 1) {