CVE-2018-20761: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: GPAC version 0.7.1 and earlier has a Buffer Overflow vulnerability in the gf_sm_load_init function in scene_manager.c in libgpac_static.a.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 7.8
Published: 2019-02-06T23:29Z
Repository: https://github.com/gpac/gpac
Language: C
Files changed: applications/mp4box/fileimport.c, applications/mp4client/main.c, modules/ffmpeg_in/ffmpeg_demux.c, src/scene_manager/scene_manager.c
Commit date: 2019-01-11 12:11:45 +0100
Commit message: fix some overflows due to strcpy

fixes #1184, #1186, #1187 among other things

Diff: diff --git a/applications/mp4box/fileimport.c b/applications/mp4box/fileimport.c
index a1bc80b9d2..d3c878a854 100644
--- a/applications/mp4box/fileimport.c
+++ b/applications/mp4box/fileimport.c
@@ -2354,28 +2354,48 @@ GF_Err cat_multiple_files(GF_ISOFile *dest, char *fileName, u32 import_flags, Do
 	cat_enum.tmp_dir = tmp_dir;
 	cat_enum.force_cat = force_cat;
 	cat_enum.align_timelines = align_timelines;
 	cat_enum.allow_add_in_command = allow_add_in_command;
 
+	if (strlen(fileName) >= sizeof(cat_enum.szPath)) {
+		GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("File name %s is too long.\n", fileName));
+		return GF_NOT_SUPPORTED;
+	}
 	strcpy(cat_enum.szPath, fileName);
 	sep = strrchr(cat_enum.szPath, GF_PATH_SEPARATOR);
 	if (!sep) sep = strrchr(cat_enum.szPath, '/');
 	if (!sep) {
 		strcpy(cat_enum.szPath, ".");
+		if (strlen(fileName) >= sizeof(cat_enum.szRad1)) {
+			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("File name %s is too long.\n", fileName));
+			return GF_NOT_SUPPORTED;
+		}
 		strcpy(cat_enum.szRad1, fileName);
 	} else {
+		if (strlen(sep + 1) >= sizeof(cat_enum.szRad1)) {
+			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("File name %s is too long.\n", (sep + 1)));
+			return GF_NOT_SUPPORTED;
+		}
 		strcpy(cat_enum.szRad1, sep+1);
 		sep[0] = 0;
 	}
 	sep = strchr(cat_enum.szRad1, '*');
+	if (strlen(sep + 1) >= sizeof(cat_enum.szRad2)) {
+		GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("File name %s is too long.\n", (sep + 1)));
+		return GF_NOT_SUPPORTED;
+	}
 	strcpy(cat_enum.szRad2, sep+1);
 	sep[0] = 0;
 	sep = strchr(cat_enum.szRad2, '%');
 	if (!sep) sep = strchr(cat_enum.szRad2, '#');
 	if (!sep) sep = strchr(cat_enum.szRad2, ':');
 	strcpy(cat_enum.szOpt, "");
 	if (sep) {
+		if (strlen(sep) >= sizeof(cat_enum.szOpt)) {
+			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Invalid option: %s.\n", sep));
+			return GF_NOT_SUPPORTED;
+		}
 		strcpy(cat_enum.szOpt, sep);
 		sep[0] = 0;
 	}
 	return gf_enum_directory(cat_enum.szPath, 0, cat_enumerate, &cat_enum, NULL);
 }
diff --git a/applications/mp4client/main.c b/applications/mp4client/main.c
index bda694d276..46404ff985 100644
--- a/applications/mp4client/main.c
+++ b/applications/mp4client/main.c
@@ -908,11 +908,12 @@ Bool GPAC_EventProc(void *ptr, GF_Event *evt)
 	case GF_EVENT_NAVIGATE_INFO:
 		if (evt->navigate.to_url) fprintf(stderr, "Go to URL: \"%s\"\r", evt->navigate.to_url);
 		break;
 	case GF_EVENT_NAVIGATE:
 		if (gf_term_is_supported_url(term, evt->navigate.to_url, 1, no_mime_check)) {
-			strcpy(the_url, evt->navigate.to_url);
+			strncpy(the_url, evt->navigate.to_url, sizeof(the_url)-1);
+			the_url[sizeof(the_url) - 1] = 0;
 			fprintf(stderr, "Navigating to URL %s\n", the_url);
 			gf_term_navigate_to(term, evt->navigate.to_url);
 			return 1;
 		} else {
 			fprintf(stderr, "Navigation destination not supported\nGo to URL: %s\n", evt->navigate.to_url);
@@ -1097,10 +1098,15 @@ void set_cfg_option(char *opt_string)
 		fprintf(stderr, "Badly formatted option %s - expected Section:Name=Value\n", opt_string);
 		return;
 	}
 	{
 		const size_t sepIdx = sep - opt_string;
+		if (sepIdx >= sizeof(szSec)) {
+			fprintf(stderr, "Badly formatted option %s - Section name is too long\n", opt_string);
+			return;
+		}
+
 		strncpy(szSec, opt_string, sepIdx);
 		szSec[sepIdx] = 0;
 	}
 	sep ++;
 	sep2 = strchr(sep, '=');
