Commit message: fixed #1565
CVE: CVE-2020-23930
CWE: CWE-476
Vulnerability description: An issue was discovered in gpac through 20200801. A NULL pointer dereference exists in the function nhmldump_send_header located in write_nhml.c. It allows an attacker to cause Denial of Service.

Project: gpac

static void nhmldump_send_frame(GF_NHMLDumpCtx *ctx, char *data, u32 data_size, GF_FilterPacket *pck)
{
	GF_FilterPacket *dst_pck;
	char nhml[1024];
	const GF_PropertyValue *p;
	u32 size;
	u8 *output;
	GF_FilterSAPType sap = gf_filter_pck_get_sap(pck);
	u64 dts = gf_filter_pck_get_dts(pck);
	u64 cts = gf_filter_pck_get_cts(pck);

	if (dts==GF_FILTER_NO_TS) dts = cts;
	if (cts==GF_FILTER_NO_TS) cts = dts;

	ctx->pck_num++;
	sprintf(nhml, "<NHNTSample number=\"%d\" DTS=\""LLU"\" dataLength=\"%d\" ", ctx->pck_num, dts, data_size);
	gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
	if (ctx->pckp || (cts != dts) ) {
		sprintf(nhml, "CTSOffset=\"%d\" ", (s32) ((s64)cts - (s64)dts));
		gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
	}
	if (sap==GF_FILTER_SAP_1) {
		sprintf(nhml, "isRAP=\"yes\" ");
		gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
	} else if (sap) {
		sprintf(nhml, "SAPType=\"%d\" ", sap);
		gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
	} else if (ctx->pckp) {
		sprintf(nhml, "isRAP=\"no\" ");
		gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
		if ((sap==GF_FILTER_SAP_4) || (sap==GF_FILTER_SAP_4_PROL)) {
			s32 roll = gf_filter_pck_get_roll_info(pck);
			sprintf(nhml, "SAPType=\"4\" %s=\"%d\" ", (sap==GF_FILTER_SAP_4_PROL) ? "prol" : "roll", roll);
			gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
		}
	}

	if (ctx->pckp) {
		u64 bo;
		u32 duration, idx;
		sprintf(nhml, "mediaOffset=\""LLU"\" ", ctx->mdia_pos);
		gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));

		bo = gf_filter_pck_get_byte_offset(pck);
		if (bo!=GF_FILTER_NO_BO) {
			sprintf(nhml, "sourceByteOffset=\""LLU"\" ", bo);
			gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
		}
		duration = gf_filter_pck_get_duration(pck);
		if (duration) {
			sprintf(nhml, "duration=\"%d\" ", duration);
			gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
		}
		idx = gf_filter_pck_get_carousel_version(pck);
		if (idx) {
			sprintf(nhml, "carouselVersion=\"%d\" ", idx);
			gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
		}
		idx = 0;
		while (1) {
			u32 prop_4cc;
			const char *prop_name;
			p = gf_filter_pck_enum_properties(pck, &idx, &prop_4cc, &prop_name);
			if (!p) break;
			if (prop_4cc == GF_PROP_PCK_SUBS) continue;
			nhmldump_pck_property(ctx, prop_4cc, prop_name, p);
		}
	}

	if (ctx->chksum) {
		if (ctx->chksum==1) {
			u32 crc = gf_crc_32(data, data_size);
			sprintf(nhml, "crc=\"%08X\" ", crc);
			gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
		} else {
			u32 j;
			u8 hash[GF_SHA1_DIGEST_SIZE];
			gf_sha1_csum(data, data_size, hash);
			sprintf(nhml, "sha1=\"");
			gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
			for (j=0; j<20; j++) {
				sprintf(nhml, "%02X", hash[j]);
				gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
			}
			sprintf(nhml, "\" ");
			gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
		}
	}

	sprintf(nhml, ">\n");
	gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));

	p = gf_filter_pck_get_property(pck, GF_PROP_PCK_SUBS);
	if (p) {
		u32 offset_in_sample = 0;
		Bool first_subs = GF_TRUE;
		if (!ctx->bs_r) ctx->bs_r = gf_bs_new(p->value.data.ptr, p->value.data.size, GF_BITSTREAM_READ);
		else gf_bs_reassign_buffer(ctx->bs_r, p->value.data.ptr, p->value.data.size);

		//(data) binary blob containing N [(u32)flags(u32)size(u32)reserved(u8)priority(u8) discardable]
		while (gf_bs_available(ctx->bs_r)) {
			u32 s_flags = gf_bs_read_u32(ctx->bs_r);
			u32 s_size = gf_bs_read_u32(ctx->bs_r);
			u32 s_res = gf_bs_read_u32(ctx->bs_r);
			u8 s_prio = gf_bs_read_u8(ctx->bs_r);
			u8 s_discard = gf_bs_read_u8(ctx->bs_r);


			if (offset_in_sample + s_size > data_size) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong subsample info: sample size %d vs subsample offset+size %dn", data_size, offset_in_sample + s_size));
				break;
			}

			if (ctx->is_stpp && ctx->nhmlonly) {
				if (first_subs) {
					sprintf(nhml, "<NHNTSubSample>\n");
					gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));

					gf_bs_write_data(ctx->bs_w, data, s_size);

					sprintf(nhml, "</NHNTSubSample>\n");
					gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
				} else {
					u32 d_size;
					if (ctx->b64_buffer_size<2*s_size) {
						ctx->b64_buffer_size = 2 * s_size;
						ctx->b64_buffer = gf_realloc(ctx->b64_buffer, ctx->b64_buffer_size);
					}
					d_size = gf_base64_encode(data + offset_in_sample, s_size, ctx->b64_buffer, ctx->b64_buffer_size);
					ctx->b64_buffer[d_size] = 0;
					sprintf(nhml, "<NHNTSubSample data=\"data:application/octet-string;base64,");
					gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
					gf_bs_write_data(ctx->bs_w, ctx->b64_buffer, d_size);
					sprintf(nhml, "\">\n");
					gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
				}
			} else {
				sprintf(nhml, "<NHNTSubSample size=\"%d\" flags=\"%d\" reserved=\"%d\" priority=\"%d\" discard=\"%d\" />\n", s_size, s_flags, s_res, s_prio, s_discard);
				gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
			}
			first_subs = GF_FALSE;
		}
	} else if (ctx->is_stpp && ctx->nhmlonly) {
		sprintf(nhml, "<NHNTSubSample><![CDATA[\n");
		gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
		gf_bs_write_data(ctx->bs_w, data, data_size);
		sprintf(nhml, "]]></NHNTSubSample>\n");
		gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));
	}
	sprintf(nhml, "</NHNTSample>\n");
	gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));

	gf_bs_get_content_no_truncate(ctx->bs_w, &ctx->nhml_buffer, &size, &ctx->nhml_buffer_size);

	if (ctx->filep) {
		gf_fwrite(ctx->nhml_buffer, size, ctx->filep);
		return;
	}

	dst_pck = gf_filter_pck_new_alloc(ctx->opid_nhml, size, &output);
	memcpy(output, ctx->nhml_buffer, size);
	gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_FALSE);
	gf_filter_pck_send(dst_pck);

	ctx->mdia_pos += data_size;

	if (ctx->opid_mdia) {
		//send data packet
		dst_pck = gf_filter_pck_new_ref(ctx->opid_mdia, data, data_size, pck);
		gf_filter_pck_merge_properties(pck, dst_pck);
		//keep byte offset ?
//		gf_filter_pck_set_byte_offset(dst_pck, GF_FILTER_NO_BO);

		gf_filter_pck_set_framing(dst_pck, ctx->first, GF_FALSE);
		gf_filter_pck_send(dst_pck);
	}
}

