From 028d6fefbbd9a917720a9157602806fe157a8953 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sun, 29 Nov 2015 13:46:03 +0100
Subject: [PATCH] lib-sieve: mime/foreverypart: Enforced loop nesting limit
 across includes.

---
 .../plugins/include/ext-include-common.c      |  7 +--
 src/lib-sieve/sieve-interpreter.c             | 45 +++++++++++++------
 src/lib-sieve/sieve-interpreter.h             | 18 +++++---
 src/lib-sieve/sieve.c                         |  4 +-
 src/testsuite/testsuite-script.c              |  4 +-
 src/testsuite/testsuite.c                     |  2 +-
 tests/extensions/mime/errors.svtest           | 13 +++++-
 .../mime/errors/limits-include.sieve          |  6 +++
 .../mime/included/include-loop-2.sieve        |  6 +++
 .../mime/included/include-loop-3.sieve        |  6 +++
 .../mime/included/include-loop-4.sieve        |  6 +++
 .../mime/included/include-loop-5.sieve        |  9 ++++
 12 files changed, 99 insertions(+), 27 deletions(-)
 create mode 100644 tests/extensions/mime/errors/limits-include.sieve
 create mode 100644 tests/extensions/mime/included/include-loop-2.sieve
 create mode 100644 tests/extensions/mime/included/include-loop-3.sieve
 create mode 100644 tests/extensions/mime/included/include-loop-4.sieve
 create mode 100644 tests/extensions/mime/included/include-loop-5.sieve

diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c
index b50acd36a..b40fda9cd 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.c
+++ b/src/lib-sieve/plugins/include/ext-include-common.c
@@ -708,8 +708,8 @@ int ext_include_execute_include
 			 * (first sub-interpreter)
 			 */
 			subinterp = sieve_interpreter_create_for_block
