static GF_Err gf_media_export_filters(GF_MediaExporter *dumper)
{
	char *args, szSubArgs[1024], szExt[30];
	GF_Filter *file_out, *reframer, *remux=NULL, *src_filter;
	GF_FilterSession *fsess;
	GF_Err e = GF_OK;
	u32 codec_id=0;
	u32 sample_count=0;
	Bool skip_write_filter = GF_FALSE;
	Bool ext_forced = GF_FALSE;
	Bool use_dynext = GF_FALSE;

	args = NULL;
	strcpy(szExt, "");
	if (dumper->trackID && dumper->file) {
		u32 msubtype = 0;
		u32 mtype = 0;
		u32 afmt = 0;
		GF_PixelFormat pfmt = 0;
		GF_ESD *esd;
		const char *export_ext = dumper->out_name ? gf_file_ext_start(dumper->out_name) : NULL;
		u32 track_num = gf_isom_get_track_by_id(dumper->file, dumper->trackID);
		if (!track_num) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] No tracks with ID %d in file\n", dumper->trackID));
			return GF_BAD_PARAM;
		}
		esd = gf_media_map_esd(dumper->file, track_num, 0);
		sample_count = gf_isom_get_sample_count(dumper->file, dumper->trackID);
		if (esd) {
			if (esd->decoderConfig->objectTypeIndication<GF_CODECID_LAST_MPEG4_MAPPING) {
				codec_id = gf_codecid_from_oti(esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication);
#ifndef GPAC_DISABLE_AV_PARSERS
				if (esd->decoderConfig->decoderSpecificInfo && (codec_id==GF_CODECID_AAC_MPEG4)) {
					GF_M4ADecSpecInfo acfg;
					gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &acfg);
					if (acfg.base_object_type == GF_M4A_USAC)
						codec_id = GF_CODECID_USAC;
				}
#endif
			} else {
				codec_id = esd->decoderConfig->objectTypeIndication;
			}
		}
		if (!codec_id) {
			msubtype = gf_isom_get_media_subtype(dumper->file, track_num, 1);
			codec_id = gf_codec_id_from_isobmf(msubtype);
		}
		mtype = gf_isom_get_media_type(dumper->file, track_num);
		if (!codec_id) {
			pfmt = gf_pixel_fmt_from_qt_type(msubtype);
			if (pfmt) codec_id = GF_CODECID_RAW;
		}

		if (!codec_id) {
			strcpy(szExt, gf_4cc_to_str(msubtype));
			ext_forced = GF_TRUE;
		} else if (codec_id==GF_CODECID_RAW) {
			switch (mtype) {
			case GF_ISOM_MEDIA_VISUAL:
			case GF_ISOM_MEDIA_AUXV:
			case GF_ISOM_MEDIA_PICT:
				if (pfmt)
					strcpy(szExt, gf_pixel_fmt_sname(pfmt));
				break;
			case GF_ISOM_MEDIA_AUDIO:
				afmt = gf_audio_fmt_from_isobmf(msubtype);
				if (afmt)
					strcpy(szExt, gf_audio_fmt_name(afmt));
				break;
			default:
				strcpy(szExt, gf_4cc_to_str(msubtype));
				break;
			}
		} else {
			const char *sname = gf_codecid_file_ext(codec_id);
			if (export_ext && strstr(sname, export_ext+1)) {
				szExt[0]=0;
			} else {
				char *sep;
				strncpy(szExt, sname, 29);
				szExt[29]=0;
				sep = strchr(szExt, '|');
				if (sep) sep[0] = 0;
			}
		}
		switch (mtype) {
		case GF_ISOM_MEDIA_VISUAL:
		case GF_ISOM_MEDIA_AUXV:
		case GF_ISOM_MEDIA_PICT:
		case GF_ISOM_MEDIA_AUDIO:
			skip_write_filter = codec_id ? GF_TRUE : GF_FALSE;
			break;
		default:
			switch (codec_id) {
			case GF_CODECID_WEBVTT:
				skip_write_filter = GF_TRUE;
				break;
			case GF_CODECID_META_TEXT:
			case GF_CODECID_META_XML:
			case GF_CODECID_SUBS_TEXT:
			case GF_CODECID_SUBS_XML:
			case GF_CODECID_SIMPLE_TEXT:
				//use dynamic extension
				szExt[0] = 0;
				use_dynext = GF_TRUE;
				break;
			}
			break;
		}
		//TODO, move these two to filters one of these days
		if ((codec_id==GF_CODECID_VORBIS) || (codec_id==GF_CODECID_THEORA) || (codec_id==GF_CODECID_OPUS)) {
			char *outname = dumper->out_name;
			if (outname && !strcmp(outname, "std")) outname=NULL;
			if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
#ifndef GPAC_DISABLE_AV_PARSERS
			return gf_dump_to_ogg(dumper, outname, track_num);
#else
			return GF_NOT_SUPPORTED;
#endif

		}
		if (codec_id==GF_CODECID_SUBPIC) {
#ifndef GPAC_DISABLE_AV_PARSERS
			char *dsi = NULL;
			u32 dsi_size = 0;
			if (esd && esd->decoderConfig && esd->decoderConfig->decoderSpecificInfo) {
				dsi = esd->decoderConfig->decoderSpecificInfo->data;
				dsi_size = esd->decoderConfig->decoderSpecificInfo->dataLength;
			}
			e = gf_dump_to_vobsub(dumper, dumper->out_name, track_num, dsi, dsi_size);
#else
			e = GF_NOT_SUPPORTED;
#endif
			if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
			return e;
		}
		if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
	} else {
		const char *export_ext = dumper->out_name ? gf_file_ext_start(dumper->out_name) : NULL;
		skip_write_filter = GF_TRUE;
		if (!export_ext)
			use_dynext = GF_TRUE;
	}

	fsess = gf_fs_new_defaults(0);
	if (!fsess) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Failed to create filter session\n"));
		return GF_OUT_OF_MEM;
	}
	file_out = NULL;
	args = NULL;

	if (dumper->flags & GF_EXPORT_REMUX) {
		file_out = gf_fs_load_destination(fsess, dumper->out_name, NULL, NULL, &e);
		if (!file_out) {
			gf_fs_del(fsess);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot open destination %s\n", dumper->out_name));
			return e;
		}
	}
	//except in nhml inband file dump, create a sink filter
	else if (!dumper->dump_file) {
		Bool no_ext = (dumper->flags & GF_EXPORT_NO_FILE_EXT) ? GF_TRUE : GF_FALSE;
		char *ext = gf_file_ext_start(dumper->out_name);
		//mux args, for now we only dump to file
		e = gf_dynstrcat(&args, "fout:dst=", NULL);
		e |= gf_dynstrcat(&args, dumper->out_name, NULL);

		if (dumper->flags & GF_EXPORT_NHNT) {
			strcpy(szExt, "nhnt");
			e |= gf_dynstrcat(&args, ":clone", NULL);
			no_ext = GF_TRUE;
			if (!ext)
				e |= gf_dynstrcat(&args, ":dynext", NULL);
		} else if (dumper->flags & GF_EXPORT_NHML) {
			strcpy(szExt, "nhml");
			e |= gf_dynstrcat(&args, ":clone", NULL);
			no_ext = GF_TRUE;
			if (!ext)
				e |= gf_dynstrcat(&args, ":dynext", NULL);
		}

		if (dumper->flags & GF_EXPORT_RAW_SAMPLES) {
			if (!dumper->sample_num) {

				ext = gf_file_ext_start(args);
				if (ext) ext[0] = 0;
				if (sample_count>=1000) {
					e |= gf_dynstrcat(&args, "_$num%08d$", NULL);
				} else if (sample_count) {
					e |= gf_dynstrcat(&args, "_$num%03d$", NULL);
				} else {
					e |= gf_dynstrcat(&args, "_$num$", NULL);
				}
				ext = gf_file_ext_start(dumper->out_name);
				if (ext) e |= gf_dynstrcat(&args, ext, NULL);
			}
			e |= gf_dynstrcat(&args, ":dynext", NULL);
		} else if (dumper->trackID && strlen(szExt) ) {
			if (!no_ext && !gf_file_ext_start(dumper->out_name)) {
				if (args) gf_free(args);
				args=NULL;
				e = gf_dynstrcat(&args, "fout:dst=", NULL);
				e |= gf_dynstrcat(&args, dumper->out_name, NULL);
				e |= gf_dynstrcat(&args, szExt, ".");
			} else {
				e |= gf_dynstrcat(&args, ":ext=", NULL);
				e |= gf_dynstrcat(&args, szExt, NULL);
			}
		} else if ((dumper->trackID || dumper->track_type) && use_dynext) {
			e |= gf_dynstrcat(&args, ":dynext", NULL);
		}
		if (e) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load arguments for output file dumper\n"));
			if (args) gf_free(args);
			gf_fs_del(fsess);
			return e;
		}

		file_out = gf_fs_load_filter(fsess, args, &e);
		if (!file_out) {
			gf_fs_del(fsess);
			if (args) gf_free(args);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load output file dumper\n"));
			return e;
		}
	}
	if (args) gf_free(args);
	args = NULL;

	//raw sample frame, force loading filter generic write in frame mode
	if (dumper->flags & GF_EXPORT_RAW_SAMPLES) {
		e = gf_dynstrcat(&args, "writegen:frame", NULL);
		if (dumper->sample_num) {
			sprintf(szSubArgs, ":sstart=%d:send=%d", dumper->sample_num, dumper->sample_num);
			e |= gf_dynstrcat(&args, szSubArgs, NULL);
		}
		remux = e ? NULL : gf_fs_load_filter(fsess, args, &e);
		if (!remux || e) {
			gf_fs_del(fsess);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load stream->file filter\n"));
			if (args) gf_free(args);
			return e ? e : GF_FILTER_NOT_FOUND;
		}
	}
	else if (dumper->flags & GF_EXPORT_NHNT) {
		remux = gf_fs_load_filter(fsess, "nhntw:exporter", &e);
		if (!remux) {
			gf_fs_del(fsess);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load NHNT write filter\n"));
			return e;
		}
	}
	else if (dumper->flags & GF_EXPORT_NHML) {
		e = gf_dynstrcat(&args, "nhmlw:exporter:name=", NULL);
		e |= gf_dynstrcat(&args, dumper->out_name, NULL);
		if (dumper->flags & GF_EXPORT_NHML_FULL)
			e |= gf_dynstrcat(&args, ":pckp", NULL);
		if (dumper->dump_file) {
			sprintf(szSubArgs, ":nhmlonly:filep=%p", dumper->dump_file);
			e |= gf_dynstrcat(&args, szSubArgs, NULL);
		}
		remux = e ? NULL : gf_fs_load_filter(fsess, args, &e);
		if (!remux || e) {
			gf_fs_del(fsess);
			if (args) gf_free(args);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load NHML write filter\n"));
			return e ? e : GF_FILTER_NOT_FOUND;
		}
	} else if (!skip_write_filter) {
		e = gf_dynstrcat(&args, "writegen:exporter", NULL);
		//extension has been forced, override ext at output of writegen
		if (ext_forced) {
			e |= gf_dynstrcat(&args, ":#Extension=", NULL);
			e |= gf_dynstrcat(&args, szExt, NULL);
		}

		remux = e ? NULL : gf_fs_load_filter(fsess, args, &e);
		if (!remux) {
			gf_fs_del(fsess);
			if (args) gf_free(args);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load stream->file filter\n"));
			return e;
		}
	}
	if (args) gf_free(args);
	args = NULL;

	//force a reframer filter, connected to our input
	e = gf_dynstrcat(&args, "reframer:SID=1", NULL);
	if (dumper->trackID) {
		sprintf(szSubArgs, "#PID=%d", dumper->trackID);
		e |= gf_dynstrcat(&args, szSubArgs, NULL);
	}
	e |= gf_dynstrcat(&args, ":exporter", NULL);
	if (dumper->flags & GF_EXPORT_SVC_LAYER)
		e |= gf_dynstrcat(&args, ":extract=layer", NULL);
	if (dumper->flags & GF_EXPORT_WEBVTT_NOMERGE)
		e |= gf_dynstrcat(&args, ":merge", NULL);

	reframer = gf_fs_load_filter(fsess, args, &e);
	if (!reframer || e) {
		gf_fs_del(fsess);
		if (args) gf_free(args);
		GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load reframer filter\n"));
		return e ? e : GF_FILTER_NOT_FOUND;
	}
	if (args) gf_free(args);
	args = NULL;

	//we already have the file loaded, directly load the mp4dmx filter with this file
	if (dumper->file) {
		//we want to expose every track
		e = gf_dynstrcat(&args, "mp4dmx:FID=1:noedit:alltk:allt", NULL);
		if (!e) {
			sprintf(szSubArgs, ":mov=%p", dumper->file);
			e = gf_dynstrcat(&args, szSubArgs, NULL);
		}

		//we want to expose every track
		src_filter = gf_fs_load_filter(fsess, args, &e);

		gf_free(args);
		args = NULL;
	} else {
		//we want to expose every track
		src_filter = gf_fs_load_source(fsess, dumper->in_name, "FID=1:noedit:alltk:allt", NULL, &e);
	}

	if (!src_filter || e) {
		gf_fs_del(fsess);
		GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load filter for input file \"%s\": %s\n", dumper->in_name, gf_error_to_string(e) ));
		return e;
	}

	if (dumper->track_type) {
		const char *mtype = (dumper->track_type==1) ? "video" : "audio";
		if (dumper->trackID) {
			sprintf(szSubArgs, "%s%d", mtype, dumper->trackID);
		} else {
			sprintf(szSubArgs, "%s", mtype);
		}
	}
	else if (dumper->trackID) {
		sprintf(szSubArgs, "PID=%d", dumper->trackID);
	}
	if (remux) {
		gf_filter_set_source(file_out, remux, (dumper->trackID || dumper->track_type) ? szSubArgs : NULL);
		gf_filter_set_source(remux, reframer, (dumper->trackID || dumper->track_type) ? szSubArgs : NULL);
	} else {
		gf_filter_set_source(file_out, reframer, (dumper->trackID || dumper->track_type) ? szSubArgs : NULL);
	}

	e = gf_fs_run(fsess);
	if (e>GF_OK) e = GF_OK;
	if (!e) e = gf_fs_get_last_connect_error(fsess);
	if (!e) e = gf_fs_get_last_process_error(fsess);

	if (!e) {
		if (dumper->file)
			gf_fs_print_unused_args(fsess, NULL);
		else
			gf_fs_print_unused_args(fsess, "alltk,allt,noedit");
	}
	gf_fs_print_non_connected(fsess);
	if (dumper->print_stats_graph & 1) gf_fs_print_stats(fsess);
	if (dumper->print_stats_graph & 2) gf_fs_print_connections(fsess);
	gf_fs_del(fsess);
	return e;
}