Project: gpac

void PrintHintUsage()
{
	u32 i=0;
	gf_sys_format_help(helpout, help_flags, "# Hinting Options\n"
		"IsoMedia hinting consists in creating special tracks in the file that contain transport protocol specific information and optionally multiplexing information. These tracks are then used by the server to create the actual packets being sent over the network, in other words they provide the server with hints on how to build packets, hence their names `hint tracks`.\n"
		"MP4Box supports creation of hint tracks for RTSP servers supporting these such as QuickTime Streaming Server, DarwinStreaming Server or 3GPP-compliant RTSP servers.\n"
		"Note: GPAC streaming tools [rtp output](rtpout) and [rtsp server](rtspout) do not use hint tracks, they use on-the-fly packetization "
		"from any media sources, not just MP4\n"
		"  \n"
		"Options:\n"
	);
	while (m4b_hint_args[i].name) {
		GF_GPACArg *arg = &m4b_hint_args[i];
		i++;
		gf_sys_print_arg(helpout, help_flags, arg, "mp4box-hint");
	}
}

Project: gpac
File: main.c

void SetupClockReferences(GF_ISOFile *file)
{
	u32 i, count, ocr_id;
	count = gf_isom_get_track_count(file);
	if (count==1) return;
	ocr_id = 0;
	for (i=0; i<count; i++) {
		if (!gf_isom_is_track_in_root_od(file, i+1)) continue;
		ocr_id = gf_isom_get_track_id(file, i+1);
		break;
	}
	/*doesn't look like MP4*/
	if (!ocr_id) return;
	for (i=0; i<count; i++) {
		GF_ESD *esd = gf_isom_get_esd(file, i+1, 1);
		if (esd) {
			esd->OCRESID = ocr_id;
			gf_isom_change_mpeg4_description(file, i+1, 1, esd);
			gf_odf_desc_del((GF_Descriptor *) esd);
		}
	}
}

