summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2021-12-07 12:23:57 +0000
committerBram Moolenaar <Bram@vim.org>2021-12-07 12:23:57 +0000
commit4dc24eb5adbcc76838fae1e900936dd230209d96 (patch)
tree79f8deb9ac0294b4cbc228cbde0180ce205964c7
parent92c33eb2735045d25fceed3b607f44d7156c59c1 (diff)
patch 8.2.3756: might crash when callback is not validv8.2.3756
Problem: might crash when callback is not valid. Solution: Check for valid callback. (Yegappan Lakshmanan, closes #9293)
-rw-r--r--src/insexpand.c2
-rw-r--r--src/job.c5
-rw-r--r--src/option.c2
-rw-r--r--src/tag.c3
-rw-r--r--src/testdir/test_iminsert.vim13
-rw-r--r--src/testdir/test_ins_complete.vim28
-rw-r--r--src/testdir/test_tagfunc.vim5
-rw-r--r--src/userfunc.c7
-rw-r--r--src/version.c2
9 files changed, 59 insertions, 8 deletions
diff --git a/src/insexpand.c b/src/insexpand.c
index 92f0731eb5..8c09841f00 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -2329,14 +2329,12 @@ set_thesaurusfunc_option(void)
if (*curbuf->b_p_tsrfu != NUL)
{
// buffer-local option set
- free_callback(&curbuf->b_tsrfu_cb);
retval = option_set_callback_func(curbuf->b_p_tsrfu,
&curbuf->b_tsrfu_cb);
}
else
{
// global option set
- free_callback(&tsrfu_cb);
retval = option_set_callback_func(p_tsrfu, &tsrfu_cb);
}
diff --git a/src/job.c b/src/job.c
index ebe902fb04..6ee9f4432e 100644
--- a/src/job.c
+++ b/src/job.c
@@ -1578,6 +1578,7 @@ invoke_prompt_interrupt(void)
{
typval_T rettv;
typval_T argv[1];
+ int ret;
if (curbuf->b_prompt_interrupt.cb_name == NULL
|| *curbuf->b_prompt_interrupt.cb_name == NUL)
@@ -1585,9 +1586,9 @@ invoke_prompt_interrupt(void)
argv[0].v_type = VAR_UNKNOWN;
got_int = FALSE; // don't skip executing commands
- call_callback(&curbuf->b_prompt_interrupt, -1, &rettv, 0, argv);
+ ret = call_callback(&curbuf->b_prompt_interrupt, -1, &rettv, 0, argv);
clear_tv(&rettv);
- return TRUE;
+ return ret == FAIL ? FALSE : TRUE;
}
/*
diff --git a/src/option.c b/src/option.c
index 8d950b1770..bfe8f57b52 100644
--- a/src/option.c
+++ b/src/option.c
@@ -7210,7 +7210,7 @@ option_set_callback_func(char_u *optval UNUSED, callback_T *optcb UNUSED)
return FAIL;
cb = get_callback(tv);
- if (cb.cb_name == NULL)
+ if (cb.cb_name == NULL || *cb.cb_name == NUL)
{
free_tv(tv);
return FAIL;
diff --git a/src/tag.c b/src/tag.c
index f932934769..41d21b7514 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -1361,7 +1361,8 @@ find_tagfunc_tags(
dict_T *d;
taggy_T *tag = &curwin->w_tagstack[curwin->w_tagstackidx];
- if (*curbuf->b_p_tfu == NUL)
+ if (*curbuf->b_p_tfu == NUL || curbuf->b_tfu_cb.cb_name == NULL
+ || *curbuf->b_tfu_cb.cb_name == NUL)
return FAIL;
args[0].v_type = VAR_STRING;
diff --git a/src/testdir/test_iminsert.vim b/src/testdir/test_iminsert.vim
index c3c4725a1e..a53e6f36b4 100644
--- a/src/testdir/test_iminsert.vim
+++ b/src/testdir/test_iminsert.vim
@@ -257,6 +257,19 @@ func Test_imactivatefunc_imstatusfunc_callback()
set imstatusfunc=()\ =>\ IMstatusfunc1(a)
call assert_fails('normal! i', 'E117:')
+ " set 'imactivatefunc' and 'imstatusfunc' to a non-existing function
+ set imactivatefunc=IMactivatefunc1
+ set imstatusfunc=IMstatusfunc1
+ call assert_fails("set imactivatefunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("set imstatusfunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("let &imactivatefunc = function('NonExistingFunc')", 'E700:')
+ call assert_fails("let &imstatusfunc = function('NonExistingFunc')", 'E700:')
+ let g:IMactivatefunc_called = 0
+ let g:IMstatusfunc_called = 0
+ normal! i
+ call assert_equal(2, g:IMactivatefunc_called)
+ call assert_equal(2, g:IMstatusfunc_called)
+
" cleanup
delfunc IMactivatefunc1
delfunc IMstatusfunc1
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index d508ba087f..aa7b24bf85 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -1074,6 +1074,15 @@ func Test_completefunc_callback()
call assert_fails('call feedkeys("A\<C-X>\<C-U>\<Esc>", "x")', 'E117:')
call assert_equal([], g:MycompleteFunc2_args)
+ " set 'completefunc' to a non-existing function
+ set completefunc=MycompleteFunc2
+ call setline(1, 'five')
+ call assert_fails("set completefunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("let &completefunc = function('NonExistingFunc')", 'E700:')
+ let g:MycompleteFunc2_args = []
+ call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc2_args)
+
" cleanup
delfunc MycompleteFunc1
delfunc MycompleteFunc2
@@ -1285,6 +1294,15 @@ func Test_omnifunc_callback()
call assert_fails('call feedkeys("A\<C-X>\<C-O>\<Esc>", "x")', 'E117:')
call assert_equal([], g:MyomniFunc2_args)
+ " set 'omnifunc' to a non-existing function
+ set omnifunc=MyomniFunc2
+ call setline(1, 'nine')
+ call assert_fails("set omnifunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("let &omnifunc = function('NonExistingFunc')", 'E700:')
+ let g:MyomniFunc2_args = []
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'nine']], g:MyomniFunc2_args)
+
" cleanup
delfunc MyomniFunc1
delfunc MyomniFunc2
@@ -1522,6 +1540,16 @@ func Test_thesaurusfunc_callback()
call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")
call assert_equal('sunday', getline(1))
call assert_equal([[1, ''], [0, 'sun']], g:MytsrFunc4_args)
+ %bw!
+
+ " set 'thesaurusfunc' to a non-existing function
+ set thesaurusfunc=MytsrFunc2
+ call setline(1, 'ten')
+ call assert_fails("set thesaurusfunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("let &thesaurusfunc = function('NonExistingFunc')", 'E700:')
+ let g:MytsrFunc2_args = []
+ call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'ten']], g:MytsrFunc2_args)
" cleanup
set thesaurusfunc&
diff --git a/src/testdir/test_tagfunc.vim b/src/testdir/test_tagfunc.vim
index 7f16fb25e5..b9139d4ffc 100644
--- a/src/testdir/test_tagfunc.vim
+++ b/src/testdir/test_tagfunc.vim
@@ -317,6 +317,11 @@ func Test_tagfunc_callback()
call assert_fails("tag a17", "E117:")
call assert_equal([], g:MytagFunc3_args)
+ " set 'tagfunc' to a non-existing function
+ call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:')
+ call assert_fails("let &tagfunc = function('NonExistingFunc')", 'E700:')
+ call assert_fails("tag axb123", 'E426:')
+
" cleanup
delfunc MytagFunc1
delfunc MytagFunc2
diff --git a/src/userfunc.c b/src/userfunc.c
index 4f86c07910..4423ae59a5 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -3146,6 +3146,7 @@ get_callback_depth(void)
/*
* Invoke call_func() with a callback.
+ * Returns FAIL if the callback could not be called.
*/
int
call_callback(
@@ -3159,6 +3160,8 @@ call_callback(
funcexe_T funcexe;
int ret;
+ if (callback->cb_name == NULL || *callback->cb_name == NUL)
+ return FAIL;
CLEAR_FIELD(funcexe);
funcexe.evaluate = TRUE;
funcexe.partial = callback->cb_partial;
@@ -3170,7 +3173,7 @@ call_callback(
/*
* call the 'callback' function and return the result as a number.
- * Returns -1 when calling the function fails. Uses argv[0] to argv[argc - 1]
+ * Returns -2 when calling the function fails. Uses argv[0] to argv[argc - 1]
* for the function arguments. argv[argc] should have type VAR_UNKNOWN.
*/
varnumber_T
@@ -3184,7 +3187,7 @@ call_callback_retnr(
varnumber_T retval;
if (call_callback(callback, 0, &rettv, argcount, argvars) == FAIL)
- return -1;
+ return -2;
retval = tv_get_number_chk(&rettv, NULL);
clear_tv(&rettv);
diff --git a/src/version.c b/src/version.c
index 294547b862..c586acbd2f 100644
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 3756,
+/**/
3755,
/**/
3754,