The gf_media_export_filters function in GPAC 1.0.1 allows attackers to cause a denial of service (NULL pointer dereference) via a crafted file in the MP4Box command.


static GF_Err gf_media_export_filters(GF_MediaExporter *dumper)
{
	char *args, szSubArgs[1024], szExt[30];
	GF_Filter *file_out, *reframer, *remux=NULL, *src_filter;
	GF_FilterSession *fsess;
	GF_Err e = GF_OK;
	u32 codec_id=0;
	u32 sample_count=0;
	Bool skip_write_filter = GF_FALSE;
	Bool ext_forced = GF_FALSE;
	Bool use_dynext = GF_FALSE;

	args = NULL;
	strcpy(szExt, "");
	if (dumper->trackID && dumper->file) {
		u32 msubtype = 0;
		u32 mtype = 0;
		u32 afmt = 0;
		GF_PixelFormat pfmt = 0;
		GF_ESD *esd;
		const char *export_ext = dumper->out_name ? gf_file_ext_start(dumper->out_name) : NULL;
		u32 track_num = gf_isom_get_track_by_id(dumper->file, dumper->trackID);
		if (!track_num) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] No tracks with ID %d in file\n", dumper->trackID));
			return GF_BAD_PARAM;
		}
		esd = gf_media_map_esd(dumper->file, track_num, 0);
		sample_count = gf_isom_get_sample_count(dumper->file, dumper->trackID);
		if (esd && esd->decoderConfig) {
			if (esd->decoderConfig->objectTypeIndication<GF_CODECID_LAST_MPEG4_MAPPING) {
				codec_id = gf_codecid_from_oti(esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication);
#ifndef GPAC_DISABLE_AV_PARSERS
				if (esd->decoderConfig->decoderSpecificInfo && (codec_id==GF_CODECID_AAC_MPEG4)) {
					GF_M4ADecSpecInfo acfg;
					gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &acfg);
					if (acfg.base_object_type == GF_M4A_USAC)
						codec_id = GF_CODECID_USAC;
				}
#endif
			} else {
				codec_id = esd->decoderConfig->objectTypeIndication;
			}
		}
		if (!codec_id) {
			msubtype = gf_isom_get_media_subtype(dumper->file, track_num, 1);
			codec_id = gf_codec_id_from_isobmf(msubtype);
		}
		mtype = gf_isom_get_media_type(dumper->file, track_num);
		if (!codec_id) {
			pfmt = gf_pixel_fmt_from_qt_type(msubtype);
			if (pfmt) codec_id = GF_CODECID_RAW;
		}

		if (!codec_id) {
			strcpy(szExt, gf_4cc_to_str(msubtype));
			ext_forced = GF_TRUE;
		} else if (codec_id==GF_CODECID_RAW) {
			switch (mtype) {
			case GF_ISOM_MEDIA_VISUAL:
			case GF_ISOM_MEDIA_AUXV:
			case GF_ISOM_MEDIA_PICT:
				if (pfmt)
					strcpy(szExt, gf_pixel_fmt_sname(pfmt));
				break;
			case GF_ISOM_MEDIA_AUDIO:
				afmt = gf_audio_fmt_from_isobmf(msubtype);
				if (afmt)
					strcpy(szExt, gf_audio_fmt_name(afmt));
				break;
			default:
				strcpy(szExt, gf_4cc_to_str(msubtype));
				break;
			}
		} else {
			const char *sname = gf_codecid_file_ext(codec_id);
			if (export_ext && strstr(sname, export_ext+1)) {
				szExt[0]=0;
			} else {
				char *sep;
				strncpy(szExt, sname, 29);
				szExt[29]=0;
				sep = strchr(szExt, '|');
				if (sep) sep[0] = 0;
			}
		}
		switch (mtype) {
		case GF_ISOM_MEDIA_VISUAL:
		case GF_ISOM_MEDIA_AUXV:
		case GF_ISOM_MEDIA_PICT:
		case GF_ISOM_MEDIA_AUDIO:
			skip_write_filter = codec_id ? GF_TRUE : GF_FALSE;
			break;
		default:
			switch (codec_id) {
			case GF_CODECID_WEBVTT:
				skip_write_filter = GF_TRUE;
				break;
			case GF_CODECID_META_TEXT:
			case GF_CODECID_META_XML:
			case GF_CODECID_SUBS_TEXT:
			case GF_CODECID_SUBS_XML:
			case GF_CODECID_SIMPLE_TEXT:
				//use dynamic extension
				szExt[0] = 0;
				use_dynext = GF_TRUE;
				break;
			}
			break;
		}
		//TODO, move these two to filters one of these days
		if ((codec_id==GF_CODECID_VORBIS) || (codec_id==GF_CODECID_THEORA) || (codec_id==GF_CODECID_OPUS)) {
			char *outname = dumper->out_name;
			if (outname && !strcmp(outname, "std")) outname=NULL;
			if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
#ifndef GPAC_DISABLE_AV_PARSERS
			return gf_dump_to_ogg(dumper, outname, track_num);
#else
			return GF_NOT_SUPPORTED;
#endif

		}
		if (codec_id==GF_CODECID_SUBPIC) {
#ifndef GPAC_DISABLE_AV_PARSERS
			char *dsi = NULL;
			u32 dsi_size = 0;
			if (esd && esd->decoderConfig && esd->decoderConfig->decoderSpecificInfo) {
				dsi = esd->decoderConfig->decoderSpecificInfo->data;
				dsi_size = esd->decoderConfig->decoderSpecificInfo->dataLength;
			}
			e = gf_dump_to_vobsub(dumper, dumper->out_name, track_num, dsi, dsi_size);
#else
			e = GF_NOT_SUPPORTED;
#endif
			if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
			return e;
		}
		if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
	} else {
		const char *export_ext = dumper->out_name ? gf_file_ext_start(dumper->out_name) : NULL;
		skip_write_filter = GF_TRUE;
		if (!export_ext)
			use_dynext = GF_TRUE;
	}

	fsess = gf_fs_new_defaults(0);
	if (!fsess) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Failed to create filter session\n"));
		return GF_OUT_OF_MEM;
	}
	file_out = NULL;
	args = NULL;

	if (dumper->flags & GF_EXPORT_REMUX) {
		file_out = gf_fs_load_destination(fsess, dumper->out_name, NULL, NULL, &e);
		if (!file_out) {
			gf_fs_del(fsess);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot open destination %s\n", dumper->out_name));
			return e;
		}
	}
	//except in nhml inband file dump, create a sink filter
	else if (!dumper->dump_file) {
		Bool no_ext = (dumper->flags & GF_EXPORT_NO_FILE_EXT) ? GF_TRUE : GF_FALSE;
		char *ext = gf_file_ext_start(dumper->out_name);
		//mux args, for now we only dump to file
		e = gf_dynstrcat(&args, "fout:dst=", NULL);
		e |= gf_dynstrcat(&args, dumper->out_name, NULL);

		if (dumper->flags & GF_EXPORT_NHNT) {
			strcpy(szExt, "nhnt");
			e |= gf_dynstrcat(&args, ":clone", NULL);
			no_ext = GF_TRUE;
			if (!ext)
				e |= gf_dynstrcat(&args, ":dynext", NULL);
		} else if (dumper->flags & GF_EXPORT_NHML) {
			strcpy(szExt, "nhml");
			e |= gf_dynstrcat(&args, ":clone", NULL);
			no_ext = GF_TRUE;
			if (!ext)
				e |= gf_dynstrcat(&args, ":dynext", NULL);
		}

		if (dumper->flags & GF_EXPORT_RAW_SAMPLES) {
			if (!dumper->sample_num) {

				ext = gf_file_ext_start(args);
				if (ext) ext[0] = 0;
				if (sample_count>=1000) {
					e |= gf_dynstrcat(&args, "_$num%08d$", NULL);
				} else if (sample_count) {
					e |= gf_dynstrcat(&args, "_$num%03d$", NULL);
				} else {
					e |= gf_dynstrcat(&args, "_$num$", NULL);
				}
				ext = gf_file_ext_start(dumper->out_name);
				if (ext) e |= gf_dynstrcat(&args, ext, NULL);
			}
			e |= gf_dynstrcat(&args, ":dynext", NULL);
		} else if (dumper->trackID && strlen(szExt) ) {
			if (!no_ext && !gf_file_ext_start(dumper->out_name)) {
				if (args) gf_free(args);
				args=NULL;
				e = gf_dynstrcat(&args, "fout:dst=", NULL);
				e |= gf_dynstrcat(&args, dumper->out_name, NULL);
				e |= gf_dynstrcat(&args, szExt, ".");
			} else {
				e |= gf_dynstrcat(&args, ":ext=", NULL);
				e |= gf_dynstrcat(&args, szExt, NULL);
			}
		} else if ((dumper->trackID || dumper->track_type) && use_dynext) {
			e |= gf_dynstrcat(&args, ":dynext", NULL);
		}
		if (e) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load arguments for output file dumper\n"));
			if (args) gf_free(args);
			gf_fs_del(fsess);
			return e;
		}

		file_out = gf_fs_load_filter(fsess, args, &e);
		if (!file_out) {
			gf_fs_del(fsess);
			if (args) gf_free(args);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load output file dumper\n"));
			return e;
		}
	}
	if (args) gf_free(args);
	args = NULL;

	//raw sample frame, force loading filter generic write in frame mode
	if (dumper->flags & GF_EXPORT_RAW_SAMPLES) {
		e = gf_dynstrcat(&args, "writegen:frame", NULL);
		if (dumper->sample_num) {
			sprintf(szSubArgs, ":sstart=%d:send=%d", dumper->sample_num, dumper->sample_num);
			e |= gf_dynstrcat(&args, szSubArgs, NULL);
		}
		remux = e ? NULL : gf_fs_load_filter(fsess, args, &e);
		if (!remux || e) {
			gf_fs_del(fsess);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load stream->file filter\n"));
			if (args) gf_free(args);
			return e ? e : GF_FILTER_NOT_FOUND;
		}
	}
	else if (dumper->flags & GF_EXPORT_NHNT) {
		remux = gf_fs_load_filter(fsess, "nhntw:exporter", &e);
		if (!remux) {
			gf_fs_del(fsess);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load NHNT write filter\n"));
			return e;
		}
	}
	else if (dumper->flags & GF_EXPORT_NHML) {
		e = gf_dynstrcat(&args, "nhmlw:exporter:name=", NULL);
		e |= gf_dynstrcat(&args, dumper->out_name, NULL);
		if (dumper->flags & GF_EXPORT_NHML_FULL)
			e |= gf_dynstrcat(&args, ":pckp", NULL);
		if (dumper->dump_file) {
			sprintf(szSubArgs, ":nhmlonly:filep=%p", dumper->dump_file);
			e |= gf_dynstrcat(&args, szSubArgs, NULL);
		}
		remux = e ? NULL : gf_fs_load_filter(fsess, args, &e);
		if (!remux || e) {
			gf_fs_del(fsess);
			if (args) gf_free(args);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load NHML write filter\n"));
			return e ? e : GF_FILTER_NOT_FOUND;
		}
	} else if (!skip_write_filter) {
		e = gf_dynstrcat(&args, "writegen:exporter", NULL);
		//extension has been forced, override ext at output of writegen
		if (ext_forced) {
			e |= gf_dynstrcat(&args, ":#Extension=", NULL);
			e |= gf_dynstrcat(&args, szExt, NULL);
		}

		remux = e ? NULL : gf_fs_load_filter(fsess, args, &e);
		if (!remux) {
			gf_fs_del(fsess);
			if (args) gf_free(args);
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load stream->file filter\n"));
			return e;
		}
	}
	if (args) gf_free(args);
	args = NULL;

	//force a reframer filter, connected to our input
	e = gf_dynstrcat(&args, "reframer:SID=1", NULL);
	if (dumper->trackID) {
		sprintf(szSubArgs, "#PID=%d", dumper->trackID);
		e |= gf_dynstrcat(&args, szSubArgs, NULL);
	}
	e |= gf_dynstrcat(&args, ":exporter", NULL);
	if (dumper->flags & GF_EXPORT_SVC_LAYER)
		e |= gf_dynstrcat(&args, ":extract=layer", NULL);
	if (dumper->flags & GF_EXPORT_WEBVTT_NOMERGE)
		e |= gf_dynstrcat(&args, ":merge", NULL);

	reframer = gf_fs_load_filter(fsess, args, &e);
	if (!reframer || e) {
		gf_fs_del(fsess);
		if (args) gf_free(args);
		GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load reframer filter\n"));
		return e ? e : GF_FILTER_NOT_FOUND;
	}
	if (args) gf_free(args);
	args = NULL;

	//we already have the file loaded, directly load the mp4dmx filter with this file
	if (dumper->file) {
		//we want to expose every track
		e = gf_dynstrcat(&args, "mp4dmx:FID=1:noedit:alltk:allt", NULL);
		if (!e) {
			sprintf(szSubArgs, ":mov=%p", dumper->file);
			e = gf_dynstrcat(&args, szSubArgs, NULL);
		}

		//we want to expose every track
		src_filter = gf_fs_load_filter(fsess, args, &e);

		gf_free(args);
		args = NULL;
	} else {
		//we want to expose every track
		src_filter = gf_fs_load_source(fsess, dumper->in_name, "FID=1:noedit:alltk:allt", NULL, &e);
	}

	if (!src_filter || e) {
		gf_fs_del(fsess);
		GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load filter for input file \"%s\": %s\n", dumper->in_name, gf_error_to_string(e) ));
		return e;
	}

	if (dumper->track_type) {
		const char *mtype = (dumper->track_type==1) ? "video" : "audio";
		if (dumper->trackID) {
			sprintf(szSubArgs, "%s%d", mtype, dumper->trackID);
		} else {
			sprintf(szSubArgs, "%s", mtype);
		}
	}
	else if (dumper->trackID) {
		sprintf(szSubArgs, "PID=%d", dumper->trackID);
	}
	if (remux) {
		gf_filter_set_source(file_out, remux, (dumper->trackID || dumper->track_type) ? szSubArgs : NULL);
		gf_filter_set_source(remux, reframer, (dumper->trackID || dumper->track_type) ? szSubArgs : NULL);
	} else {
		gf_filter_set_source(file_out, reframer, (dumper->trackID || dumper->track_type) ? szSubArgs : NULL);
	}

	e = gf_fs_run(fsess);
	if (e>GF_OK) e = GF_OK;
	if (!e) e = gf_fs_get_last_connect_error(fsess);
	if (!e) e = gf_fs_get_last_process_error(fsess);

	if (!e) {
		if (dumper->file)
			gf_fs_print_unused_args(fsess, NULL);
		else
			gf_fs_print_unused_args(fsess, "alltk,allt,noedit");
	}
	gf_fs_print_non_connected(fsess);
	if (dumper->print_stats_graph & 1) gf_fs_print_stats(fsess);
	if (dumper->print_stats_graph & 2) gf_fs_print_connections(fsess);
	gf_fs_del(fsess);
	return e;
}


