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:
parent
e2213ce62c
commit
f752bca4a5
@ -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);
|
||||
}
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user