@@ -1108,12 +1114,20 @@ void set_cfg_option(char *opt_string)
 		fprintf(stderr, "Badly formatted option %s - expected Section:Name=Value\n", opt_string);
 		return;
 	}
 	{
 		const size_t sepIdx = sep2 - sep;
+		if (sepIdx >= sizeof(szKey)) {
+			fprintf(stderr, "Badly formatted option %s - key name is too long\n", opt_string);
+			return;
+		}
 		strncpy(szKey, sep, sepIdx);
 		szKey[sepIdx] = 0;
+		if (strlen(sep2 + 1) >= sizeof(szVal)) {
+			fprintf(stderr, "Badly formatted option %s - value is too long\n", opt_string);
+			return;
+		}
 		strcpy(szVal, sep2+1);
 	}
 
 	if (!stricmp(szKey, "*")) {
 		if (stricmp(szVal, "null")) {
@@ -1678,11 +1692,18 @@ int mp4client_main(int argc, char **argv)
 	}
 	/*connect if requested*/
 	else if (!gui_mode && url_arg) {
 		char *ext;
 
-		strcpy(the_url, url_arg);
+		if (strlen(url_arg) >= sizeof(the_url)) {
+			fprintf(stderr, "Input url %s is too long, truncating to %d chars.\n", url_arg, (int)(sizeof(the_url) - 1));
+			strncpy(the_url, url_arg, sizeof(the_url)-1);
+			the_url[sizeof(the_url) - 1] = 0;
+		}
+		else {
+			strcpy(the_url, url_arg);
+		}
 		ext = strrchr(the_url, '.');
 		if (ext && (!stricmp(ext, ".m3u") || !stricmp(ext, ".pls"))) {
 			GF_Err e = GF_OK;
 			fprintf(stderr, "Opening Playlist %s\n", the_url);
 
@@ -1690,11 +1711,14 @@ int mp4client_main(int argc, char **argv)
 			/*this is not clean, we need to have a plugin handle playlist for ourselves*/
 			if (!strncmp("http:", the_url, 5)) {
 				GF_DownloadSession *sess = gf_dm_sess_new(term->downloader, the_url, GF_NETIO_SESSION_NOT_THREADED, NULL, NULL, &e);
 				if (sess) {
 					e = gf_dm_sess_process(sess);
-					if (!e) strcpy(the_url, gf_dm_sess_get_cache_name(sess));
+					if (!e) {
+						strncpy(the_url, gf_dm_sess_get_cache_name(sess), sizeof(the_url) - 1);
+						the_url[sizeof(the_cfg) - 1] = 0;
+					}
 					gf_dm_sess_del(sess);
 				}
 			}
 
 			playlist = e ? NULL : gf_fopen(the_url, "rt");
@@ -1713,11 +1737,12 @@ int mp4client_main(int argc, char **argv)
 		}
 	} else {
 		fprintf(stderr, "Hit 'h' for help\n\n");
 		str = gf_cfg_get_key(cfg_file, "General", "StartupFile");
 		if (str) {
-			strcpy(the_url, "MP4Client "GPAC_FULL_VERSION);
+			strncpy(the_url, "MP4Client "GPAC_FULL_VERSION , sizeof(the_url)-1);
+			the_url[sizeof(the_url) - 1] = 0;
 			gf_term_connect(term, str);
 			startup_file = 1;
 			is_connected = 1;
 		}
 	}
diff --git a/modules/ffmpeg_in/ffmpeg_demux.c b/modules/ffmpeg_in/ffmpeg_demux.c
index 7acdae38ad..fbd61af9f2 100644
--- a/modules/ffmpeg_in/ffmpeg_demux.c
+++ b/modules/ffmpeg_in/ffmpeg_demux.c
@@ -225,11 +225,11 @@ static Bool FFD_CanHandleURL(GF_InputService *plug, const char *url)
 	Bool has_audio, has_video;
 	s32 i;
 	AVFormatContext *ctx;
 	AVOutputFormat *fmt_out;
 	Bool ret = GF_FALSE;
-	char *ext, szName[1000], szExt[20];
+	char *ext, szName[1024], szExt[20];
 	const char *szExtList;
 	FFDemux *ffd;
 	if (!plug || !url)
 		return GF_FALSE;
 	/*disable RTP/RTSP from ffmpeg*/
@@ -241,20 +241,23 @@ static Bool FFD_CanHandleURL(GF_InputService *plug, const char *url)
 	if (!strnicmp(url, "tcp://", 6)) return GF_FALSE;
 	if (!strnicmp(url, "data:", 5)) return GF_FALSE;
 
 	ffd = (FFDemux*)plug->priv;
 
+	if (strlen(url) >= sizeof(szName))
+		return GF_FALSE;
+
 	strcpy(szName, url);
 	ext = strrchr(szName, '#');
 	if (ext) ext[0] = 0;
 	ext = strrchr(szName, '?');
 	if (ext) ext[0] = 0;
 
 	ext = strrchr(szName, '.');
 	if (ext && strlen(ext) > 19) ext = NULL;
 
-	if (ext && strlen(ext) > 1) {
+	if (ext && strlen(ext) > 1 && strlen(ext) <= sizeof(szExt)) {
 		strcpy(szExt, &ext[1]);
 		strlwr(szExt);
 #ifndef FFMPEG_DEMUX_ENABLE_MPEG2TS
 		if (strstr("ts m2t mts dmb trp", szExt) ) return GF_FALSE;
 #endif
diff --git a/src/scene_manager/scene_manager.c b/src/scene_manager/scene_manager.c
index ed44b0a9f3..0b21a400db 100644
--- a/src/scene_manager/scene_manager.c
+++ b/src/scene_manager/scene_manager.c
@@ -644,10 +644,14 @@ GF_Err gf_sm_load_init(GF_SceneLoader *load)
 				ext[0] = 0;
 				anext = (char *)strrchr(load->fileName, '.');
 				ext[0] = '.';
 				ext = anext;
 			}
+			if (strlen(ext) < 2 || strlen(ext) > sizeof(szExt)) {
+				GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene Manager] invalid extension in file name %s\n", load->fileName));
+				return GF_NOT_SUPPORTED;
+			}
 			strcpy(szExt, &ext[1]);
 			strlwr(szExt);
 			if (strstr(szExt, "bt")) load->type = GF_SM_LOAD_BT;
 			else if (strstr(szExt, "wrl")) load->type = GF_SM_LOAD_VRML;
 			else if (strstr(szExt, "x3dv")) load->type = GF_SM_LOAD_X3DV;

Vulnerable code: 			strcpy(the_url, evt->navigate.to_url);
		strcpy(the_url, url_arg);
					if (!e) strcpy(the_url, gf_dm_sess_get_cache_name(sess));
			strcpy(the_url, "MP4Client "GPAC_FULL_VERSION);
	char *ext, szName[1000], szExt[20];
	if (ext && strlen(ext) > 1) {
Fixed code: 	if (strlen(fileName) >= sizeof(cat_enum.szPath)) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("File name %s is too long.\n", fileName));
		return GF_NOT_SUPPORTED;
	}
		if (strlen(fileName) >= sizeof(cat_enum.szRad1)) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("File name %s is too long.\n", fileName));
			return GF_NOT_SUPPORTED;
		}
		if (strlen(sep + 1) >= sizeof(cat_enum.szRad1)) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("File name %s is too long.\n", (sep + 1)));
			return GF_NOT_SUPPORTED;
		}
	if (strlen(sep + 1) >= sizeof(cat_enum.szRad2)) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("File name %s is too long.\n", (sep + 1)));
		return GF_NOT_SUPPORTED;
	}
		if (strlen(sep) >= sizeof(cat_enum.szOpt)) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Invalid option: %s.\n", sep));
			return GF_NOT_SUPPORTED;
		}
			strncpy(the_url, evt->navigate.to_url, sizeof(the_url)-1);
			the_url[sizeof(the_url) - 1] = 0;
		if (sepIdx >= sizeof(szSec)) {
			fprintf(stderr, "Badly formatted option %s - Section name is too long\n", opt_string);
			return;
		}

		if (sepIdx >= sizeof(szKey)) {
			fprintf(stderr, "Badly formatted option %s - key name is too long\n", opt_string);
			return;
		}
		if (strlen(sep2 + 1) >= sizeof(szVal)) {
			fprintf(stderr, "Badly formatted option %s - value is too long\n", opt_string);
			return;
		}
		if (strlen(url_arg) >= sizeof(the_url)) {
			fprintf(stderr, "Input url %s is too long, truncating to %d chars.\n", url_arg, (int)(sizeof(the_url) - 1));
			strncpy(the_url, url_arg, sizeof(the_url)-1);
			the_url[sizeof(the_url) - 1] = 0;
		}
		else {
			strcpy(the_url, url_arg);
		}
					if (!e) {
						strncpy(the_url, gf_dm_sess_get_cache_name(sess), sizeof(the_url) - 1);
						the_url[sizeof(the_cfg) - 1] = 0;
					}
			strncpy(the_url, "MP4Client "GPAC_FULL_VERSION , sizeof(the_url)-1);
			the_url[sizeof(the_url) - 1] = 0;
	char *ext, szName[1024], szExt[20];
	if (strlen(url) >= sizeof(szName))
		return GF_FALSE;

	if (ext && strlen(ext) > 1 && strlen(ext) <= sizeof(szExt)) {
			if (strlen(ext) < 2 || strlen(ext) > sizeof(szExt)) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene Manager] invalid extension in file name %s\n", load->fileName));
				return GF_NOT_SUPPORTED;
			}