Copy the silence flags when sending update notifications.

This fixes a crash bug in the silence module on some versions of GCC.

	SilenceMessage(const std::string& mask, const std::string& flags)
		: ClientProtocol::Message("SILENCE")
	{
		PushParam(mask);
		PushParamRef(flags);
	}

An issue was discovered in InspIRCd 3 before 3.1.0. The silence module contains a use after free vulnerability. This vulnerability can be used for remote crashing of an InspIRCd server by any user able to fully connect to a server.


	SilenceMessage(const std::string& mask, const std::string& flags)
		: ClientProtocol::Message("SILENCE")
	{
		PushParam(mask);
		PushParam(flags);
	}


static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status)
{
	u32 info_length, pos, desc_len, evt_type, nb_es,i;
	u32 nb_sections;
	u32 data_size;
	u32 nb_hevc, nb_hevc_temp, nb_shvc, nb_shvc_temp, nb_mhvc, nb_mhvc_temp;
	unsigned char *data;
	GF_M2TS_Section *section;
	GF_Err e = GF_OK;

	/*wait for the last section */
	if (!(status&GF_M2TS_TABLE_END)) return;

	nb_es = 0;

	/*skip if already received but no update detected (eg same data) */
	if ((status&GF_M2TS_TABLE_REPEAT) && !(status&GF_M2TS_TABLE_UPDATE))  {
		if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PMT_REPEAT, pmt->program);
		return;
	}

	if (pmt->sec->demux_restarted) {
		pmt->sec->demux_restarted = 0;
		return;
	}
	GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PMT Found or updated\n"));

	nb_sections = gf_list_count(sections);
	if (nb_sections > 1) {
		GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("PMT on multiple sections not supported\n"));
	}

	section = (GF_M2TS_Section *)gf_list_get(sections, 0);
	data = section->data;
	data_size = section->data_size;

	pmt->program->pcr_pid = ((data[0] & 0x1f) << 8) | data[1];

	info_length = ((data[2]&0xf)<<8) | data[3];
	if (info_length != 0) {
		/* ...Read Descriptors ... */
		u8 tag, len;
		u32 first_loop_len = 0;
		tag = data[4];
		len = data[5];
		while (info_length > first_loop_len) {
			if (tag == GF_M2TS_MPEG4_IOD_DESCRIPTOR) {
				u32 size;
				GF_BitStream *iod_bs;
				iod_bs = gf_bs_new((char *)data+8, len-2, GF_BITSTREAM_READ);
				if (pmt->program->pmt_iod) gf_odf_desc_del((GF_Descriptor *)pmt->program->pmt_iod);
				e = gf_odf_parse_descriptor(iod_bs , (GF_Descriptor **) &pmt->program->pmt_iod, &size);
				gf_bs_del(iod_bs );
				if (e==GF_OK) {
					/*remember program number for service/program selection*/
					if (pmt->program->pmt_iod) pmt->program->pmt_iod->ServiceID = pmt->program->number;
					/*if empty IOD (freebox case), discard it and use dynamic declaration of object*/
					if (!gf_list_count(pmt->program->pmt_iod->ESDescriptors)) {
						gf_odf_desc_del((GF_Descriptor *)pmt->program->pmt_iod);
						pmt->program->pmt_iod = NULL;
					}
				}
			} else if (tag == GF_M2TS_METADATA_POINTER_DESCRIPTOR) {
				GF_BitStream *metadatapd_bs;
				GF_M2TS_MetadataPointerDescriptor *metapd;
				metadatapd_bs = gf_bs_new((char *)data+6, len, GF_BITSTREAM_READ);
				metapd = gf_m2ts_read_metadata_pointer_descriptor(metadatapd_bs, len);
				gf_bs_del(metadatapd_bs);
				if (metapd->application_format_identifier == GF_M2TS_META_ID3 &&
				        metapd->format_identifier == GF_M2TS_META_ID3 &&
				        metapd->carriage_flag == METADATA_CARRIAGE_SAME_TS) {
					/*HLS ID3 Metadata */
					pmt->program->metadata_pointer_descriptor = metapd;
				} else {
					/* don't know what to do with it for now, delete */
					gf_m2ts_metadata_pointer_descriptor_del(metapd);
				}
			} else {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Skipping descriptor (0x%x) and others not supported\n", tag));
			}
			first_loop_len += 2 + len;
		}
	}
	if (data_size <= 4 + info_length) return;
	data += 4 + info_length;
	data_size -= 4 + info_length;
	pos = 0;

	/* count de number of program related PMT received */
	for(i=0; i<gf_list_count(ts->programs); i++) {
		GF_M2TS_Program *prog = (GF_M2TS_Program *)gf_list_get(ts->programs,i);
		if(prog->pmt_pid == pmt->pid) {
			break;
		}
	}

	nb_hevc = nb_hevc_temp = nb_shvc = nb_shvc_temp = nb_mhvc = nb_mhvc_temp = 0;
	while (pos<data_size) {
		GF_M2TS_PES *pes = NULL;
		GF_M2TS_SECTION_ES *ses = NULL;
		GF_M2TS_ES *es = NULL;
		Bool inherit_pcr = 0;
		u32 pid, stream_type, reg_desc_format;

		stream_type = data[0];
		pid = ((data[1] & 0x1f) << 8) | data[2];
		desc_len = ((data[3] & 0xf) << 8) | data[4];

		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("stream_type :%d \n",stream_type));
		switch (stream_type) {

		/* PES */
		case GF_M2TS_VIDEO_MPEG1:
		case GF_M2TS_VIDEO_MPEG2:
		case GF_M2TS_VIDEO_DCII:
		case GF_M2TS_VIDEO_MPEG4:
		case GF_M2TS_SYSTEMS_MPEG4_PES:
		case GF_M2TS_VIDEO_H264:
		case GF_M2TS_VIDEO_SVC:
		case GF_M2TS_VIDEO_MVCD:
		case GF_M2TS_VIDEO_HEVC:
		case GF_M2TS_VIDEO_HEVC_MCTS:
		case GF_M2TS_VIDEO_HEVC_TEMPORAL:
		case GF_M2TS_VIDEO_SHVC:
		case GF_M2TS_VIDEO_SHVC_TEMPORAL:
		case GF_M2TS_VIDEO_MHVC:
		case GF_M2TS_VIDEO_MHVC_TEMPORAL:
			inherit_pcr = 1;
		case GF_M2TS_AUDIO_MPEG1:
		case GF_M2TS_AUDIO_MPEG2:
		case GF_M2TS_AUDIO_AAC:
		case GF_M2TS_AUDIO_LATM_AAC:
		case GF_M2TS_AUDIO_AC3:
		case GF_M2TS_AUDIO_DTS:
		case GF_M2TS_MHAS_MAIN:
		case GF_M2TS_MHAS_AUX:
		case GF_M2TS_SUBTITLE_DVB:
		case GF_M2TS_METADATA_PES:
			GF_SAFEALLOC(pes, GF_M2TS_PES);
			if (!pes) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
				return;
			}
			pes->cc = -1;
			pes->flags = GF_M2TS_ES_IS_PES;
			if (inherit_pcr)
				pes->flags |= GF_M2TS_INHERIT_PCR;
			es = (GF_M2TS_ES *)pes;
			break;
		case GF_M2TS_PRIVATE_DATA:
			GF_SAFEALLOC(pes, GF_M2TS_PES);
			if (!pes) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
				return;
			}
			pes->cc = -1;
			pes->flags = GF_M2TS_ES_IS_PES;
			es = (GF_M2TS_ES *)pes;
			break;
		/* Sections */
		case GF_M2TS_SYSTEMS_MPEG4_SECTIONS:
			GF_SAFEALLOC(ses, GF_M2TS_SECTION_ES);
			if (!ses) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
				return;
			}
			es = (GF_M2TS_ES *)ses;
			es->flags |= GF_M2TS_ES_IS_SECTION;
			/* carriage of ISO_IEC_14496 data in sections */
			if (stream_type == GF_M2TS_SYSTEMS_MPEG4_SECTIONS) {
				/*MPEG-4 sections need to be fully checked: if one section is lost, this means we lost
				one SL packet in the AU so we must wait for the complete section again*/
				ses->sec = gf_m2ts_section_filter_new(gf_m2ts_process_mpeg4section, 0);
				/*create OD container*/
				if (!pmt->program->additional_ods) {
					pmt->program->additional_ods = gf_list_new();
					ts->has_4on2 = 1;
				}
			}
			break;

		case GF_M2TS_13818_6_ANNEX_A:
		case GF_M2TS_13818_6_ANNEX_B:
		case GF_M2TS_13818_6_ANNEX_C:
		case GF_M2TS_13818_6_ANNEX_D:
		case GF_M2TS_PRIVATE_SECTION:
		case GF_M2TS_QUALITY_SEC:
		case GF_M2TS_MORE_SEC:
			GF_SAFEALLOC(ses, GF_M2TS_SECTION_ES);
			if (!ses) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
				return;
			}
			es = (GF_M2TS_ES *)ses;
			es->flags |= GF_M2TS_ES_IS_SECTION;
			es->pid = pid;
			es->service_id = pmt->program->number;
			if (stream_type == GF_M2TS_PRIVATE_SECTION) {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("AIT sections on pid %d\n", pid));
			} else if (stream_type == GF_M2TS_QUALITY_SEC) {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Quality metadata sections on pid %d\n", pid));
			} else if (stream_type == GF_M2TS_MORE_SEC) {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("MORE sections on pid %d\n", pid));
			} else {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("stream type DSM CC user private sections on pid %d \n", pid));
			}
			/* NULL means: trigger the call to on_event with DVB_GENERAL type and the raw section as payload */
			ses->sec = gf_m2ts_section_filter_new(NULL, 1);
			//ses->sec->service_id = pmt->program->number;
			break;

		case GF_M2TS_MPE_SECTIONS:
			if (! ts->prefix_present) {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("stream type MPE found : pid = %d \n", pid));