-				(included->block, included->script, renv->msgdata, renv->scriptenv,
-					ehandler, rtflags);
+				(included->block, included->script, renv->interp,
+					renv->msgdata, renv->scriptenv, ehandler, rtflags);
 
 			if ( subinterp != NULL ) {
 				curctx = ext_include_interpreter_context_init_child
@@ -767,7 +767,8 @@ int ext_include_execute_include
 
 							/* Create sub-interpreter */
 							subinterp = sieve_interpreter_create_for_block
-								(curctx->include->block, curctx->include->script, renv->msgdata,
+								(curctx->include->block, curctx->include->script,
+									curctx->interp, renv->msgdata,
 									renv->scriptenv, ehandler, rtflags);
 
 							if ( subinterp != NULL ) {
diff --git a/src/lib-sieve/sieve-interpreter.c b/src/lib-sieve/sieve-interpreter.c
index 5e332156d..7c87e1e5e 100644
--- a/src/lib-sieve/sieve-interpreter.c
+++ b/src/lib-sieve/sieve-interpreter.c
@@ -71,6 +71,7 @@ struct sieve_interpreter {
 	/* Loop stack */
 	ARRAY(struct sieve_interpreter_loop) loop_stack;
 	sieve_size_t loop_limit;
+	unsigned int parent_loop_level;
 
 	/* Runtime environment */
 	struct sieve_runtime_env runenv;
@@ -85,10 +86,15 @@ struct sieve_interpreter {
 };
 
 static struct sieve_interpreter *_sieve_interpreter_create
-(struct sieve_binary *sbin, struct sieve_binary_block *sblock,
-	struct sieve_script *script, const struct sieve_message_data *msgdata,
-	const struct sieve_script_env *senv, struct sieve_error_handler *ehandler,
+(struct sieve_binary *sbin,
+	struct sieve_binary_block *sblock,
+	struct sieve_script *script,
+	struct sieve_interpreter *parent,
+	const struct sieve_message_data *msgdata,
+	const struct sieve_script_env *senv,
+	struct sieve_error_handler *ehandler,
 	enum sieve_runtime_flags flags)
+	ATTR_NULL(3, 4)
 {
 	unsigned int i, ext_count;
 	struct sieve_interpreter *interp;
@@ -143,6 +149,12 @@ static struct sieve_interpreter *_sieve_interpreter_create
 
 	p_array_init(&interp->extensions, pool, sieve_extensions_get_count(svinst));
 
+	interp->parent_loop_level = 0;
+	if ( parent != NULL && array_is_created(&parent->loop_stack) ) {
+		interp->parent_loop_level = parent->parent_loop_level +
+			array_count(&parent->loop_stack);
+	}
+
 	/* Pre-load core language features implemented as 'extensions' */
 	ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count);
 	for ( i = 0; i < ext_count; i++ ) {
@@ -211,8 +223,11 @@ static struct sieve_interpreter *_sieve_interpreter_create
 }
 
 struct sieve_interpreter *sieve_interpreter_create
-(struct sieve_binary *sbin, const struct sieve_message_data *msgdata,
-	const struct sieve_script_env *senv, struct sieve_error_handler *ehandler,
+(struct sieve_binary *sbin,
+	struct sieve_interpreter *parent,
+	const struct sieve_message_data *msgdata,
+	const struct sieve_script_env *senv,
+	struct sieve_error_handler *ehandler,
 	enum sieve_runtime_flags flags)
 {
 	struct sieve_binary_block *sblock;
@@ -221,20 +236,24 @@ struct sieve_interpreter *sieve_interpreter_create
 		== NULL )
 		return NULL;
 
- 	return _sieve_interpreter_create
-		(sbin, sblock, NULL, msgdata, senv, ehandler, flags);
+ 	return _sieve_interpreter_create(sbin, sblock, NULL,
+		parent, msgdata, senv, ehandler, flags);
 }
 
 struct sieve_interpreter *sieve_interpreter_create_for_block
-(struct sieve_binary_block *sblock, struct sieve_script *script,
-	const struct sieve_message_data *msgdata, const struct sieve_script_env *senv,
-	struct sieve_error_handler *ehandler, enum sieve_runtime_flags flags)
+(struct sieve_binary_block *sblock,
+	struct sieve_script *script,
+	struct sieve_interpreter *parent,
+	const struct sieve_message_data *msgdata,
+	const struct sieve_script_env *senv,
+	struct sieve_error_handler *ehandler,
+	enum sieve_runtime_flags flags)
 {
 	if ( sblock == NULL ) return NULL;
 
  	return _sieve_interpreter_create
-		(sieve_binary_block_get_binary(sblock), sblock, script, msgdata, senv,
-			ehandler, flags);
+		(sieve_binary_block_get_binary(sblock), sblock, script,
+			parent, msgdata, senv, ehandler, flags);
 }
 
 void sieve_interpreter_free(struct sieve_interpreter **_interp)
@@ -513,7 +532,7 @@ int sieve_interpreter_loop_start
 
 	if ( !array_is_created(&interp->loop_stack) )
 		p_array_init(&interp->loop_stack, interp->pool, 8);
-	else if ( array_count(&interp->loop_stack)
+	if ( (interp->parent_loop_level + array_count(&interp->loop_stack))
 		>= SIEVE_MAX_LOOP_DEPTH ) {
 		/* Should normally be caught at compile time */
 		sieve_runtime_error(renv, NULL,
diff --git a/src/lib-sieve/sieve-interpreter.h b/src/lib-sieve/sieve-interpreter.h
index bace3155f..f2d96380b 100644
--- a/src/lib-sieve/sieve-interpreter.h
+++ b/src/lib-sieve/sieve-interpreter.h
@@ -16,14 +16,22 @@
  */
 
 struct sieve_interpreter *sieve_interpreter_create
-	(struct sieve_binary *sbin, const struct sieve_message_data *msgdata,
-		const struct sieve_script_env *senv, struct sieve_error_handler *ehandler,
-		enum sieve_runtime_flags flags);
+	(struct sieve_binary *sbin,
+		struct sieve_interpreter *parent,
+		const struct sieve_message_data *msgdata,
+		const struct sieve_script_env *senv,
+		struct sieve_error_handler *ehandler,
+		enum sieve_runtime_flags flags)
+	ATTR_NULL(2);
 struct sieve_interpreter *sieve_interpreter_create_for_block
-	(struct sieve_binary_block *sblock, struct sieve_script *script,
+	(struct sieve_binary_block *sblock,
+		struct sieve_script *script,
+		struct sieve_interpreter *parent,
 		const struct sieve_message_data *msgdata,
-		const struct sieve_script_env *senv, struct sieve_error_handler *ehandler,
+		const struct sieve_script_env *senv,
+		struct sieve_error_handler *ehandler,
 		enum sieve_runtime_flags flags);
+	ATTR_NULL(3);
 void sieve_interpreter_free(struct sieve_interpreter **_interp);
 
 /*
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index c5a4fe977..de30e1862 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -323,8 +323,8 @@ static int sieve_run
 	int ret = 0;
 
 	/* Create the interpreter */
-	if ( (interp=sieve_interpreter_create(sbin, msgdata, senv, ehandler, flags))
-		== NULL )
+	if ( (interp=sieve_interpreter_create
+		(sbin, NULL, msgdata, senv, ehandler, flags)) == NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
 
 	/* Reset execution status */
diff --git a/src/testsuite/testsuite-script.c b/src/testsuite/testsuite-script.c
index f653b2014..095fb3067 100644
--- a/src/testsuite/testsuite-script.c
+++ b/src/testsuite/testsuite-script.c
@@ -122,8 +122,8 @@ bool testsuite_script_run(const struct sieve_runtime_env *renv)
 	result = testsuite_result_get();
 
 	/* Execute the script */
-	interp=sieve_interpreter_create(ictx->compiled_script, renv->msgdata,
-		&scriptenv, testsuite_log_ehandler, 0);
+	interp=sieve_interpreter_create(ictx->compiled_script,
+		NULL, renv->msgdata, &scriptenv, testsuite_log_ehandler, 0);
 
 	if ( interp == NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c
index 38786b8cf..fd5d0f563 100644
--- a/src/testsuite/testsuite.c
+++ b/src/testsuite/testsuite.c
@@ -65,7 +65,7 @@ static int testsuite_run
 
 	/* Create the interpreter */
 	if ( (interp=sieve_interpreter_create
-		(sbin, msgdata, senv, ehandler, 0)) == NULL )
+		(sbin, NULL, msgdata, senv, ehandler, 0)) == NULL )
 		return SIEVE_EXEC_BIN_CORRUPT;
 
 	/* Run the interpreter */
diff --git a/tests/extensions/mime/errors.svtest b/tests/extensions/mime/errors.svtest
index 2a183b5e4..31235264e 100644
--- a/tests/extensions/mime/errors.svtest
+++ b/tests/extensions/mime/errors.svtest
@@ -3,7 +3,7 @@ require "vnd.dovecot.testsuite";
 require "relational";
 require "comparator-i;ascii-numeric";
 
-test "Foreverypart command" {
+/*test "Foreverypart command" {
 	if test_script_compile "errors/foreverypart.sieve" {
 		test_fail "compile should have failed";
 	}
@@ -61,4 +61,15 @@ test "Limits" {
 	if test_error :count "ne" :comparator "i;ascii-numeric" "2" {
 		test_fail "incorrect number of compile errors reported";
 	}
+}*/
+
+test "Limits - include" {
+	if not test_script_compile "errors/limits-include.sieve" {
+		test_fail "script compile failed";
+	}
+
+	if test_script_run {
+		test_fail "script run should have failed";
+	}
 }
+
diff --git a/tests/extensions/mime/errors/limits-include.sieve b/tests/extensions/mime/errors/limits-include.sieve
new file mode 100644
index 000000000..ef924563c
--- /dev/null
+++ b/tests/extensions/mime/errors/limits-include.sieve
@@ -0,0 +1,6 @@
+require "foreverypart";
+require "include";
+
+foreverypart :name "frop" {
+	include "include-loop-2";
+}
diff --git a/tests/extensions/mime/included/include-loop-2.sieve b/tests/extensions/mime/included/include-loop-2.sieve
new file mode 100644
index 000000000..80c588454
--- /dev/null
+++ b/tests/extensions/mime/included/include-loop-2.sieve
@@ -0,0 +1,6 @@
+require "foreverypart";
+require "include";
+
+foreverypart :name "friep" {
+	include "include-loop-3";
+}
diff --git a/tests/extensions/mime/included/include-loop-3.sieve b/tests/extensions/mime/included/include-loop-3.sieve
new file mode 100644
index 000000000..228a8bc34
--- /dev/null
+++ b/tests/extensions/mime/included/include-loop-3.sieve
@@ -0,0 +1,6 @@
+require "foreverypart";
+require "include";
+
+foreverypart :name "frml" {
+	include "include-loop-4";
+}
diff --git a/tests/extensions/mime/included/include-loop-4.sieve b/tests/extensions/mime/included/include-loop-4.sieve
new file mode 100644
index 000000000..00dad847d
--- /dev/null
+++ b/tests/extensions/mime/included/include-loop-4.sieve
@@ -0,0 +1,6 @@
+require "foreverypart";
+require "include";
+
+foreverypart {
+	include "include-loop-5";
+}
diff --git a/tests/extensions/mime/included/include-loop-5.sieve b/tests/extensions/mime/included/include-loop-5.sieve
new file mode 100644
index 000000000..e22b21cad
--- /dev/null
+++ b/tests/extensions/mime/included/include-loop-5.sieve
@@ -0,0 +1,9 @@
+require "foreverypart";
+require "include";
+require "mime";
+
+foreverypart {
+	if header :mime :subtype "content-type" "plain" {
+		break;
+	}
+}
-- 
GitLab