From: Bram Moolenaar <Bram@vim.org>
Date: Mon, 24 Jan 2022 18:16:12 +0000
Subject: [PATCH] patch 8.2.4206: condition with many "(" causes a crash

Problem:    Condition with many "(" causes a crash.
Solution:   Limit recursion to 1000.
---
 src/eval.c                      | 12 ++++++++++++
 src/globals.h                   |  2 ++
 src/testdir/test_eval_stuff.vim |  5 +++++
 3 files changed, 19 insertions(+)

diff --git a/src/eval.c b/src/eval.c
index 3b563f7..95dda90 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -2495,6 +2495,7 @@ eval7(
     char_u	*start_leader, *end_leader;
     int		ret = OK;
     char_u	*alias;
+    static	int recurse = 0;
 
     /*
      * Initialise variable so that clear_tv() can't mistake this for a
@@ -2521,6 +2522,15 @@ eval7(
 	return FAIL;
     }
 
+    // Limit recursion to 1000 levels.  At least at 10000 we run out of stack
+    // and crash.
+    if (recurse == 1000)
+    {
+	semsg(_(e_expression_too_recursive_str), *arg);
+	return FAIL;
+    }
+    ++recurse;
+
     switch (**arg)
     {
     /*
@@ -2761,6 +2771,8 @@ eval7(
      */
     if (ret == OK && evaluate && end_leader > start_leader)
 	ret = eval7_leader(rettv, start_leader, &end_leader);
+
+    --recurse;
     return ret;
 }
 
diff --git a/src/globals.h b/src/globals.h
index 75092b7..659bad6 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1464,6 +1464,8 @@ EXTERN char e_endwhile[]	INIT(= N_("E170: Missing :endwhile"));
 EXTERN char e_endfor[]	INIT(= N_("E170: Missing :endfor"));
 EXTERN char e_while[]		INIT(= N_("E588: :endwhile without :while"));
 EXTERN char e_for[]		INIT(= N_("E588: :endfor without :for"));
+EXTERN char e_expression_too_recursive_str[]
+       INIT(= N_("E1169: Expression too recursive: %s"));
 #endif
 EXTERN char e_exists[]	INIT(= N_("E13: File exists (add ! to override)"));
 EXTERN char e_failed[]	INIT(= N_("E472: Command failed"));
diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim
index ec566da..32a5411 100644
--- a/src/testdir/test_eval_stuff.vim
+++ b/src/testdir/test_eval_stuff.vim
@@ -216,3 +216,8 @@ func Test_scriptversion_fail()
   call assert_fails('source Xversionscript', 'E999:')
   call delete('Xversionscript')
 endfunc
+
+func Test_deep_recursion()
+  " this was running out of stack
+  call assert_fails("exe 'if ' .. repeat('(', 1002)", 'E1169: Expression too recursive: ((')
+endfunc
-- 
1.8.3.1