#ifdef GPAC_ENABLE_MPE
				es = gf_dvb_mpe_section_new();
				if (es->flags & GF_M2TS_ES_IS_SECTION) {
					/* NULL means: trigger the call to on_event with DVB_GENERAL type and the raw section as payload */
					((GF_M2TS_SECTION_ES*)es)->sec = gf_m2ts_section_filter_new(NULL, 1);
				}
#endif
				break;
			}

		default:
			GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Stream type (0x%x) for PID %d not supported\n", stream_type, pid ) );
			//GF_LOG(/*GF_LOG_WARNING*/GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Stream type (0x%x) for PID %d not supported\n", stream_type, pid ) );
			break;
		}

		if (es) {
			es->stream_type = (stream_type==GF_M2TS_PRIVATE_DATA) ? 0 : stream_type;
			es->program = pmt->program;
			es->pid = pid;
			es->component_tag = -1;
		}

		pos += 5;
		data += 5;

		while (desc_len) {
			u8 tag = data[0];
			u32 len = data[1];
			if (es) {
				switch (tag) {
				case GF_M2TS_ISO_639_LANGUAGE_DESCRIPTOR:
					if (pes)
						pes->lang = GF_4CC(' ', data[2], data[3], data[4]);
					break;
				case GF_M2TS_MPEG4_SL_DESCRIPTOR:
					es->mpeg4_es_id = ( (u32) data[2] & 0x1f) << 8  | data[3];
					es->flags |= GF_M2TS_ES_IS_SL;
					break;
				case GF_M2TS_REGISTRATION_DESCRIPTOR:
					reg_desc_format = GF_4CC(data[2], data[3], data[4], data[5]);
					/*cf http://www.smpte-ra.org/mpegreg/mpegreg.html*/
					switch (reg_desc_format) {
					case GF_M2TS_RA_STREAM_AC3:
						es->stream_type = GF_M2TS_AUDIO_AC3;
						break;
					case GF_M2TS_RA_STREAM_VC1:
						es->stream_type = GF_M2TS_VIDEO_VC1;
						break;
					case GF_M2TS_RA_STREAM_GPAC:
						if (len==8) {
							es->stream_type = GF_4CC(data[6], data[7], data[8], data[9]);
							es->flags |= GF_M2TS_GPAC_CODEC_ID;
							break;
						}
					default:
						GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Unknown registration descriptor %s\n", gf_4cc_to_str(reg_desc_format) ));
						break;
					}
					break;
				case GF_M2TS_DVB_EAC3_DESCRIPTOR:
					es->stream_type = GF_M2TS_AUDIO_EC3;
					break;
				case GF_M2TS_DVB_DATA_BROADCAST_ID_DESCRIPTOR:
				{
					u32 id = data[2]<<8 | data[3];
					if ((id == 0xB) && ses && !ses->sec) {
						ses->sec = gf_m2ts_section_filter_new(NULL, 1);
					}
				}
				break;
				case GF_M2TS_DVB_SUBTITLING_DESCRIPTOR:
					if (pes) {
						pes->sub.language[0] = data[2];
						pes->sub.language[1] = data[3];
						pes->sub.language[2] = data[4];
						pes->sub.type = data[5];
						pes->sub.composition_page_id = (data[6]<<8) | data[7];
						pes->sub.ancillary_page_id = (data[8]<<8) | data[9];
					}
					es->stream_type = GF_M2TS_DVB_SUBTITLE;
					break;
				case GF_M2TS_DVB_STREAM_IDENTIFIER_DESCRIPTOR:
				{
					es->component_tag = data[2];
					GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Component Tag: %d on Program %d\n", es->component_tag, es->program->number));
				}
				break;
				case GF_M2TS_DVB_TELETEXT_DESCRIPTOR:
					es->stream_type = GF_M2TS_DVB_TELETEXT;
					break;
				case GF_M2TS_DVB_VBI_DATA_DESCRIPTOR:
					es->stream_type = GF_M2TS_DVB_VBI;
					break;
				case GF_M2TS_HIERARCHY_DESCRIPTOR:
					if (pes) {
						u8 hierarchy_embedded_layer_index;
						GF_BitStream *hbs = gf_bs_new((const char *)data, data_size, GF_BITSTREAM_READ);
						/*u32 skip = */gf_bs_read_int(hbs, 16);
						/*u8 res1 = */gf_bs_read_int(hbs, 1);
						/*u8 temp_scal = */gf_bs_read_int(hbs, 1);
						/*u8 spatial_scal = */gf_bs_read_int(hbs, 1);
						/*u8 quality_scal = */gf_bs_read_int(hbs, 1);
						/*u8 hierarchy_type = */gf_bs_read_int(hbs, 4);
						/*u8 res2 = */gf_bs_read_int(hbs, 2);
						/*u8 hierarchy_layer_index = */gf_bs_read_int(hbs, 6);
						/*u8 tref_not_present = */gf_bs_read_int(hbs, 1);
						/*u8 res3 = */gf_bs_read_int(hbs, 1);
						hierarchy_embedded_layer_index = gf_bs_read_int(hbs, 6);
						/*u8 res4 = */gf_bs_read_int(hbs, 2);
						/*u8 hierarchy_channel = */gf_bs_read_int(hbs, 6);
						gf_bs_del(hbs);

						pes->depends_on_pid = 1+hierarchy_embedded_layer_index;
					}
					break;
				case GF_M2TS_METADATA_DESCRIPTOR:
				{
					GF_BitStream *metadatad_bs;
					GF_M2TS_MetadataDescriptor *metad;
					metadatad_bs = gf_bs_new((char *)data+2, len, GF_BITSTREAM_READ);
					metad = gf_m2ts_read_metadata_descriptor(metadatad_bs, len);
					gf_bs_del(metadatad_bs);
					if (metad->application_format_identifier == GF_M2TS_META_ID3 &&
					        metad->format_identifier == GF_M2TS_META_ID3) {
						/*HLS ID3 Metadata */
						if (pes) {
							pes->metadata_descriptor = metad;
							pes->stream_type = GF_M2TS_METADATA_ID3_HLS;
						}
					} else {
						/* don't know what to do with it for now, delete */
						gf_m2ts_metadata_descriptor_del(metad);
					}
				}
				break;

				default:
					GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] skipping descriptor (0x%x) not supported\n", tag));
					break;
				}
			}

			data += len+2;
			pos += len+2;
			if (desc_len < len+2) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PMT es descriptor size for PID %d\n", pid ) );
				break;
			}
			desc_len-=len+2;
		}

		if (es && !es->stream_type) {
			gf_free(es);
			es = NULL;
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Private Stream type (0x%x) for PID %d not supported\n", stream_type, pid ) );
		}

		if (!es) continue;

		if (ts->ess[pid]) {
			//this is component reuse across programs, overwrite the previously declared stream ...
			if (status & GF_M2TS_TABLE_FOUND) {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d reused across programs %d and %d, not completely supported\n", pid, ts->ess[pid]->program->number, es->program->number ) );

				//add stream to program but don't reassign the pid table until the stream is playing (>GF_M2TS_PES_FRAMING_SKIP)
				gf_list_add(pmt->program->streams, es);
				if (!(es->flags & GF_M2TS_ES_IS_SECTION) ) gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP);

				nb_es++;
				//skip assignment below
				es = NULL;
			}
			/*watchout for pmt update - FIXME this likely won't work in most cases*/
			else {

				GF_M2TS_ES *o_es = ts->ess[es->pid];

				if ((o_es->stream_type == es->stream_type)
				        && ((o_es->flags & GF_M2TS_ES_STATIC_FLAGS_MASK) == (es->flags & GF_M2TS_ES_STATIC_FLAGS_MASK))
				        && (o_es->mpeg4_es_id == es->mpeg4_es_id)
				        && ((o_es->flags & GF_M2TS_ES_IS_SECTION) || ((GF_M2TS_PES *)o_es)->lang == ((GF_M2TS_PES *)es)->lang)
				   ) {
					gf_free(es);
					es = NULL;
				} else {
					gf_m2ts_es_del(o_es, ts);
					ts->ess[es->pid] = NULL;
				}
			}
		}

		if (es) {
			ts->ess[es->pid] = es;
			gf_list_add(pmt->program->streams, es);
			if (!(es->flags & GF_M2TS_ES_IS_SECTION) ) gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP);

			nb_es++;
		}

		if (es->stream_type == GF_M2TS_VIDEO_HEVC) nb_hevc++;
		else if (es->stream_type == GF_M2TS_VIDEO_HEVC_TEMPORAL) nb_hevc_temp++;
		else if (es->stream_type == GF_M2TS_VIDEO_SHVC) nb_shvc++;
		else if (es->stream_type == GF_M2TS_VIDEO_SHVC_TEMPORAL) nb_shvc_temp++;
		else if (es->stream_type == GF_M2TS_VIDEO_MHVC) nb_mhvc++;
		else if (es->stream_type == GF_M2TS_VIDEO_MHVC_TEMPORAL) nb_mhvc_temp++;
	}

	//Table 2-139, implied hierarchy indexes
	if (nb_hevc_temp + nb_shvc + nb_shvc_temp + nb_mhvc+ nb_mhvc_temp) {
		for (i=0; i<gf_list_count(pmt->program->streams); i++) {
			GF_M2TS_PES *es = (GF_M2TS_PES *)gf_list_get(pmt->program->streams, i);
			if ( !(es->flags & GF_M2TS_ES_IS_PES)) continue;
			if (es->depends_on_pid) continue;

			switch (es->stream_type) {
			case GF_M2TS_VIDEO_HEVC_TEMPORAL:
				es->depends_on_pid = 1;
				break;
			case GF_M2TS_VIDEO_SHVC:
				if (!nb_hevc_temp) es->depends_on_pid = 1;
				else es->depends_on_pid = 2;
				break;
			case GF_M2TS_VIDEO_SHVC_TEMPORAL:
				es->depends_on_pid = 3;
				break;
			case GF_M2TS_VIDEO_MHVC:
				if (!nb_hevc_temp) es->depends_on_pid = 1;
				else es->depends_on_pid = 2;
				break;
			case GF_M2TS_VIDEO_MHVC_TEMPORAL:
				if (!nb_hevc_temp) es->depends_on_pid = 2;
				else es->depends_on_pid = 3;
				break;
			}
		}
	}

	if (nb_es) {
		u32 i;

		//translate hierarchy descriptors indexes into PIDs - check whether the PMT-index rules are the same for HEVC
		for (i=0; i<gf_list_count(pmt->program->streams); i++) {
			GF_M2TS_PES *an_es = NULL;
			GF_M2TS_PES *es = (GF_M2TS_PES *)gf_list_get(pmt->program->streams, i);
			if ( !(es->flags & GF_M2TS_ES_IS_PES)) continue;
			if (!es->depends_on_pid) continue;

			//fixeme we are not always assured that hierarchy_layer_index matches the stream index...
			//+1 is because our first stream is the PMT
			an_es =  (GF_M2TS_PES *)gf_list_get(pmt->program->streams, es->depends_on_pid);
			if (an_es) {
				es->depends_on_pid = an_es->pid;
			} else {
				GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TS] Wrong dependency index in hierarchy descriptor, assuming non-scalable stream\n"));
				es->depends_on_pid = 0;
			}
		}

		evt_type = (status&GF_M2TS_TABLE_FOUND) ? GF_M2TS_EVT_PMT_FOUND : GF_M2TS_EVT_PMT_UPDATE;
		if (ts->on_event) ts->on_event(ts, evt_type, pmt->program);
	} else {
		/* if we found no new ES it's simply a repeat of the PMT */
		if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PMT_REPEAT, pmt->program);
	}
}

An issue was discovered in GPAC before 0.8.0, as demonstrated by MP4Box. It contains an invalid memory read in gf_m2ts_process_pmt in media_tools/mpegts.c that can cause a denial of service via a crafted MP4 file.


