# HG changeset patch # User Bram Moolenaar # Date 1573341304 -3600 # Node ID e9675870c48057d5518f2788580c1407d5eb558f # Parent a5f35d5b839ffeb6a2d2c7ee23d8b47dc92e24c8 patch 8.1.2282: crash when passing many arguments through a partial Commit: https://github.com/vim/vim/commit/4c054e9fb23027b55a09ee647a3a2c91936aeb1b Author: Bram Moolenaar Date: Sun Nov 10 00:13:50 2019 +0100 patch 8.1.2282: crash when passing many arguments through a partial Problem: Crash when passing many arguments through a partial. (Andy Massimino) Solution: Check the number of arguments. (closes #5186) diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2527,6 +2527,12 @@ common_function(typval_T *argvars, typva list = argvars[arg_idx].vval.v_list; if (list == NULL || list->lv_len == 0) arg_idx = 0; + else if (list->lv_len > MAX_FUNC_ARGS) + { + emsg_funcname((char *)e_toomanyarg, name); + vim_free(name); + goto theend; + } } } if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -3,6 +3,7 @@ void func_init(void); hashtab_T *func_tbl_get(void); int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate); char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload); +void emsg_funcname(char *ermsg, char_u *name); int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, funcexe_T *funcexe); ufunc_T *find_func(char_u *name); void save_funccal(funccal_entry_T *entry); diff --git a/src/regexp.c b/src/regexp.c --- a/src/regexp.c +++ b/src/regexp.c @@ -2015,12 +2015,18 @@ vim_regsub_both( call_func(s, -1, &rettv, 1, argv, &funcexe); } if (matchList.sl_list.lv_len > 0) - /* fill_submatch_list() was called */ + // fill_submatch_list() was called clear_submatch_list(&matchList); - eval_result = tv_get_string_buf_chk(&rettv, buf); - if (eval_result != NULL) - eval_result = vim_strsave(eval_result); + if (rettv.v_type == VAR_UNKNOWN) + // something failed, no need to report another error + eval_result = NULL; + else + { + eval_result = tv_get_string_buf_chk(&rettv, buf); + if (eval_result != NULL) + eval_result = vim_strsave(eval_result); + } clear_tv(&rettv); } else diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim --- a/src/testdir/test_expr.vim +++ b/src/testdir/test_expr.vim @@ -496,6 +496,8 @@ func Test_funcref() let OneByRef = 'One'->funcref() call assert_equal(2, OneByRef()) call assert_fails('echo funcref("{")', 'E475:') + let OneByRef = funcref("One", repeat(["foo"], 20)) + call assert_fails('let OneByRef = funcref("One", repeat(["foo"], 21))', 'E118:') endfunc func Test_setmatches() diff --git a/src/testdir/test_substitute.vim b/src/testdir/test_substitute.vim --- a/src/testdir/test_substitute.vim +++ b/src/testdir/test_substitute.vim @@ -408,9 +408,20 @@ endfunc func SubReplacer(text, submatches) return a:text .. a:submatches[0] .. a:text endfunc +func SubReplacer20(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, submatches) + return a:t3 .. a:submatches[0] .. a:t11 +endfunc func Test_substitute_partial() call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacer', ['foo']), 'g')) + + " 19 arguments plus one is just OK + let Replacer = function('SubReplacer20', repeat(['foo'], 19)) + call assert_equal('1foo2foo3', substitute('123', '2', Replacer, 'g')) + + " 20 arguments plus one is too many + let Replacer = function('SubReplacer20', repeat(['foo'], 20)) + call assert_fails("call substitute('123', '2', Replacer, 'g')", 'E118') endfunc " Tests for *sub-replace-special* and *sub-replace-expression* on :substitute. diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -408,7 +408,7 @@ deref_func_name(char_u *name, int *lenp, * Give an error message with a function name. Handle things. * "ermsg" is to be passed without translation, use N_() instead of _(). */ - static void + void emsg_funcname(char *ermsg, char_u *name) { char_u *p; @@ -1537,7 +1537,14 @@ call_func( if (error == ERROR_NONE && partial->pt_argc > 0) { for (argv_clear = 0; argv_clear < partial->pt_argc; ++argv_clear) + { + if (argv_clear + argcount_in >= MAX_FUNC_ARGS) + { + error = ERROR_TOOMANY; + goto theend; + } copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]); + } for (i = 0; i < argcount_in; ++i) argv[i + argv_clear] = argvars_in[i]; argvars = argv; @@ -1672,6 +1679,7 @@ call_func( if (error == ERROR_NONE) ret = OK; +theend: /* * Report an error unless the argument evaluation or function call has been * cancelled due to an aborting error, an interrupt, or an exception. diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2282, +/**/ 2281, /**/ 2280,