summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-11-17 18:23:19 +0100
committerBram Moolenaar <Bram@vim.org>2020-11-17 18:23:19 +0100
commit0ba48e8c2741bd65d547fe6bf1d9873f411b25b4 (patch)
tree829cfacb8d529de2a8c32ba7035b44e3f8a92caa
parentfc74d03e7694bac3b50d8d6b6b78b40a71818744 (diff)
patch 8.2.2001: Vim9: :def function does not apply 'maxfuncdepth'v8.2.2001
Problem: Vim9: :def function does not apply 'maxfuncdepth'. Solution: Use 'maxfuncdepth'. (issue #7313)
-rw-r--r--src/proto/userfunc.pro4
-rw-r--r--src/testdir/test_vim9_func.vim30
-rw-r--r--src/userfunc.c56
-rw-r--r--src/version.c2
-rw-r--r--src/vim9execute.c15
5 files changed, 99 insertions, 8 deletions
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index 5bc7a4b90f..7ee36dbc18 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -14,6 +14,10 @@ ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx);
int func_is_global(ufunc_T *ufunc);
int func_name_refcount(char_u *name);
void copy_func(char_u *lambda, char_u *global);
+int funcdepth_increment(void);
+void funcdepth_decrement(void);
+int funcdepth_get(void);
+void funcdepth_restore(int depth);
int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
void save_funccal(funccal_entry_T *entry);
void restore_funccal(void);
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index a3f21491f2..bbfce23c4a 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -49,6 +49,36 @@ def TestCompilingError()
call delete('XTest_compile_error')
enddef
+def CallRecursive(n: number): number
+ return CallRecursive(n + 1)
+enddef
+
+def CallMapRecursive(l: list<number>): number
+ return map(l, {_, v -> CallMapRecursive([v])})[0]
+enddef
+
+def Test_funcdepth_error()
+ set maxfuncdepth=10
+
+ var caught = false
+ try
+ CallRecursive(1)
+ catch /E132:/
+ caught = true
+ endtry
+ assert_true(caught)
+
+ caught = false
+ try
+ CallMapRecursive([1])
+ catch /E132:/
+ caught = true
+ endtry
+ assert_true(caught)
+
+ set maxfuncdepth&
+enddef
+
def ReturnString(): string
return 'string'
enddef
diff --git a/src/userfunc.c b/src/userfunc.c
index 6e780ea011..7a306b1b7f 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -1373,6 +1373,50 @@ failed:
func_clear_free(fp, TRUE);
}
+static int funcdepth = 0;
+
+/*
+ * Increment the function call depth count.
+ * Return FAIL when going over 'maxfuncdepth'.
+ * Otherwise return OK, must call funcdepth_decrement() later!
+ */
+ int
+funcdepth_increment(void)
+{
+ if (funcdepth >= p_mfd)
+ {
+ emsg(_("E132: Function call depth is higher than 'maxfuncdepth'"));
+ return FAIL;
+ }
+ ++funcdepth;
+ return OK;
+}
+
+ void
+funcdepth_decrement(void)
+{
+ --funcdepth;
+}
+
+/*
+ * Get the current function call depth.
+ */
+ int
+funcdepth_get(void)
+{
+ return funcdepth;
+}
+
+/*
+ * Restore the function call depth. This is for cases where there is no
+ * garantee funcdepth_decrement() can be called exactly the same number of
+ * times as funcdepth_increment().
+ */
+ void
+funcdepth_restore(int depth)
+{
+ funcdepth = depth;
+}
/*
* Call a user function.
@@ -1391,7 +1435,6 @@ call_user_func(
funccall_T *fc;
int save_did_emsg;
int default_arg_err = FALSE;
- static int depth = 0;
dictitem_T *v;
int fixvar_idx = 0; // index in fixvar[]
int i;
@@ -1406,15 +1449,13 @@ call_user_func(
#endif
ESTACK_CHECK_DECLARATION
- // If depth of calling is getting too high, don't execute the function
- if (depth >= p_mfd)
+ // If depth of calling is getting too high, don't execute the function.
+ if (funcdepth_increment() == FAIL)
{
- emsg(_("E132: Function call depth is higher than 'maxfuncdepth'"));
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = -1;
return;
}
- ++depth;
line_breakcheck(); // check for CTRL-C hit
@@ -1437,7 +1478,7 @@ call_user_func(
{
// Execute the function, possibly compiling it first.
call_def_function(fp, argcount, argvars, funcexe->partial, rettv);
- --depth;
+ funcdepth_decrement();
current_funccal = fc->caller;
free_funccal(fc);
return;
@@ -1783,8 +1824,7 @@ call_user_func(
}
did_emsg |= save_did_emsg;
- --depth;
-
+ funcdepth_decrement();
cleanup_function_call(fc);
}
diff --git a/src/version.c b/src/version.c
index a988b21dc1..4b91cf1ca1 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2001,
+/**/
2000,
/**/
1999,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index d8f9cfe02d..a7d83b47a7 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -227,6 +227,10 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
== FAIL)
return FAIL;
+ // If depth of calling is getting too high, don't execute the function.
+ if (funcdepth_increment() == FAIL)
+ return FAIL;
+
// Move the vararg-list to below the missing optional arguments.
if (vararg_count > 0 && arg_to_add > 0)
*STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1);
@@ -503,6 +507,7 @@ func_return(ectx_T *ectx)
ectx->ec_stack.ga_len = top + 1;
*STACK_TV_BOT(-1) = *STACK_TV(idx);
+ funcdepth_decrement();
return OK;
}
@@ -835,6 +840,7 @@ call_def_function(
cmdmod_T save_cmdmod;
int restore_cmdmod = FALSE;
int trylevel_at_start = trylevel;
+ int orig_funcdepth;
// Get pointer to item in the stack.
#define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx)
@@ -870,11 +876,19 @@ call_def_function(
}
}
+ // If depth of calling is getting too high, don't execute the function.
+ orig_funcdepth = funcdepth_get();
+ if (funcdepth_increment() == FAIL)
+ return FAIL;
+
CLEAR_FIELD(ectx);
ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx;
ga_init2(&ectx.ec_stack, sizeof(typval_T), 500);
if (ga_grow(&ectx.ec_stack, 20) == FAIL)
+ {
+ funcdepth_decrement();
return FAIL;
+ }
ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10);
ga_init2(&ectx.ec_funcrefs, sizeof(partial_T *), 10);
@@ -2941,6 +2955,7 @@ failed_early:
if (ret != OK && did_emsg == did_emsg_before)
semsg(_(e_unknown_error_while_executing_str),
printable_func_name(ufunc));
+ funcdepth_restore(orig_funcdepth);
return ret;
}