static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status)
{
	u32 info_length, pos, desc_len, evt_type, nb_es,i;
	u32 nb_sections;
	u32 data_size;
	u32 nb_hevc, nb_hevc_temp, nb_shvc, nb_shvc_temp, nb_mhvc, nb_mhvc_temp;
	unsigned char *data;
	GF_M2TS_Section *section;
	GF_Err e = GF_OK;

	/*wait for the last section */
	if (!(status&GF_M2TS_TABLE_END)) return;

	nb_es = 0;

	/*skip if already received but no update detected (eg same data) */
	if ((status&GF_M2TS_TABLE_REPEAT) && !(status&GF_M2TS_TABLE_UPDATE))  {
		if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PMT_REPEAT, pmt->program);
		return;
	}

	if (pmt->sec->demux_restarted) {
		pmt->sec->demux_restarted = 0;
		return;
	}
	GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PMT Found or updated\n"));

	nb_sections = gf_list_count(sections);
	if (nb_sections > 1) {
		GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("PMT on multiple sections not supported\n"));
	}

	section = (GF_M2TS_Section *)gf_list_get(sections, 0);
	data = section->data;
	data_size = section->data_size;

	pmt->program->pcr_pid = ((data[0] & 0x1f) << 8) | data[1];

	info_length = ((data[2]&0xf)<<8) | data[3];
	if (info_length != 0) {
		/* ...Read Descriptors ... */
		u8 tag, len;
		u32 first_loop_len = 0;
		tag = data[4];
		len = data[5];
		while (info_length > first_loop_len) {
			if (tag == GF_M2TS_MPEG4_IOD_DESCRIPTOR) {
				u32 size;
				GF_BitStream *iod_bs;
				iod_bs = gf_bs_new((char *)data+8, len-2, GF_BITSTREAM_READ);
				if (pmt->program->pmt_iod) gf_odf_desc_del((GF_Descriptor *)pmt->program->pmt_iod);
				e = gf_odf_parse_descriptor(iod_bs , (GF_Descriptor **) &pmt->program->pmt_iod, &size);
				gf_bs_del(iod_bs );
				if (e==GF_OK) {
					/*remember program number for service/program selection*/
					if (pmt->program->pmt_iod) pmt->program->pmt_iod->ServiceID = pmt->program->number;
					/*if empty IOD (freebox case), discard it and use dynamic declaration of object*/
					if (!gf_list_count(pmt->program->pmt_iod->ESDescriptors)) {
						gf_odf_desc_del((GF_Descriptor *)pmt->program->pmt_iod);
						pmt->program->pmt_iod = NULL;
					}
				}
			} else if (tag == GF_M2TS_METADATA_POINTER_DESCRIPTOR) {
				GF_BitStream *metadatapd_bs;
				GF_M2TS_MetadataPointerDescriptor *metapd;
				metadatapd_bs = gf_bs_new((char *)data+6, len, GF_BITSTREAM_READ);
				metapd = gf_m2ts_read_metadata_pointer_descriptor(metadatapd_bs, len);
				gf_bs_del(metadatapd_bs);
				if (metapd->application_format_identifier == GF_M2TS_META_ID3 &&
				        metapd->format_identifier == GF_M2TS_META_ID3 &&
				        metapd->carriage_flag == METADATA_CARRIAGE_SAME_TS) {
					/*HLS ID3 Metadata */
					pmt->program->metadata_pointer_descriptor = metapd;
				} else {
					/* don't know what to do with it for now, delete */
					gf_m2ts_metadata_pointer_descriptor_del(metapd);
				}
			} else {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Skipping descriptor (0x%x) and others not supported\n", tag));
			}
			first_loop_len += 2 + len;
		}
	}
	if (data_size <= 4 + info_length) return;
	data += 4 + info_length;
	data_size -= 4 + info_length;
	pos = 0;

	/* count de number of program related PMT received */
	for(i=0; i<gf_list_count(ts->programs); i++) {
		GF_M2TS_Program *prog = (GF_M2TS_Program *)gf_list_get(ts->programs,i);
		if(prog->pmt_pid == pmt->pid) {
			break;
		}
	}

	nb_hevc = nb_hevc_temp = nb_shvc = nb_shvc_temp = nb_mhvc = nb_mhvc_temp = 0;
	while (pos<data_size) {
		GF_M2TS_PES *pes = NULL;
		GF_M2TS_SECTION_ES *ses = NULL;
		GF_M2TS_ES *es = NULL;
		Bool inherit_pcr = 0;
		u32 pid, stream_type, reg_desc_format;

		stream_type = data[0];
		pid = ((data[1] & 0x1f) << 8) | data[2];
		desc_len = ((data[3] & 0xf) << 8) | data[4];

		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("stream_type :%d \n",stream_type));
		switch (stream_type) {

		/* PES */
		case GF_M2TS_VIDEO_MPEG1:
		case GF_M2TS_VIDEO_MPEG2:
		case GF_M2TS_VIDEO_DCII:
		case GF_M2TS_VIDEO_MPEG4:
		case GF_M2TS_SYSTEMS_MPEG4_PES:
		case GF_M2TS_VIDEO_H264:
		case GF_M2TS_VIDEO_SVC:
		case GF_M2TS_VIDEO_MVCD:
		case GF_M2TS_VIDEO_HEVC:
		case GF_M2TS_VIDEO_HEVC_MCTS:
		case GF_M2TS_VIDEO_HEVC_TEMPORAL:
		case GF_M2TS_VIDEO_SHVC:
		case GF_M2TS_VIDEO_SHVC_TEMPORAL:
		case GF_M2TS_VIDEO_MHVC:
		case GF_M2TS_VIDEO_MHVC_TEMPORAL:
			inherit_pcr = 1;
		case GF_M2TS_AUDIO_MPEG1:
		case GF_M2TS_AUDIO_MPEG2:
		case GF_M2TS_AUDIO_AAC:
		case GF_M2TS_AUDIO_LATM_AAC:
		case GF_M2TS_AUDIO_AC3:
		case GF_M2TS_AUDIO_DTS:
		case GF_M2TS_MHAS_MAIN:
		case GF_M2TS_MHAS_AUX:
		case GF_M2TS_SUBTITLE_DVB:
		case GF_M2TS_METADATA_PES:
			GF_SAFEALLOC(pes, GF_M2TS_PES);
			if (!pes) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
				return;
			}
			pes->cc = -1;
			pes->flags = GF_M2TS_ES_IS_PES;
			if (inherit_pcr)
				pes->flags |= GF_M2TS_INHERIT_PCR;
			es = (GF_M2TS_ES *)pes;
			break;
		case GF_M2TS_PRIVATE_DATA:
			GF_SAFEALLOC(pes, GF_M2TS_PES);
			if (!pes) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
				return;
			}
			pes->cc = -1;
			pes->flags = GF_M2TS_ES_IS_PES;
			es = (GF_M2TS_ES *)pes;
			break;
		/* Sections */
		case GF_M2TS_SYSTEMS_MPEG4_SECTIONS:
			GF_SAFEALLOC(ses, GF_M2TS_SECTION_ES);
			if (!ses) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
				return;
			}
			es = (GF_M2TS_ES *)ses;
			es->flags |= GF_M2TS_ES_IS_SECTION;
			/* carriage of ISO_IEC_14496 data in sections */
			if (stream_type == GF_M2TS_SYSTEMS_MPEG4_SECTIONS) {
				/*MPEG-4 sections need to be fully checked: if one section is lost, this means we lost
				one SL packet in the AU so we must wait for the complete section again*/
				ses->sec = gf_m2ts_section_filter_new(gf_m2ts_process_mpeg4section, 0);
				/*create OD container*/
				if (!pmt->program->additional_ods) {
					pmt->program->additional_ods = gf_list_new();
					ts->has_4on2 = 1;
				}
			}
			break;

		case GF_M2TS_13818_6_ANNEX_A:
		case GF_M2TS_13818_6_ANNEX_B:
		case GF_M2TS_13818_6_ANNEX_C:
		case GF_M2TS_13818_6_ANNEX_D:
		case GF_M2TS_PRIVATE_SECTION:
		case GF_M2TS_QUALITY_SEC:
		case GF_M2TS_MORE_SEC:
			GF_SAFEALLOC(ses, GF_M2TS_SECTION_ES);
			if (!ses) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
				return;
			}
			es = (GF_M2TS_ES *)ses;
			es->flags |= GF_M2TS_ES_IS_SECTION;
			es->pid = pid;
			es->service_id = pmt->program->number;
			if (stream_type == GF_M2TS_PRIVATE_SECTION) {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("AIT sections on pid %d\n", pid));
			} else if (stream_type == GF_M2TS_QUALITY_SEC) {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Quality metadata sections on pid %d\n", pid));
			} else if (stream_type == GF_M2TS_MORE_SEC) {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("MORE sections on pid %d\n", pid));
			} else {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("stream type DSM CC user private sections on pid %d \n", pid));
			}
			/* NULL means: trigger the call to on_event with DVB_GENERAL type and the raw section as payload */
			ses->sec = gf_m2ts_section_filter_new(NULL, 1);
			//ses->sec->service_id = pmt->program->number;
			break;

		case GF_M2TS_MPE_SECTIONS:
			if (! ts->prefix_present) {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("stream type MPE found : pid = %d \n", pid));
#ifdef GPAC_ENABLE_MPE
				es = gf_dvb_mpe_section_new();
				if (es->flags & GF_M2TS_ES_IS_SECTION) {
					/* NULL means: trigger the call to on_event with DVB_GENERAL type and the raw section as payload */
					((GF_M2TS_SECTION_ES*)es)->sec = gf_m2ts_section_filter_new(NULL, 1);
				}
