CVE-2022-3222: Uncontrolled Recursion (CWE-674)
value: Uncontrolled Recursion in GitHub repository gpac/gpac prior to 2.1.0-DEV.
CWE description: The product does not properly control the amount of recursion that takes place,  consuming excessive resources, such as allocated memory or the program stack.
CVSS v3 base score: 5.5
Published: 2022-09-15T09:15Z
Repository: https://github.com/gpac/gpac
Language: Other
Files changed: build/xcode/gpac.xcodeproj/project.pbxproj, src/bifs/script_dec.c
Commit date: 2022-09-12 10:54:16 +0200
Commit message: fixed #2238

Diff: diff --git a/build/xcode/gpac.xcodeproj/project.pbxproj b/build/xcode/gpac.xcodeproj/project.pbxproj
index 26daf1a13dac..01a54f59360a 100644
--- a/build/xcode/gpac.xcodeproj/project.pbxproj
+++ b/build/xcode/gpac.xcodeproj/project.pbxproj
@@ -3337,11 +3337,11 @@
 				INSTALL_PATH = "";
 				LIBRARY_SEARCH_PATHS = (
 					/usr/local/lib,
 					/opt/local/lib,
 				);
-				MACOSX_DEPLOYMENT_TARGET = 10.6;
+				MACOSX_DEPLOYMENT_TARGET = 10.9;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "-lSDL2";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx;
 			};
@@ -3376,11 +3376,11 @@
 				INSTALL_PATH = "";
 				LIBRARY_SEARCH_PATHS = (
 					/usr/local/lib,
 					/opt/local/lib,
 				);
-				MACOSX_DEPLOYMENT_TARGET = 10.6;
+				MACOSX_DEPLOYMENT_TARGET = 10.9;
 				OTHER_LDFLAGS = "-lSDL2";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx;
 			};
 			name = Release;
diff --git a/src/bifs/script_dec.c b/src/bifs/script_dec.c
index 81613ed4559c..873d8c9ca7ab 100644
--- a/src/bifs/script_dec.c
+++ b/src/bifs/script_dec.c
@@ -38,10 +38,11 @@ typedef struct
 	char *string;
 	u32 length;
 	GF_List *identifiers;
 	char *new_line;
 	u32 indent;
+	u32 expr_stack_size;
 } ScriptParser;
 
 
 void SFS_Identifier(ScriptParser *parser);
 void SFS_Arguments(ScriptParser *parser, Bool is_var);
@@ -167,10 +168,11 @@ GF_Err SFScript_Parse(GF_BifsDecoder *codec, SFScript *script_field, GF_BitStrea
 	char *ptr;
 	ScriptParser parser;
 	e = GF_OK;
 	if (gf_node_get_tag(n) != TAG_MPEG4_Script) return GF_NON_COMPLIANT_BITSTREAM;
 
+	memset(&parser, 0, sizeof(ScriptParser));
 	parser.codec = codec;
 	parser.script = n;
 	parser.bs = bs;
 	parser.length = 500;
 	parser.string = (char *) gf_malloc(sizeof(char)* parser.length);
@@ -207,10 +209,14 @@ GF_Err SFScript_Parse(GF_BifsDecoder *codec, SFScript *script_field, GF_BitStrea
 		SFS_Identifier(&parser);
 		SFS_Arguments(&parser, GF_FALSE);
 		SFS_Space(&parser);
 		SFS_StatementBlock(&parser, GF_TRUE);
 		SFS_Line(&parser);
+		if (codec->LastError) {
+			e = codec->LastError;
+			goto exit;
+		}
 	}
 
 	SFS_Line(&parser);
 
 	if (script_field->script_text) gf_free(script_field->script_text);
@@ -416,10 +422,11 @@ void SFS_ReturnStatement(ScriptParser *parser)
 void SFS_CompoundExpression(ScriptParser *parser)
 {
 	if (parser->codec->LastError) return;
 	SFS_Expression(parser);
 	if (! gf_bs_read_int(parser->bs, 1)) return;
+	if (parser->codec->LastError) return;
 	SFS_AddString(parser, ",");
 	SFS_CompoundExpression(parser);
 }
 
 void SFS_OptionalExpression(ScriptParser *parser)
@@ -428,16 +435,24 @@ void SFS_OptionalExpression(ScriptParser *parser)
 	if (gf_bs_read_int(parser->bs, 1)) {
 		SFS_CompoundExpression(parser);
 	}
 }
 
