Commit message: fixed #1780 (fuzz)
CVE: CVE-2021-33362
CWE: CWE-787
Vulnerability description: Stack buffer overflow in the hevc_parse_vps_extension function in MP4Box in GPAC 1.0.1 allows attackers to cause a denial of service or execute arbitrary code via a crafted file.

Project: gpac
File: av_parsers.c

s32 gf_mpegh_get_mhas_pl(u8 *ptr, u32 size, u64 *ch_layout)
{
	s32 PL = -1;
	GF_BitStream *bs;
	u32 i;
	s32 sync_pos=-1;
	for (i=0; i<size-3; i++) {
		if ((ptr[i]==0xC0) && (ptr[i+1]== 0x01) && (ptr[i+2]==0xA5)) {
			sync_pos = i;
			break;
		}
	}
	if (sync_pos<0) return 0;
	if (ch_layout) *ch_layout = 0;
	bs = gf_bs_new(ptr, size, GF_BITSTREAM_READ);
	gf_bs_skip_bytes(bs, sync_pos);

	while (gf_bs_available(bs)) {
		u32 type = (u32) gf_mpegh_escaped_value(bs, 3, 8, 8);
		/*u64 label = */gf_mpegh_escaped_value(bs, 2, 8, 32);
		u64 mh_size = gf_mpegh_escaped_value(bs, 11, 24, 24);
		if (mh_size > gf_bs_available(bs))
			break;
		//MHAS config
		if (type==1) {
			PL = gf_bs_read_int(bs, 8);
			if (ch_layout) {
				u32 idx = gf_bs_read_int(bs, 5);
				if (idx==0x1f)
					gf_bs_read_int(bs, 24);
				/*idx = */gf_bs_read_int(bs, 3);
				gf_bs_read_int(bs, 1);
				gf_bs_read_int(bs, 1);

				//speaker config
				idx = gf_bs_read_int(bs, 2);
				if (idx == 0) {
					*ch_layout = gf_audio_fmt_get_layout_from_cicp( gf_bs_read_int(bs, 6) );
				}
			}
			break;
		}
		gf_bs_skip_bytes(bs, mh_size);
	}
	gf_bs_del(bs);
	return PL;
}

Project: gpac
File: av_parsers.c