#endif
				break;
			}

		default:
			GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Stream type (0x%x) for PID %d not supported\n", stream_type, pid ) );
			//GF_LOG(/*GF_LOG_WARNING*/GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Stream type (0x%x) for PID %d not supported\n", stream_type, pid ) );
			break;
		}

		if (es) {
			es->stream_type = (stream_type==GF_M2TS_PRIVATE_DATA) ? 0 : stream_type;
			es->program = pmt->program;
			es->pid = pid;
			es->component_tag = -1;
		}

		pos += 5;
		data += 5;

		while (desc_len) {
			u8 tag = data[0];
			u32 len = data[1];
			if (es) {
				switch (tag) {
				case GF_M2TS_ISO_639_LANGUAGE_DESCRIPTOR:
					if (pes)
						pes->lang = GF_4CC(' ', data[2], data[3], data[4]);
					break;
				case GF_M2TS_MPEG4_SL_DESCRIPTOR:
					es->mpeg4_es_id = ( (u32) data[2] & 0x1f) << 8  | data[3];
					es->flags |= GF_M2TS_ES_IS_SL;
					break;
				case GF_M2TS_REGISTRATION_DESCRIPTOR:
					reg_desc_format = GF_4CC(data[2], data[3], data[4], data[5]);
					/*cf http://www.smpte-ra.org/mpegreg/mpegreg.html*/
					switch (reg_desc_format) {
					case GF_M2TS_RA_STREAM_AC3:
						es->stream_type = GF_M2TS_AUDIO_AC3;
						break;
					case GF_M2TS_RA_STREAM_VC1:
						es->stream_type = GF_M2TS_VIDEO_VC1;
						break;
					case GF_M2TS_RA_STREAM_GPAC:
						if (len==8) {
							es->stream_type = GF_4CC(data[6], data[7], data[8], data[9]);
							es->flags |= GF_M2TS_GPAC_CODEC_ID;
							break;
						}
					default:
						GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Unknown registration descriptor %s\n", gf_4cc_to_str(reg_desc_format) ));
						break;
					}
					break;
				case GF_M2TS_DVB_EAC3_DESCRIPTOR:
					es->stream_type = GF_M2TS_AUDIO_EC3;
					break;
				case GF_M2TS_DVB_DATA_BROADCAST_ID_DESCRIPTOR:
				{
					u32 id = data[2]<<8 | data[3];
					if ((id == 0xB) && ses && !ses->sec) {
						ses->sec = gf_m2ts_section_filter_new(NULL, 1);
					}
				}
				break;
				case GF_M2TS_DVB_SUBTITLING_DESCRIPTOR:
					if (pes) {
						pes->sub.language[0] = data[2];
						pes->sub.language[1] = data[3];
						pes->sub.language[2] = data[4];
						pes->sub.type = data[5];
						pes->sub.composition_page_id = (data[6]<<8) | data[7];
						pes->sub.ancillary_page_id = (data[8]<<8) | data[9];
					}
					es->stream_type = GF_M2TS_DVB_SUBTITLE;
					break;
				case GF_M2TS_DVB_STREAM_IDENTIFIER_DESCRIPTOR:
				{
					es->component_tag = data[2];
					GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Component Tag: %d on Program %d\n", es->component_tag, es->program->number));
				}
				break;
				case GF_M2TS_DVB_TELETEXT_DESCRIPTOR:
					es->stream_type = GF_M2TS_DVB_TELETEXT;
					break;
				case GF_M2TS_DVB_VBI_DATA_DESCRIPTOR:
					es->stream_type = GF_M2TS_DVB_VBI;
					break;
				case GF_M2TS_HIERARCHY_DESCRIPTOR:
					if (pes) {
						u8 hierarchy_embedded_layer_index;
						GF_BitStream *hbs = gf_bs_new((const char *)data, data_size, GF_BITSTREAM_READ);
						/*u32 skip = */gf_bs_read_int(hbs, 16);
						/*u8 res1 = */gf_bs_read_int(hbs, 1);
						/*u8 temp_scal = */gf_bs_read_int(hbs, 1);
						/*u8 spatial_scal = */gf_bs_read_int(hbs, 1);
						/*u8 quality_scal = */gf_bs_read_int(hbs, 1);
						/*u8 hierarchy_type = */gf_bs_read_int(hbs, 4);
						/*u8 res2 = */gf_bs_read_int(hbs, 2);
						/*u8 hierarchy_layer_index = */gf_bs_read_int(hbs, 6);
						/*u8 tref_not_present = */gf_bs_read_int(hbs, 1);
						/*u8 res3 = */gf_bs_read_int(hbs, 1);
						hierarchy_embedded_layer_index = gf_bs_read_int(hbs, 6);
						/*u8 res4 = */gf_bs_read_int(hbs, 2);
						/*u8 hierarchy_channel = */gf_bs_read_int(hbs, 6);
						gf_bs_del(hbs);

						pes->depends_on_pid = 1+hierarchy_embedded_layer_index;
					}
					break;
				case GF_M2TS_METADATA_DESCRIPTOR:
				{
					GF_BitStream *metadatad_bs;
					GF_M2TS_MetadataDescriptor *metad;
					metadatad_bs = gf_bs_new((char *)data+2, len, GF_BITSTREAM_READ);
					metad = gf_m2ts_read_metadata_descriptor(metadatad_bs, len);
					gf_bs_del(metadatad_bs);
					if (metad->application_format_identifier == GF_M2TS_META_ID3 &&
					        metad->format_identifier == GF_M2TS_META_ID3) {
						/*HLS ID3 Metadata */
						if (pes) {
							pes->metadata_descriptor = metad;
							pes->stream_type = GF_M2TS_METADATA_ID3_HLS;
						}
					} else {
						/* don't know what to do with it for now, delete */
						gf_m2ts_metadata_descriptor_del(metad);
					}
				}
				break;

				default:
					GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] skipping descriptor (0x%x) not supported\n", tag));
					break;
				}
			}

			data += len+2;
			pos += len+2;
			if (desc_len < len+2) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PMT es descriptor size for PID %d\n", pid ) );
				break;
			}
			desc_len-=len+2;
		}

		if (es && !es->stream_type) {
			gf_free(es);
			es = NULL;
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Private Stream type (0x%x) for PID %d not supported\n", stream_type, pid ) );
		}

		if (!es) continue;

		if (ts->ess[pid]) {
			//this is component reuse across programs, overwrite the previously declared stream ...
			if (status & GF_M2TS_TABLE_FOUND) {
				GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d reused across programs %d and %d, not completely supported\n", pid, ts->ess[pid]->program->number, es->program->number ) );

				//add stream to program but don't reassign the pid table until the stream is playing (>GF_M2TS_PES_FRAMING_SKIP)
				gf_list_add(pmt->program->streams, es);
				if (!(es->flags & GF_M2TS_ES_IS_SECTION) ) gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP);

				nb_es++;
				//skip assignment below
				es = NULL;
			}
			/*watchout for pmt update - FIXME this likely won't work in most cases*/
			else {

				GF_M2TS_ES *o_es = ts->ess[es->pid];

				if ((o_es->stream_type == es->stream_type)
				        && ((o_es->flags & GF_M2TS_ES_STATIC_FLAGS_MASK) == (es->flags & GF_M2TS_ES_STATIC_FLAGS_MASK))
				        && (o_es->mpeg4_es_id == es->mpeg4_es_id)
				        && ((o_es->flags & GF_M2TS_ES_IS_SECTION) || ((GF_M2TS_PES *)o_es)->lang == ((GF_M2TS_PES *)es)->lang)
				   ) {
					gf_free(es);
					es = NULL;
				} else {
					gf_m2ts_es_del(o_es, ts);
					ts->ess[es->pid] = NULL;
				}
			}
		}

		if (es) {
			ts->ess[es->pid] = es;
			gf_list_add(pmt->program->streams, es);
			if (!(es->flags & GF_M2TS_ES_IS_SECTION) ) gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP);

			nb_es++;

			if (es->stream_type == GF_M2TS_VIDEO_HEVC) nb_hevc++;
			else if (es->stream_type == GF_M2TS_VIDEO_HEVC_TEMPORAL) nb_hevc_temp++;
			else if (es->stream_type == GF_M2TS_VIDEO_SHVC) nb_shvc++;
			else if (es->stream_type == GF_M2TS_VIDEO_SHVC_TEMPORAL) nb_shvc_temp++;
			else if (es->stream_type == GF_M2TS_VIDEO_MHVC) nb_mhvc++;
			else if (es->stream_type == GF_M2TS_VIDEO_MHVC_TEMPORAL) nb_mhvc_temp++;
		}
	}

	//Table 2-139, implied hierarchy indexes
	if (nb_hevc_temp + nb_shvc + nb_shvc_temp + nb_mhvc+ nb_mhvc_temp) {
		for (i=0; i<gf_list_count(pmt->program->streams); i++) {
			GF_M2TS_PES *es = (GF_M2TS_PES *)gf_list_get(pmt->program->streams, i);
			if ( !(es->flags & GF_M2TS_ES_IS_PES)) continue;
			if (es->depends_on_pid) continue;

			switch (es->stream_type) {
			case GF_M2TS_VIDEO_HEVC_TEMPORAL:
				es->depends_on_pid = 1;
				break;
			case GF_M2TS_VIDEO_SHVC:
				if (!nb_hevc_temp) es->depends_on_pid = 1;
				else es->depends_on_pid = 2;
				break;
			case GF_M2TS_VIDEO_SHVC_TEMPORAL:
				es->depends_on_pid = 3;
				break;
			case GF_M2TS_VIDEO_MHVC:
				if (!nb_hevc_temp) es->depends_on_pid = 1;
				else es->depends_on_pid = 2;
				break;
			case GF_M2TS_VIDEO_MHVC_TEMPORAL:
				if (!nb_hevc_temp) es->depends_on_pid = 2;
				else es->depends_on_pid = 3;
				break;
			}
		}
	}

	if (nb_es) {
		u32 i;

		//translate hierarchy descriptors indexes into PIDs - check whether the PMT-index rules are the same for HEVC
		for (i=0; i<gf_list_count(pmt->program->streams); i++) {
			GF_M2TS_PES *an_es = NULL;
			GF_M2TS_PES *es = (GF_M2TS_PES *)gf_list_get(pmt->program->streams, i);
			if ( !(es->flags & GF_M2TS_ES_IS_PES)) continue;
			if (!es->depends_on_pid) continue;

			//fixeme we are not always assured that hierarchy_layer_index matches the stream index...
			//+1 is because our first stream is the PMT
			an_es =  (GF_M2TS_PES *)gf_list_get(pmt->program->streams, es->depends_on_pid);
			if (an_es) {
				es->depends_on_pid = an_es->pid;
			} else {
				GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TS] Wrong dependency index in hierarchy descriptor, assuming non-scalable stream\n"));
				es->depends_on_pid = 0;
			}
		}

		evt_type = (status&GF_M2TS_TABLE_FOUND) ? GF_M2TS_EVT_PMT_FOUND : GF_M2TS_EVT_PMT_UPDATE;
		if (ts->on_event) ts->on_event(ts, evt_type, pmt->program);
	} else {
		/* if we found no new ES it's simply a repeat of the PMT */
		if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PMT_REPEAT, pmt->program);
	}
}


Added stack overflow check for hermes::vm:: hermesBuiltinApply

Summary: This adds a missing check for stack overflow.

Reviewed By: tmikov

Differential Revision: D20104955

fbshipit-source-id: 1f37e23d2e28ebcd3aa4176d134b8418e7059ebd

hermesBuiltinApply(void *, Runtime *runtime, NativeArgs args) {
  GCScopeMarkerRAII marker{runtime};

  Handle<Callable> fn = args.dyncastArg<Callable>(0);
  if (LLVM_UNLIKELY(!fn)) {
    return runtime->raiseTypeErrorForValue(
        args.getArgHandle(0), " is not a function");
  }

  Handle<JSArray> argArray = args.dyncastArg<JSArray>(1);
  if (LLVM_UNLIKELY(!argArray)) {
    return runtime->raiseTypeError("args must be an array");
  }

  uint32_t len = JSArray::getLength(*argArray);

  bool isConstructor = args.getArgCount() == 2;

  MutableHandle<> thisVal{runtime};
  if (isConstructor) {
    auto thisValRes = Callable::createThisForConstruct(fn, runtime);
    if (LLVM_UNLIKELY(thisValRes == ExecutionStatus::EXCEPTION)) {
      return ExecutionStatus::EXCEPTION;
    }
    thisVal = *thisValRes;
  } else {
    thisVal = args.getArg(2);
  }

  ScopedNativeCallFrame newFrame{
      runtime, len, *fn, isConstructor, thisVal.getHermesValue()};
  for (uint32_t i = 0; i < len; ++i) {
    newFrame->getArgRef(i) = argArray->at(runtime, i);
  }
  return isConstructor ? Callable::construct(fn, runtime, thisVal)
                       : Callable::call(fn, runtime);
}

A stack overflow vulnerability in Facebook Hermes 'builtin apply' prior to commit 86543ac47e59c522976b5632b8bf9a2a4583c7d2 (https://github.com/facebook/hermes/commit/86543ac47e59c522976b5632b8bf9a2a4583c7d2) allows attackers to potentially execute arbitrary code via crafted JavaScript. Note that this is only exploitable if the application using Hermes permits evaluation of untrusted JavaScript. Hence, most React Native applications are not affected.


hermesBuiltinApply(void *, Runtime *runtime, NativeArgs args) {
  GCScopeMarkerRAII marker{runtime};

  Handle<Callable> fn = args.dyncastArg<Callable>(0);
  if (LLVM_UNLIKELY(!fn)) {
    return runtime->raiseTypeErrorForValue(
        args.getArgHandle(0), " is not a function");
  }

  Handle<JSArray> argArray = args.dyncastArg<JSArray>(1);
  if (LLVM_UNLIKELY(!argArray)) {
    return runtime->raiseTypeError("args must be an array");
  }

  uint32_t len = JSArray::getLength(*argArray);

  bool isConstructor = args.getArgCount() == 2;

  MutableHandle<> thisVal{runtime};
  if (isConstructor) {
    auto thisValRes = Callable::createThisForConstruct(fn, runtime);
    if (LLVM_UNLIKELY(thisValRes == ExecutionStatus::EXCEPTION)) {
      return ExecutionStatus::EXCEPTION;
    }
    thisVal = *thisValRes;
  } else {
    thisVal = args.getArg(2);
  }

  ScopedNativeCallFrame newFrame{
      runtime, len, *fn, isConstructor, thisVal.getHermesValue()};
  if (LLVM_UNLIKELY(newFrame.overflowed()))
    return runtime->raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);

  for (uint32_t i = 0; i < len; ++i) {
    newFrame->getArgRef(i) = argArray->at(runtime, i);
  }
  return isConstructor ? Callable::construct(fn, runtime, thisVal)
                       : Callable::call(fn, runtime);
}


Reject replies to DNS PTR requests that contain invalid characters

Fixes issue #1033 reported by @ping86

DNSResult DNS::GetResult()
{
	/* Fetch dns query response and decide where it belongs */
	DNSHeader header;
	DNSRequest *req;
	unsigned char buffer[sizeof(DNSHeader)];
	irc::sockets::sockaddrs from;
	memset(&from, 0, sizeof(from));
	socklen_t x = sizeof(from);

	int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);

	/* Did we get the whole header? */
	if (length < 12)
	{
		ServerInstance->Logs->Log("RESOLVER",DEBUG,"GetResult didn't get a full packet (len=%d)", length);
		/* Nope - something screwed up. */
		return DNSResult(-1,"",0,"");
	}

	/* Check wether the reply came from a different DNS
	 * server to the one we sent it to, or the source-port
	 * is not 53.
	 * A user could in theory still spoof dns packets anyway
	 * but this is less trivial than just sending garbage
	 * to the server, which is possible without this check.
	 *
	 * -- Thanks jilles for pointing this one out.
	 */
	if (from != myserver)
	{
		std::string server1 = from.str();
		std::string server2 = myserver.str();
		ServerInstance->Logs->Log("RESOLVER",DEBUG,"Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
			server1.c_str(), server2.c_str());
		return DNSResult(-1,"",0,"");
	}

	/* Put the read header info into a header class */
	DNS::FillHeader(&header,buffer,length - 12);

	/* Get the id of this request.
	 * Its a 16 bit value stored in two char's,
	 * so we use logic shifts to create the value.
	 */
	unsigned long this_id = header.id[1] + (header.id[0] << 8);

	/* Do we have a pending request matching this id? */
	if (!requests[this_id])
	{
		/* Somehow we got a DNS response for a request we never made... */
		ServerInstance->Logs->Log("RESOLVER",DEBUG,"Hmm, got a result that we didn't ask for (id=%lx). Ignoring.", this_id);
		return DNSResult(-1,"",0,"");
	}
	else
	{
		/* Remove the query from the list of pending queries */
		req = requests[this_id];
		requests[this_id] = NULL;
	}

	/* Inform the DNSRequest class that it has a result to be read.
	 * When its finished it will return a DNSInfo which is a pair of
	 * unsigned char* resource record data, and an error message.
	 */
	DNSInfo data = req->ResultIsReady(header, length);
	std::string resultstr;

	/* Check if we got a result, if we didnt, its an error */
	if (data.first == NULL)
	{
		/* An error.
		 * Mask the ID with the value of ERROR_MASK, so that
		 * the dns_deal_with_classes() function knows that its
		 * an error response and needs to be treated uniquely.
		 * Put the error message in the second field.
		 */
		std::string ro = req->orig;
		delete req;
		return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
	}
	else
	{
		unsigned long ttl = req->ttl;
		char formatted[128];

		/* Forward lookups come back as binary data. We must format them into ascii */
		switch (req->type)
		{
			case DNS_QUERY_A:
				snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
				resultstr = formatted;
			break;

			case DNS_QUERY_AAAA:
			{
				if (!inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted)))
				{
					std::string ro = req->orig;
					delete req;
					return DNSResult(this_id | ERROR_MASK, "inet_ntop() failed", 0, ro);
				}

				resultstr = formatted;

				/* Special case. Sending ::1 around between servers
				 * and to clients is dangerous, because the : on the
				 * start makes the client or server interpret the IP
				 * as the last parameter on the line with a value ":1".
				 */
				if (*formatted == ':')
					resultstr.insert(0, "0");
			}
			break;

			case DNS_QUERY_CNAME:
				/* Identical handling to PTR */

			case DNS_QUERY_PTR:
				/* Reverse lookups just come back as char* */
				resultstr = std::string((const char*)data.first);
			break;

			default:
			break;
		}

		/* Build the reply with the id and hostname/ip in it */
		std::string ro = req->orig;
		DNSResult result = DNSResult(this_id,resultstr,ttl,ro,req->type);
		delete req;
		return result;
	}
}