Project: gpac

GF_Err nhmldump_process(GF_Filter *filter)
{
	GF_NHMLDumpCtx *ctx = gf_filter_get_udta(filter);
	GF_FilterPacket *pck;
	char *data;
	u32 pck_size;

	if (!ctx->side_streams_config) {
		return nhmldump_config_side_stream(filter, ctx);
	}

	pck = gf_filter_pid_get_packet(ctx->ipid);
	if (!pck) {
		if (gf_filter_pid_is_eos(ctx->ipid)) {
			if (ctx->bs_w && ctx->szRootName) {
				char nhml[1024];
				u32 size;
				gf_bs_reassign_buffer(ctx->bs_w, ctx->nhml_buffer, ctx->nhml_buffer_size);
				sprintf(nhml, "</%s>\n", ctx->szRootName);
				gf_bs_write_data(ctx->bs_w, nhml, (u32) strlen(nhml));

				gf_bs_get_content_no_truncate(ctx->bs_w, &ctx->nhml_buffer, &size, &ctx->nhml_buffer_size);

				if (ctx->filep) {
					gf_fwrite(ctx->nhml_buffer, size, ctx->filep);
				} else {
					GF_FilterPacket *dst_pck;
					u8 *output;
					dst_pck = gf_filter_pck_new_alloc(ctx->opid_nhml, size, &output);
					memcpy(output, ctx->nhml_buffer, size);
					gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_TRUE);
					gf_filter_pck_send(dst_pck);
				}
				ctx->szRootName = NULL;
			}
			if (ctx->opid_nhml) gf_filter_pid_set_eos(ctx->opid_nhml);
			if (ctx->opid_mdia) gf_filter_pid_set_eos(ctx->opid_mdia);
			if (ctx->opid_info) gf_filter_pid_set_eos(ctx->opid_info);
			return GF_EOS;
		}
		return GF_OK;
	}

	if (!ctx->bs_w) ctx->bs_w = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
	else gf_bs_reassign_buffer(ctx->bs_w, ctx->nhml_buffer, ctx->nhml_buffer_size);

	if (ctx->first) {
		nhmldump_send_header(ctx);
		gf_bs_reassign_buffer(ctx->bs_w, ctx->nhml_buffer, ctx->nhml_buffer_size);
	}

	//get media data
	data = (char *) gf_filter_pck_get_data(pck, &pck_size);

	//send data
	if (ctx->is_dims) {
		nhmldump_send_dims(ctx, data, pck_size, pck);
	} else {
		nhmldump_send_frame(ctx, data, pck_size, pck);
	}
	ctx->first = GF_FALSE;


	if (ctx->exporter) {
		u32 timescale = gf_filter_pck_get_timescale(pck);
		u64 ts = gf_filter_pck_get_cts(pck);
		gf_set_progress("Exporting", ts*ctx->duration.den, ctx->duration.num*timescale);
	}

	gf_filter_pid_drop_packet(ctx->ipid);

	return GF_OK;
}