GF_Err gf_av1_parse_obu(GF_BitStream *bs, ObuType *obu_type, u64 *obu_size, u32 *obu_hdr_size, AV1State *state)
{
	GF_Err e = GF_OK;
	u32 hdr_size;
	u64 pos = gf_bs_get_position(bs);

	if (!bs || !obu_type || !state)
		return GF_BAD_PARAM;

	state->bs_overread = GF_FALSE;
	gf_bs_set_eos_callback(bs, on_aom_av1_eos, state);

	state->obu_extension_flag = state->obu_has_size_field = 0;
	state->temporal_id = state->spatial_id = 0;
	state->frame_state.uncompressed_header_bytes = 0;
	e = gf_av1_parse_obu_header(bs, obu_type, &state->obu_extension_flag, &state->obu_has_size_field, &state->temporal_id, &state->spatial_id);
	if (e)
		return e;

	if (state->obu_has_size_field) {
		*obu_size = (u32)gf_av1_leb128_read(bs, NULL);
	}
	else {
		if (*obu_size >= 1 + state->obu_extension_flag) {
			*obu_size = *obu_size - 1 - state->obu_extension_flag;
		}
		else {
			GF_LOG(state->config ? GF_LOG_WARNING : GF_LOG_DEBUG, GF_LOG_CODING, ("[AV1] computed OBU size "LLD" (input value = "LLU"). Skipping.\n", *obu_size - 1 - state->obu_extension_flag, *obu_size));
			return GF_NON_COMPLIANT_BITSTREAM;
		}
	}
	hdr_size = (u32)(gf_bs_get_position(bs) - pos);
	if ((gf_bs_available(bs) < *obu_size) || state->bs_overread) {
		gf_bs_seek(bs, pos);
		return GF_BUFFER_TOO_SMALL;
	}
	*obu_size += hdr_size;
	if (obu_hdr_size) *obu_hdr_size = hdr_size;


	if (*obu_type != OBU_SEQUENCE_HEADER && *obu_type != OBU_TEMPORAL_DELIMITER &&
		state->OperatingPointIdc != 0 && state->obu_extension_flag == 1)
	{
		u32 inTemporalLayer = (state->OperatingPointIdc >> state->temporal_id) & 1;
		u32 inSpatialLayer = (state->OperatingPointIdc >> (state->spatial_id + 8)) & 1;
		if (!inTemporalLayer || !inSpatialLayer) {
			*obu_type = -1;
			gf_bs_seek(bs, pos + *obu_size);
			return GF_OK;
		}
	}

	e = GF_OK;
	switch (*obu_type) {
	case OBU_SEQUENCE_HEADER:
		av1_parse_sequence_header_obu(bs, state);
		if (gf_bs_get_position(bs) > pos + *obu_size) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[AV1] Sequence header parsing consumed too many bytes !\n"));
			e = GF_NON_COMPLIANT_BITSTREAM;
		}
		gf_bs_seek(bs, pos + *obu_size);
		break;

	case OBU_METADATA:
#if 0
		//TODO + sample groups
		const ObuMetadataType metadata_type = (u32)read_leb128(bs, NULL); we should check for 16 bits limit(AV1MetadataSampleGroupEntry) for ISOBMFF bindings, see https ://github.com/AOMediaCodec/av1-isobmff/pull/86#issuecomment-416659538
		if (metadata_type == OBU_METADATA_TYPE_ITUT_T35) {
		}
		else if (metadata_type == OBU_METADATA_TYPE_HDR_CLL) {
		}
		else if (metadata_type == OBU_METADATA_TYPE_HDR_MDCV) {
		}
		else if (metadata_type == OBU_METADATA_TYPE_SCALABILITY) {
		}
		else if (metadata_type == METADATA_TYPE_TIMECODE) {
		}
#endif
		GF_LOG(GF_LOG_INFO, GF_LOG_CODING, ("[AV1] parsing for metadata is not implemented. Forwarding.\n"));

		if (gf_bs_get_position(bs) > pos + *obu_size) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[AV1] Metadata parsing consumed too many bytes !\n"));
			e = GF_NON_COMPLIANT_BITSTREAM;
		}
		gf_bs_seek(bs, pos + *obu_size);
		break;

	case OBU_FRAME_HEADER:
	case OBU_REDUNDANT_FRAME_HEADER:
		if (state->config) {
			av1_parse_frame_header(bs, state);
			if (gf_bs_get_position(bs) > pos + *obu_size) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[AV1] Frame header parsing consumed too many bytes !\n"));
				e = GF_NON_COMPLIANT_BITSTREAM;
			}
		}
		gf_bs_seek(bs, pos + *obu_size);
		break;
	case OBU_FRAME:
		e = av1_parse_frame(bs, state, pos, *obu_size);
		if (gf_bs_get_position(bs) != pos + *obu_size) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[AV1] Frame parsing did not consume the right number of bytes !\n"));
			e = GF_NON_COMPLIANT_BITSTREAM;
		}
		gf_bs_seek(bs, pos + *obu_size);
		break;
	case OBU_TILE_GROUP:
		if (state->config) {
			e = av1_parse_tile_group(bs, state, pos, *obu_size);
			if (gf_bs_get_position(bs) != pos + *obu_size) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[AV1] Tile group parsing did not consume the right number of bytes !\n"));
				e = GF_NON_COMPLIANT_BITSTREAM;
			}
		}
		gf_bs_seek(bs, pos + *obu_size);
		break;
	case OBU_TEMPORAL_DELIMITER:
		state->frame_state.seen_frame_header = GF_FALSE;
	case OBU_PADDING:
		gf_bs_seek(bs, pos + *obu_size);
		break;
	default:
		GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[AV1] unknown OBU type %u (size "LLU"). Skipping.\n", *obu_type, *obu_size));
		gf_bs_seek(bs, pos + *obu_size);
		break;
	}
	return e;
}