-
+#define MAX_EXPR_STACK	500
 void SFS_Expression(ScriptParser *parser)
 {
 	u32 val = gf_bs_read_int(parser->bs, NUMBITS_EXPR_TYPE);
 	if (parser->codec->LastError) return;
 
+	//limit max expression stack size
+	parser->expr_stack_size++;
+	if (parser->expr_stack_size>MAX_EXPR_STACK) {
+		GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[BIFS] Max stack size %d reached for expressions, not supported\n", MAX_EXPR_STACK))
+		parser->codec->LastError = GF_NON_COMPLIANT_BITSTREAM;
+		return;
+	}
+
 	switch(val) {
 	case ET_CURVED_EXPR:
 		SFS_AddString(parser, "(");
 		SFS_CompoundExpression(parser);
 		SFS_AddString(parser, ")");
@@ -673,10 +688,11 @@ void SFS_Expression(ScriptParser *parser)
 		break;
 	default:
 		parser->codec->LastError = GF_NON_COMPLIANT_BITSTREAM;
 		break;
 	}
+	parser->expr_stack_size--;
 }
 
 void SFS_NewObject(ScriptParser *parser)
 {
 	if (parser->codec->LastError) return;
@@ -689,10 +705,11 @@ void SFS_NewObject(ScriptParser *parser)
 
 void SFS_ArrayDeref(ScriptParser *parser)
 {
 	if (parser->codec->LastError) return;
 	SFS_Expression(parser);
+	if (parser->codec->LastError) return;
 	SFS_AddString(parser, "[");
 	SFS_CompoundExpression(parser);
 	SFS_AddString(parser, "]");
 }
 
@@ -707,19 +724,21 @@ void SFS_FunctionCall(ScriptParser *parser)
 
 void SFS_ObjectMemberAccess(ScriptParser *parser)
 {
 	if (parser->codec->LastError) return;
 	SFS_Expression(parser);
+	if (parser->codec->LastError) return;
 	SFS_AddString(parser, ".");
 	SFS_Identifier(parser);
 }
 
 
 void SFS_ObjectMethodCall(ScriptParser *parser)
 {
 	if (parser->codec->LastError) return;
 	SFS_Expression(parser);
+	if (parser->codec->LastError) return;
 	SFS_AddString(parser, ".");
 	SFS_Identifier(parser);
 	SFS_AddString(parser, "(");
 	SFS_Params(parser);
 	SFS_AddString(parser, ")");
@@ -730,10 +749,11 @@ void SFS_Params(ScriptParser *parser)
 	u32 val;
 	if (parser->codec->LastError) return;
 	val = gf_bs_read_int(parser->bs, 1);
 	while (val) {
 		SFS_Expression(parser);
+		if (parser->codec->LastError) return;
 		val = gf_bs_read_int(parser->bs, 1);
 		if(val) SFS_AddString(parser, ",");
 	}
 }
 

Vulnerable code: 				MACOSX_DEPLOYMENT_TARGET = 10.6;
				MACOSX_DEPLOYMENT_TARGET = 10.6;

Fixed code: 				MACOSX_DEPLOYMENT_TARGET = 10.9;
				MACOSX_DEPLOYMENT_TARGET = 10.9;
	u32 expr_stack_size;
	memset(&parser, 0, sizeof(ScriptParser));
		if (codec->LastError) {
			e = codec->LastError;
			goto exit;
		}
	if (parser->codec->LastError) return;
#define MAX_EXPR_STACK	500
	//limit max expression stack size
	parser->expr_stack_size++;
	if (parser->expr_stack_size>MAX_EXPR_STACK) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[BIFS] Max stack size %d reached for expressions, not supported\n", MAX_EXPR_STACK))
		parser->codec->LastError = GF_NON_COMPLIANT_BITSTREAM;
		return;
	}

	parser->expr_stack_size--;
	if (parser->codec->LastError) return;
	if (parser->codec->LastError) return;
	if (parser->codec->LastError) return;
		if (parser->codec->LastError) return;