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}
This commit is contained in:
kthelgason 2016-11-03 17:30:34 -07:00 committed by Commit bot
parent e2213ce62c
commit f752bca4a5
3 changed files with 73 additions and 64 deletions

View File

@ -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<int32_t>();
std::unique_ptr<rtc::Buffer> 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<int32_t>(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<H264::NaluIndex> 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);
}

View File

@ -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<SpsParser::SpsState> sps_;

View File

@ -24,6 +24,7 @@ std::vector<NaluIndex> FindNaluIndices(const uint8_t* buffer,
std::vector<NaluIndex> 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) {