The DNS::GetResult function in dns.cpp in InspIRCd before 2.0.19 allows remote DNS servers to cause a denial of service (netsplit) via an invalid character in a PTR response, as demonstrated by a "\032" (whitespace) character in a hostname.


DNSResult DNS::GetResult()
{
	/* Fetch dns query response and decide where it belongs */
	DNSHeader header;
	DNSRequest *req;
	unsigned char buffer[sizeof(DNSHeader)];
	irc::sockets::sockaddrs from;
	memset(&from, 0, sizeof(from));
	socklen_t x = sizeof(from);

	int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);

	/* Did we get the whole header? */
	if (length < 12)
	{
		ServerInstance->Logs->Log("RESOLVER",DEBUG,"GetResult didn't get a full packet (len=%d)", length);
		/* Nope - something screwed up. */
		return DNSResult(-1,"",0,"");
	}

	/* Check wether the reply came from a different DNS
	 * server to the one we sent it to, or the source-port
	 * is not 53.
	 * A user could in theory still spoof dns packets anyway
	 * but this is less trivial than just sending garbage
	 * to the server, which is possible without this check.
	 *
	 * -- Thanks jilles for pointing this one out.
	 */
	if (from != myserver)
	{
		std::string server1 = from.str();
		std::string server2 = myserver.str();
		ServerInstance->Logs->Log("RESOLVER",DEBUG,"Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
			server1.c_str(), server2.c_str());
		return DNSResult(-1,"",0,"");
	}

	/* Put the read header info into a header class */
	DNS::FillHeader(&header,buffer,length - 12);

	/* Get the id of this request.
	 * Its a 16 bit value stored in two char's,
	 * so we use logic shifts to create the value.
	 */
	unsigned long this_id = header.id[1] + (header.id[0] << 8);

	/* Do we have a pending request matching this id? */
	if (!requests[this_id])
	{
		/* Somehow we got a DNS response for a request we never made... */
		ServerInstance->Logs->Log("RESOLVER",DEBUG,"Hmm, got a result that we didn't ask for (id=%lx). Ignoring.", this_id);
		return DNSResult(-1,"",0,"");
	}
	else
	{
		/* Remove the query from the list of pending queries */
		req = requests[this_id];
		requests[this_id] = NULL;
	}

	/* Inform the DNSRequest class that it has a result to be read.
	 * When its finished it will return a DNSInfo which is a pair of
	 * unsigned char* resource record data, and an error message.
	 */
	DNSInfo data = req->ResultIsReady(header, length);
	std::string resultstr;

	/* Check if we got a result, if we didnt, its an error */
	if (data.first == NULL)
	{
		/* An error.
		 * Mask the ID with the value of ERROR_MASK, so that
		 * the dns_deal_with_classes() function knows that its
		 * an error response and needs to be treated uniquely.
		 * Put the error message in the second field.
		 */
		std::string ro = req->orig;
		delete req;
		return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
	}
	else
	{
		unsigned long ttl = req->ttl;
		char formatted[128];

		/* Forward lookups come back as binary data. We must format them into ascii */
		switch (req->type)
		{
			case DNS_QUERY_A:
				snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
				resultstr = formatted;
			break;

			case DNS_QUERY_AAAA:
			{
				if (!inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted)))
				{
					std::string ro = req->orig;
					delete req;
					return DNSResult(this_id | ERROR_MASK, "inet_ntop() failed", 0, ro);
				}

				resultstr = formatted;

				/* Special case. Sending ::1 around between servers
				 * and to clients is dangerous, because the : on the
				 * start makes the client or server interpret the IP
				 * as the last parameter on the line with a value ":1".
				 */
				if (*formatted == ':')
					resultstr.insert(0, "0");
			}
			break;

			case DNS_QUERY_CNAME:
				/* Identical handling to PTR */

			case DNS_QUERY_PTR:
			{
				/* Reverse lookups just come back as char* */
				resultstr = std::string((const char*)data.first);
				if (resultstr.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-") != std::string::npos)
				{
					std::string ro = req->orig;
					delete req;
					return DNSResult(this_id | ERROR_MASK, "Invalid char(s) in reply", 0, ro);
				}
			}
			break;

			default:
			break;
		}

		/* Build the reply with the id and hostname/ip in it */
		std::string ro = req->orig;
		DNSResult result = DNSResult(this_id,resultstr,ttl,ro,req->type);
		delete req;
		return result;
	}
}


Prevent a division by 0

PiperOrigin-RevId: 371007407
Change-Id: Iecf2718de48d6bf5a69b02a9df9deda8ec1b19d3

TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  TfLiteTensor* output;
  TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output));
  TfLiteTensor* hits;
  TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 1, &hits));
  const TfLiteTensor* lookup;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &lookup));
  const TfLiteTensor* key;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 1, &key));
  const TfLiteTensor* value;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 2, &value));

  const int num_rows = SizeOfDimension(value, 0);
  const int row_bytes = value->bytes / num_rows;
  void* pointer = nullptr;
  DynamicBuffer buf;

  for (int i = 0; i < SizeOfDimension(lookup, 0); i++) {
    int idx = -1;
    pointer = bsearch(&(lookup->data.i32[i]), key->data.i32, num_rows,
                      sizeof(int32_t), greater);
    if (pointer != nullptr) {
      idx = (reinterpret_cast<char*>(pointer) - (key->data.raw)) /
            sizeof(int32_t);
    }

    if (idx >= num_rows || idx < 0) {
      if (output->type == kTfLiteString) {
        buf.AddString(nullptr, 0);
      } else {
        memset(output->data.raw + i * row_bytes, 0, row_bytes);
      }
      hits->data.uint8[i] = 0;
    } else {
      if (output->type == kTfLiteString) {
        buf.AddString(GetString(value, idx));
      } else {
        memcpy(output->data.raw + i * row_bytes,
               value->data.raw + idx * row_bytes, row_bytes);
      }
      hits->data.uint8[i] = 1;
    }
  }
  if (output->type == kTfLiteString) {
    buf.WriteToTensorAsVector(output);
  }

  return kTfLiteOk;
}

TensorFlow is an end-to-end open source platform for machine learning. The TFLite implementation of hashtable lookup is vulnerable to a division by zero error(https://github.com/tensorflow/tensorflow/blob/1a8e885b864c818198a5b2c0cbbeca5a1e833bc8/tensorflow/lite/kernels/hashtable_lookup.cc#L114-L115) An attacker can craft a model such that `values`'s first dimension would be 0. The fix will be included in TensorFlow 2.5.0. We will also cherrypick this commit on TensorFlow 2.4.2, TensorFlow 2.3.3, TensorFlow 2.2.3 and TensorFlow 2.1.4, as these are also affected and still in supported range.


TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  TfLiteTensor* output;
  TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output));
  TfLiteTensor* hits;
  TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 1, &hits));
  const TfLiteTensor* lookup;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &lookup));
  const TfLiteTensor* key;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 1, &key));
  const TfLiteTensor* value;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 2, &value));

  const int num_rows = SizeOfDimension(value, 0);
  TF_LITE_ENSURE(context, num_rows != 0);
  const int row_bytes = value->bytes / num_rows;
  void* pointer = nullptr;
  DynamicBuffer buf;

  for (int i = 0; i < SizeOfDimension(lookup, 0); i++) {
    int idx = -1;
    pointer = bsearch(&(lookup->data.i32[i]), key->data.i32, num_rows,
                      sizeof(int32_t), greater);
    if (pointer != nullptr) {
      idx = (reinterpret_cast<char*>(pointer) - (key->data.raw)) /
            sizeof(int32_t);
    }

    if (idx >= num_rows || idx < 0) {
      if (output->type == kTfLiteString) {
        buf.AddString(nullptr, 0);
      } else {
        memset(output->data.raw + i * row_bytes, 0, row_bytes);
      }
      hits->data.uint8[i] = 0;
    } else {
      if (output->type == kTfLiteString) {
        buf.AddString(GetString(value, idx));
      } else {
        memcpy(output->data.raw + i * row_bytes,
               value->data.raw + idx * row_bytes, row_bytes);
      }
      hits->data.uint8[i] = 1;
    }
  }
  if (output->type == kTfLiteString) {
    buf.WriteToTensorAsVector(output);
  }

  return kTfLiteOk;
}


Do not realloc array if new raster size is 0.

if realloc() is called with 0 size it may return NULL and this will be incorrectly handled
as not enough memory and (also) rasterBits will be freed by realloc but we will not update
it.

void DDGifSlurp(GifInfo *info, bool decode, bool exitAfterFrame) {
	GifRecordType RecordType;
	GifByteType *ExtData;
	int ExtFunction;
	GifFileType *gifFilePtr;
	gifFilePtr = info->gifFilePtr;
	uint_fast32_t lastAllocatedGCBIndex = 0;
	do {
		if (DGifGetRecordType(gifFilePtr, &RecordType) == GIF_ERROR) {
			break;
		}
		bool isInitialPass = !decode && !exitAfterFrame;
		switch (RecordType) {
			case IMAGE_DESC_RECORD_TYPE:

				if (DGifGetImageDesc(gifFilePtr, isInitialPass) == GIF_ERROR) {
					break;
				}

				if (isInitialPass) {
					int_fast32_t widthOverflow = gifFilePtr->Image.Width - gifFilePtr->SWidth;
					int_fast32_t heightOverflow = gifFilePtr->Image.Height - gifFilePtr->SHeight;
					if (widthOverflow > 0 || heightOverflow > 0) {
						gifFilePtr->SWidth += widthOverflow;
						gifFilePtr->SHeight += heightOverflow;
					}
					SavedImage *sp = &gifFilePtr->SavedImages[gifFilePtr->ImageCount - 1];
					int_fast32_t topOverflow = gifFilePtr->Image.Top + gifFilePtr->Image.Height - gifFilePtr->SHeight;
					if (topOverflow > 0) {
						sp->ImageDesc.Top -= topOverflow;
					}

					int_fast32_t leftOverflow = gifFilePtr->Image.Left + gifFilePtr->Image.Width - gifFilePtr->SWidth;
					if (leftOverflow > 0) {
						sp->ImageDesc.Left -= leftOverflow;
					}
					if (!updateGCB(info, &lastAllocatedGCBIndex)) {
						break;
					}
				}

				if (decode) {
					int_fast32_t widthOverflow = gifFilePtr->Image.Width - info->originalWidth;
					int_fast32_t heightOverflow = gifFilePtr->Image.Height - info->originalHeight;
					const uint_fast32_t newRasterSize = gifFilePtr->Image.Width * gifFilePtr->Image.Height;
					if (newRasterSize > info->rasterSize || widthOverflow > 0 || heightOverflow > 0) {
						void *tmpRasterBits = reallocarray(info->rasterBits, newRasterSize, sizeof(GifPixelType));
						if (tmpRasterBits == NULL) {
							gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
							break;
						}
						info->rasterBits = tmpRasterBits;
						info->rasterSize = newRasterSize;
					}
					if (gifFilePtr->Image.Interlace) {
						uint_fast16_t i, j;
						/*
						 * The way an interlaced image should be read -
						 * offsets and jumps...
						 */
						uint_fast8_t InterlacedOffset[] = {0, 4, 2, 1};
						uint_fast8_t InterlacedJumps[] = {8, 8, 4, 2};
						/* Need to perform 4 passes on the image */
						for (i = 0; i < 4; i++)
							for (j = InterlacedOffset[i]; j < gifFilePtr->Image.Height; j += InterlacedJumps[i]) {
								if (DGifGetLine(gifFilePtr, info->rasterBits + j * gifFilePtr->Image.Width, gifFilePtr->Image.Width) == GIF_ERROR)
									break;
							}
					} else {
						if (DGifGetLine(gifFilePtr, info->rasterBits, gifFilePtr->Image.Width * gifFilePtr->Image.Height) == GIF_ERROR) {
							break;
						}
					}

					if (info->sampleSize > 1) {
						unsigned char *dst = info->rasterBits;
						unsigned char *src = info->rasterBits;
						unsigned char *const srcEndImage = info->rasterBits + gifFilePtr->Image.Width * gifFilePtr->Image.Height;
						do {
							unsigned char *srcNextLineStart = src + gifFilePtr->Image.Width * info->sampleSize;
							unsigned char *const srcEndLine = src + gifFilePtr->Image.Width;
							unsigned char *dstEndLine = dst + gifFilePtr->Image.Width / info->sampleSize;
							do {
								*dst = *src;
								dst++;
								src += info->sampleSize;
							} while (src < srcEndLine);
							dst = dstEndLine;
							src = srcNextLineStart;
						} while (src < srcEndImage);
					}
					return;
				} else {
					do {
						if (DGifGetCodeNext(gifFilePtr, &ExtData) == GIF_ERROR) {
							break;
						}
					} while (ExtData != NULL);
					if (exitAfterFrame) {
						return;
					}
				}
				break;

			case EXTENSION_RECORD_TYPE:
				if (DGifGetExtension(gifFilePtr, &ExtFunction, &ExtData) == GIF_ERROR) {
					break;
				}
				if (isInitialPass) {
					updateGCB(info, &lastAllocatedGCBIndex);
					if (readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) {
						break;
					}
				}
				while (ExtData != NULL) {
					if (DGifGetExtensionNext(gifFilePtr, &ExtData) == GIF_ERROR) {
						break;
					}
					if (isInitialPass && readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) {
						break;
					}
				}
				break;

			case TERMINATE_RECORD_TYPE:
				break;

			default: /* Should be trapped by DGifGetRecordType */
				break;
		}
	} while (RecordType != TERMINATE_RECORD_TYPE);

	info->rewindFunction(info);
}