Project: gpac
File: av_parsers.c

static void vvc_compute_poc(VVCSliceInfo *si)
{
	u32 max_poc_lsb = 1 << (si->sps->log2_max_poc_lsb);

	/*POC reset for IDR frames, NOT for CRA*/
	if (si->irap_or_gdr_pic && !si->gdr_pic) {
		si->poc_lsb_prev = 0;
		si->poc_msb_prev = 0;
	}

	if (si->poc_msb_cycle_present_flag) {
		si->poc_msb = si->poc_msb_cycle;
	} else {
		if ((si->poc_lsb < si->poc_lsb_prev) && (si->poc_lsb_prev - si->poc_lsb >= max_poc_lsb / 2))
			si->poc_msb = si->poc_msb_prev + max_poc_lsb;
		else if ((si->poc_lsb > si->poc_lsb_prev) && (si->poc_lsb - si->poc_lsb_prev > max_poc_lsb / 2))
			si->poc_msb = si->poc_msb_prev - max_poc_lsb;
		else
			si->poc_msb = si->poc_msb_prev;
	}

	si->poc = si->poc_msb + si->poc_lsb;
}

Project: gpac
File: av_parsers.c

static GF_Err gf_m4v_parse_frame_mpeg12(GF_M4VParser *m4v, GF_M4VDecSpecInfo *dsi, u8 *frame_type, u32 *time_inc, u64 *size, u64 *start, Bool *is_coded)
{
	u8 go, hasVOP, firstObj, val;
	s32 o_type;

	if (!m4v || !size || !start || !frame_type) return GF_BAD_PARAM;

	*size = 0;
	firstObj = 1;
	hasVOP = 0;
	*is_coded = GF_FALSE;
	m4v->current_object_type = (u32)-1;
	*frame_type = 0;

	if (!m4v->step_mode)
		M4V_Reset(m4v, m4v->current_object_start);

	go = 1;
	while (go) {
		o_type = M4V_LoadObject(m4v);
		switch (o_type) {
		case M2V_PIC_START_CODE:
			/*done*/
			if (hasVOP) {
				go = 0;
				break;
			}
			if (firstObj) {
				*start = m4v->current_object_start;
				firstObj = 0;
			}
			hasVOP = 1;
			*is_coded = 1;

			/*val = */gf_bs_read_u8(m4v->bs);
			val = gf_bs_read_u8(m4v->bs);
			*frame_type = ((val >> 3) & 0x7) - 1;
			break;
		case M2V_GOP_START_CODE:
			if (firstObj) {
				*start = m4v->current_object_start;
				firstObj = 0;
			}
			if (hasVOP) go = 0;
			break;

		case M2V_SEQ_START_CODE:
			if (firstObj) {
				*start = m4v->current_object_start;
				firstObj = 0;
			}
			if (hasVOP) {
				go = 0;
				break;
			}

			/**/
			break;

		default:
			break;

		case -1:
			*size = gf_bs_get_position(m4v->bs) - *start;
			return GF_EOS;
		}
		if (m4v->step_mode)
			return GF_OK;
	}
	*size = m4v->current_object_start - *start;
	return GF_OK;
}

Project: gpac
File: av_parsers.c