A double free vulnerability in the DDGifSlurp function in decoding.c in the android-gif-drawable library before version 1.2.18, as used in WhatsApp for Android before version 2.19.244 and many other Android applications, allows remote attackers to execute arbitrary code or cause a denial of service when the library is used to parse a specially crafted GIF image.


void DDGifSlurp(GifInfo *info, bool decode, bool exitAfterFrame) {
	GifRecordType RecordType;
	GifByteType *ExtData;
	int ExtFunction;
	GifFileType *gifFilePtr;
	gifFilePtr = info->gifFilePtr;
	uint_fast32_t lastAllocatedGCBIndex = 0;
	do {
		if (DGifGetRecordType(gifFilePtr, &RecordType) == GIF_ERROR) {
			break;
		}
		bool isInitialPass = !decode && !exitAfterFrame;
		switch (RecordType) {
			case IMAGE_DESC_RECORD_TYPE:

				if (DGifGetImageDesc(gifFilePtr, isInitialPass) == GIF_ERROR) {
					break;
				}

				if (isInitialPass) {
					int_fast32_t widthOverflow = gifFilePtr->Image.Width - gifFilePtr->SWidth;
					int_fast32_t heightOverflow = gifFilePtr->Image.Height - gifFilePtr->SHeight;
					if (widthOverflow > 0 || heightOverflow > 0) {
						gifFilePtr->SWidth += widthOverflow;
						gifFilePtr->SHeight += heightOverflow;
					}
					SavedImage *sp = &gifFilePtr->SavedImages[gifFilePtr->ImageCount - 1];
					int_fast32_t topOverflow = gifFilePtr->Image.Top + gifFilePtr->Image.Height - gifFilePtr->SHeight;
					if (topOverflow > 0) {
						sp->ImageDesc.Top -= topOverflow;
					}

					int_fast32_t leftOverflow = gifFilePtr->Image.Left + gifFilePtr->Image.Width - gifFilePtr->SWidth;
					if (leftOverflow > 0) {
						sp->ImageDesc.Left -= leftOverflow;
					}
					if (!updateGCB(info, &lastAllocatedGCBIndex)) {
						break;
					}
				}

				if (decode) {
					const uint_fast32_t newRasterSize = gifFilePtr->Image.Width * gifFilePtr->Image.Height;
					if (newRasterSize == 0) {
						free(info->rasterBits);
						info->rasterBits = NULL;
						info->rasterSize = newRasterSize;
						return;
					}
					const int_fast32_t widthOverflow = gifFilePtr->Image.Width - info->originalWidth;
					const int_fast32_t heightOverflow = gifFilePtr->Image.Height - info->originalHeight;
					if (newRasterSize > info->rasterSize || widthOverflow > 0 || heightOverflow > 0) {
						void *tmpRasterBits = reallocarray(info->rasterBits, newRasterSize, sizeof(GifPixelType));
						if (tmpRasterBits == NULL) {
							gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
							break;
						}
						info->rasterBits = tmpRasterBits;
						info->rasterSize = newRasterSize;
					}
					if (gifFilePtr->Image.Interlace) {
						uint_fast16_t i, j;
						/*
						 * The way an interlaced image should be read -
						 * offsets and jumps...
						 */
						uint_fast8_t InterlacedOffset[] = {0, 4, 2, 1};
						uint_fast8_t InterlacedJumps[] = {8, 8, 4, 2};
						/* Need to perform 4 passes on the image */
						for (i = 0; i < 4; i++)
							for (j = InterlacedOffset[i]; j < gifFilePtr->Image.Height; j += InterlacedJumps[i]) {
								if (DGifGetLine(gifFilePtr, info->rasterBits + j * gifFilePtr->Image.Width, gifFilePtr->Image.Width) == GIF_ERROR)
									break;
							}
					} else {
						if (DGifGetLine(gifFilePtr, info->rasterBits, gifFilePtr->Image.Width * gifFilePtr->Image.Height) == GIF_ERROR) {
							break;
						}
					}

					if (info->sampleSize > 1) {
						unsigned char *dst = info->rasterBits;
						unsigned char *src = info->rasterBits;
						unsigned char *const srcEndImage = info->rasterBits + gifFilePtr->Image.Width * gifFilePtr->Image.Height;
						do {
							unsigned char *srcNextLineStart = src + gifFilePtr->Image.Width * info->sampleSize;
							unsigned char *const srcEndLine = src + gifFilePtr->Image.Width;
							unsigned char *dstEndLine = dst + gifFilePtr->Image.Width / info->sampleSize;
							do {
								*dst = *src;
								dst++;
								src += info->sampleSize;
							} while (src < srcEndLine);
							dst = dstEndLine;
							src = srcNextLineStart;
						} while (src < srcEndImage);
					}
					return;
				} else {
					do {
						if (DGifGetCodeNext(gifFilePtr, &ExtData) == GIF_ERROR) {
							break;
						}
					} while (ExtData != NULL);
					if (exitAfterFrame) {
						return;
					}
				}
				break;

			case EXTENSION_RECORD_TYPE:
				if (DGifGetExtension(gifFilePtr, &ExtFunction, &ExtData) == GIF_ERROR) {
					break;
				}
				if (isInitialPass) {
					updateGCB(info, &lastAllocatedGCBIndex);
					if (readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) {
						break;
					}
				}
				while (ExtData != NULL) {
					if (DGifGetExtensionNext(gifFilePtr, &ExtData) == GIF_ERROR) {
						break;
					}
					if (isInitialPass && readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) {
						break;
					}
				}
				break;

			case TERMINATE_RECORD_TYPE:
				break;

			default: /* Should be trapped by DGifGetRecordType */
				break;
		}
	} while (RecordType != TERMINATE_RECORD_TYPE);

	info->rewindFunction(info);
}


Throw on skipping an invalid type.

Summary:
Certain values (e.g.) T_STOP should not appear as a skip type. Allowing them to
can cause thrift to loop unboundedly.

Reviewed By: spalamarchuk

Differential Revision: D15102451

fbshipit-source-id: c08d52f44f37e9c212d3480233ac217105586c9f

uint32_t skip(Protocol_& prot, TType arg_type) {
  switch (arg_type) {
    case T_BOOL: {
      bool boolv;
      return prot.readBool(boolv);
    }
    case T_BYTE: {
      int8_t bytev = 0;
      return prot.readByte(bytev);
    }
    case T_I16: {
      int16_t i16;
      return prot.readI16(i16);
    }
    case T_I32: {
      int32_t i32;
      return prot.readI32(i32);
    }
    case T_I64: {
      int64_t i64;
      return prot.readI64(i64);
    }
    case T_DOUBLE: {
      double dub;
      return prot.readDouble(dub);
    }
    case T_FLOAT: {
      float flt;
      return prot.readFloat(flt);
    }
    case T_STRING: {
      std::string str;
      return prot.readBinary(str);
    }
    case T_STRUCT: {
      uint32_t result = 0;
      std::string name;
      int16_t fid;
      TType ftype;
      result += prot.readStructBegin(name);
      while (true) {
        result += prot.readFieldBegin(name, ftype, fid);
        if (ftype == T_STOP) {
          break;
        }
        result += skip(prot, ftype);
        result += prot.readFieldEnd();
      }
      result += prot.readStructEnd();
      return result;
    }
    case T_MAP: {
      uint32_t result = 0;
      TType keyType;
      TType valType;
      uint32_t i, size;
      bool sizeUnknown;
      result += prot.readMapBegin(keyType, valType, size, sizeUnknown);
      if (!sizeUnknown) {
        for (i = 0; i < size; i++) {
          result += skip(prot, keyType);
          result += skip(prot, valType);
        }
      } else {
        while (prot.peekMap()) {
          result += skip(prot, keyType);
          result += skip(prot, valType);
        }
      }
      result += prot.readMapEnd();
      return result;
    }
    case T_SET: {
      uint32_t result = 0;
      TType elemType;
      uint32_t i, size;
      bool sizeUnknown;
      result += prot.readSetBegin(elemType, size, sizeUnknown);
      if (!sizeUnknown) {
        for (i = 0; i < size; i++) {
          result += skip(prot, elemType);
        }
      } else {
        while (prot.peekSet()) {
          result += skip(prot, elemType);
        }
      }
      result += prot.readSetEnd();
      return result;
    }
    case T_LIST: {
      uint32_t result = 0;
      TType elemType;
      uint32_t i, size;
      bool sizeUnknown;
      result += prot.readListBegin(elemType, size, sizeUnknown);
      if (!sizeUnknown) {
        for (i = 0; i < size; i++) {
          result += skip(prot, elemType);
        }
      } else {
        while (prot.peekList()) {
          result += skip(prot, elemType);
        }
      }
      result += prot.readListEnd();
      return result;
    }
    default:
      return 0;
  }
}

Legacy C++ Facebook Thrift servers (using cpp instead of cpp2) would not error upon receiving messages with containers of fields of unknown type. As a result, malicious clients could send short messages which would take a long time for the server to parse, potentially leading to denial of service. This issue affects Facebook Thrift prior to v2019.05.06.00.


uint32_t skip(Protocol_& prot, TType arg_type) {
  switch (arg_type) {
    case T_BOOL: {
      bool boolv;
      return prot.readBool(boolv);
    }
    case T_BYTE: {
      int8_t bytev = 0;
      return prot.readByte(bytev);
    }
    case T_I16: {
      int16_t i16;
      return prot.readI16(i16);
    }
    case T_I32: {
      int32_t i32;
      return prot.readI32(i32);
    }
    case T_I64: {
      int64_t i64;
      return prot.readI64(i64);
    }
    case T_DOUBLE: {
      double dub;
      return prot.readDouble(dub);
    }
    case T_FLOAT: {
      float flt;
      return prot.readFloat(flt);
    }
    case T_STRING: {
      std::string str;
      return prot.readBinary(str);
    }
    case T_STRUCT: {
      uint32_t result = 0;
      std::string name;
      int16_t fid;
      TType ftype;
      result += prot.readStructBegin(name);
      while (true) {
        result += prot.readFieldBegin(name, ftype, fid);
        if (ftype == T_STOP) {
          break;
        }
        result += skip(prot, ftype);
        result += prot.readFieldEnd();
      }
      result += prot.readStructEnd();
      return result;
    }
    case T_MAP: {
      uint32_t result = 0;
      TType keyType;
      TType valType;
      uint32_t i, size;
      bool sizeUnknown;
      result += prot.readMapBegin(keyType, valType, size, sizeUnknown);
      if (!sizeUnknown) {
        for (i = 0; i < size; i++) {
          result += skip(prot, keyType);
          result += skip(prot, valType);
        }
      } else {
        while (prot.peekMap()) {
          result += skip(prot, keyType);
          result += skip(prot, valType);
        }
      }
      result += prot.readMapEnd();
      return result;
    }
    case T_SET: {
      uint32_t result = 0;
      TType elemType;
      uint32_t i, size;
      bool sizeUnknown;
      result += prot.readSetBegin(elemType, size, sizeUnknown);
      if (!sizeUnknown) {
        for (i = 0; i < size; i++) {
          result += skip(prot, elemType);
        }
      } else {
        while (prot.peekSet()) {
          result += skip(prot, elemType);
        }
      }
      result += prot.readSetEnd();
      return result;
    }
    case T_LIST: {
      uint32_t result = 0;
      TType elemType;
      uint32_t i, size;
      bool sizeUnknown;
      result += prot.readListBegin(elemType, size, sizeUnknown);
      if (!sizeUnknown) {
        for (i = 0; i < size; i++) {
          result += skip(prot, elemType);
        }
      } else {
        while (prot.peekList()) {
          result += skip(prot, elemType);
        }
      }
      result += prot.readListEnd();
      return result;
    }
    default: {
      TProtocolException::throwInvalidSkipType(arg_type);
    }
  }
}