s32 gf_media_vvc_parse_nalu_bs(GF_BitStream *bs, VVCState *vvc, u8 *nal_unit_type, u8 *temporal_id, u8 *layer_id)
{
	Bool is_slice = GF_FALSE;
	s32 ret = -1;
	VVCSliceInfo n_state;

	gf_bs_enable_emulation_byte_removal(bs, GF_TRUE);

	memcpy(&n_state, &vvc->s_info, sizeof(VVCSliceInfo));
	if (!vvc_parse_nal_header(bs, nal_unit_type, temporal_id, layer_id)) return -1;

	n_state.nal_unit_type = *nal_unit_type;

	switch (n_state.nal_unit_type) {
	case GF_VVC_NALU_ACCESS_UNIT:
	case GF_VVC_NALU_END_OF_SEQ:
	case GF_VVC_NALU_END_OF_STREAM:
		ret = 1;
		break;

	case GF_VVC_NALU_SLICE_TRAIL:
	case GF_VVC_NALU_SLICE_STSA:
	case GF_VVC_NALU_SLICE_RADL:
	case GF_VVC_NALU_SLICE_RASL:
	case GF_VVC_NALU_SLICE_IDR_W_RADL:
	case GF_VVC_NALU_SLICE_IDR_N_LP:
	case GF_VVC_NALU_SLICE_CRA:
	case GF_VVC_NALU_SLICE_GDR:
		/* slice - read the info and compare.*/
		ret = vvc_parse_slice(bs, vvc, &n_state);
		if (ret < 0) return ret;

		ret = 0;
		if (n_state.picture_header_in_slice_header_flag) {
			is_slice = GF_TRUE;
			vvc_compute_poc(&n_state);
			if (vvc->s_info.poc != n_state.poc) {
				ret = 1;
				break;
			}
			if (!(*layer_id) || (n_state.prev_layer_id_plus1 && ((*layer_id) <= n_state.prev_layer_id_plus1 - 1))) {
				ret = 1;
				break;
			}
		}
		break;

	case GF_VVC_NALU_PIC_HEADER:
		if (vvc_parse_picture_header(bs, vvc, &n_state)<0) {
			ret = -1;
			break;
		}
		is_slice = GF_TRUE;
		vvc_compute_poc(&n_state);
		if (!(*layer_id) || (n_state.prev_layer_id_plus1 && ((*layer_id) <= n_state.prev_layer_id_plus1 - 1))) {
			ret = 1;
		}
		break;
	case GF_VVC_NALU_SEQ_PARAM:
		vvc->last_parsed_sps_id = gf_media_vvc_read_sps_bs_internal(bs, vvc, *layer_id, NULL);
		ret = (vvc->last_parsed_sps_id>=0) ? 0 : -1;
		break;
	case GF_VVC_NALU_PIC_PARAM:
		vvc->last_parsed_pps_id = gf_media_vvc_read_pps_bs_internal(bs, vvc);
		ret = (vvc->last_parsed_pps_id>=0) ? 0 : -1;
		break;
	case GF_VVC_NALU_VID_PARAM:
		vvc->last_parsed_vps_id = gf_media_vvc_read_vps_bs_internal(bs, vvc, GF_FALSE);
		ret = (vvc->last_parsed_vps_id>=0) ? 0 : -1;
		break;
	case GF_VVC_NALU_DEC_PARAM:
		ret = 0;
		break;
	case GF_VVC_NALU_APS_PREFIX:
		//we use the mix aps type + aps id (first 8 bits) as unique identifier
		vvc->last_parsed_aps_id = gf_bs_read_int_log(bs, 8, "aps_id");
		ret = 0;
		break;
	default:
		ret = 0;
		break;
	}

	/* save _prev values */
	if ((ret>0) && vvc->s_info.sps) {
//		n_state.frame_num_offset_prev = vvc->s_info.frame_num_offset;
//		n_state.frame_num_prev = vvc->s_info.frame_num;

		n_state.poc_lsb_prev = vvc->s_info.poc_lsb;
		n_state.poc_msb_prev = vvc->s_info.poc_msb;
		if (is_slice)
			n_state.prev_layer_id_plus1 = *layer_id + 1;
	}
	if (is_slice) vvc_compute_poc(&n_state);
	memcpy(&vvc->s_info, &n_state, sizeof(VVCSliceInfo));

	return ret;
}

Project: gpac
File: av_parsers.c

static s32 gf_media_vvc_read_vps_bs_internal(GF_BitStream *bs, VVCState *vvc, Bool stop_at_vps_ext)
{
	u32 i, j;
	s32 vps_id;
	VVC_VPS *vps;
	Bool vps_default_ptl_dpb_hrd_max_tid_flag=0;

	//nalu header already parsed
	vps_id = gf_bs_read_int_log(bs, 4, "vps_id");
	if ((vps_id<0) || (vps_id >= 16)) return -1;
	if (!vps_id) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[VVC] VPS ID 0 is forbidden\n"));
		return -1;
	}
	vps = &vvc->vps[vps_id];
	if (!vps->state) {
		vps->id = vps_id;
		vps->state = 1;
	}
	vps->max_layers = 1 + gf_bs_read_int_log(bs, 6, "max_layers");
	if (vps->max_layers > MAX_LHVC_LAYERS) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[VVC] sorry, %d layers in VPS but only %d supported\n", vps->max_layers, MAX_LHVC_LAYERS));
		return -1;
	}
	vps->max_sub_layers = gf_bs_read_int_log(bs, 3, "max_sub_layers_minus1") + 1;

	if ((vps->max_layers>1) && (vps->max_sub_layers>1))
		vps_default_ptl_dpb_hrd_max_tid_flag = gf_bs_read_int_log(bs, 1, "vps_default_ptl_dpb_hrd_max_tid_flag");

	if (vps->max_layers>1)
		vps->all_layers_independent = gf_bs_read_int_log(bs, 1, "all_layers_independent");

	for (i=0; i<vps->max_layers; i++) {
		u32 layer_id = gf_bs_read_int_log_idx(bs, 6, "layer_id", i);
		if (layer_id>vps->max_layer_id) vps->max_layer_id = layer_id;
		if (i && !vps->all_layers_independent) {
			Bool layer_indep = gf_bs_read_int_log_idx(bs, 1, "layer_independent", i);
			if (!layer_indep) {
				Bool vps_max_tid_ref_present_flag = gf_bs_read_int_log_idx(bs, 1, "vps_max_tid_ref_present_flag", i);
				for (j=0; j<i; j++) {
					Bool vps_direct_ref_layer_flag = gf_bs_read_int_log_idx2(bs, 1, "vps_direct_ref_layer_flag", i, j);
					if (vps_max_tid_ref_present_flag && vps_direct_ref_layer_flag) {
						gf_bs_read_int_log_idx2(bs, 3, "vps_max_tid_il_ref_pics_plus1", i, j);
					}
				}
			}
		}
	}
	vps->num_ptl = 1;
	if (vps->max_layers > 1) {
		if (vps->all_layers_independent) {
			vps->each_layer_is_ols = gf_bs_read_int_log(bs, 1, "each_layer_is_ols");
		}
		if (!vps->each_layer_is_ols) {
			u32 vps_ols_mode_idc = 2;
			if (!vps->all_layers_independent) {
				vps_ols_mode_idc = gf_bs_read_int_log(bs, 2, "vps_ols_mode_idc");
			}
			if (vps_ols_mode_idc==2) {
				u8 vps_num_output_layer_sets = 2 + gf_bs_read_int_log(bs, 8, "vps_num_output_layer_sets_minus2");
				for (i=0; i<vps_num_output_layer_sets; i++) {
					for (j=0; j<vps->max_layers; j++) {
						gf_bs_read_int_log_idx2(bs, 1, "vps_ols_output_layer_flag", i, j);
					}
				}
			}
		}
		vps->num_ptl = 1 + gf_bs_read_int_log(bs, 8, "num_ptl_minus1");
	}
	vps->ptl[0].pt_present = 1;
	for (i=0; i<vps->num_ptl; i++) {
		if (i)
			vps->ptl[i].pt_present = gf_bs_read_int_log_idx(bs, 1, "pt_present", i);
		if (!vps_default_ptl_dpb_hrd_max_tid_flag)
			vps->ptl[i].ptl_max_tid = gf_bs_read_int_log_idx(bs, 3, "ptl_max_tid", i);
		else
			vps->ptl[i].ptl_max_tid = vps->max_sub_layers - 1;;
	}
	//align
	gf_bs_align(bs);

	for (i=0; i<vps->num_ptl; i++) {
		vvc_profile_tier_level(bs, &vps->ptl[i], i);
	}

	//TODO, parse multilayer stuff
	return vps_id;
}