From d7bd7f13a8f026b8b95fdc49b4754f6199105891 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Nov 2022 11:04:33 +0800 Subject: [PATCH] vim-patch:8.2.3756: might crash when callback is not valid Problem: might crash when callback is not valid. Solution: Check for valid callback. (Yegappan Lakshmanan, closes vim/vim#9293) https://github.com/vim/vim/commit/4dc24eb5adbcc76838fae1e900936dd230209d96 Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.c | 5 +++-- src/nvim/eval/userfunc.c | 4 ++-- src/nvim/insexpand.c | 4 +--- src/nvim/option.c | 2 +- src/nvim/tag.c | 4 ++-- src/nvim/testdir/test_ins_complete.vim | 28 ++++++++++++++++++++++++++ src/nvim/testdir/test_tagfunc.vim | 5 +++++ 7 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a4fadcd7bc..daae6416dc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5860,6 +5860,7 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) return true; } +/// @return whether the callback could be called. bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, typval_T *const rettv) FUNC_ATTR_NONNULL_ALL @@ -8705,9 +8706,9 @@ bool invoke_prompt_interrupt(void) argv[0].v_type = VAR_UNKNOWN; got_int = false; // don't skip executing commands - callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv); + int ret = callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv); tv_clear(&rettv); - return true; + return ret != FAIL; } /// Compare "typ1" and "typ2". Put the result in "typ1". diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index d07cfe0bd9..cc21bf56ca 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1394,7 +1394,7 @@ func_call_skip_call: } /// 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. /// /// @param argcount number of "argvars" @@ -1403,7 +1403,7 @@ varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argv { typval_T rettv; if (!callback_call(callback, argcount, argvars, &rettv)) { - return -1; + return -2; } varnumber_T retval = tv_get_number_chk(&rettv, NULL); diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 1acdddedef..7d4e77b263 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -2233,7 +2233,7 @@ static Callback tsrfu_cb; ///< 'thesaurusfunc' callback function static void copy_global_to_buflocal_cb(Callback *globcb, Callback *bufcb) { callback_free(bufcb); - if (globcb->data.funcref != NULL && *globcb->data.funcref != NUL) { + if (globcb->type != kCallbackNone) { callback_copy(bufcb, globcb); } } @@ -2290,11 +2290,9 @@ int set_thesaurusfunc_option(void) if (*curbuf->b_p_tsrfu != NUL) { // buffer-local option set - callback_free(&curbuf->b_tsrfu_cb); retval = option_set_callback_func(curbuf->b_p_tsrfu, &curbuf->b_tsrfu_cb); } else { // global option set - callback_free(&tsrfu_cb); retval = option_set_callback_func(p_tsrfu, &tsrfu_cb); } diff --git a/src/nvim/option.c b/src/nvim/option.c index c0a1f14463..419cfb6873 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5193,7 +5193,7 @@ int option_set_callback_func(char *optval, Callback *optcb) } Callback cb; - if (!callback_from_typval(&cb, tv)) { + if (!callback_from_typval(&cb, tv) || cb.type == kCallbackNone) { tv_free(tv); return FAIL; } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 16d6629c2e..d57bbead63 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -154,7 +154,7 @@ void free_tagfunc_option(void) void set_buflocal_tfu_callback(buf_T *buf) { callback_free(&buf->b_tfu_cb); - if (tfu_cb.data.funcref != NULL && *tfu_cb.data.funcref != NUL) { + if (tfu_cb.type != kCallbackNone) { callback_copy(&buf->b_tfu_cb, &tfu_cb); } } @@ -1153,7 +1153,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl } } - if (*curbuf->b_p_tfu == NUL) { + if (*curbuf->b_p_tfu == NUL || curbuf->b_tfu_cb.type == kCallbackNone) { return FAIL; } diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 8b5f9a189f..05a67e7956 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -1491,6 +1491,15 @@ func Test_completefunc_callback() " call assert_fails('call feedkeys("A\\\", "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\\\", 'x') + call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc2_args) + " cleanup delfunc MycompleteFunc1 delfunc MycompleteFunc2 @@ -1702,6 +1711,15 @@ func Test_omnifunc_callback() " call assert_fails('call feedkeys("A\\\", "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\\\", 'x') + call assert_equal([[1, ''], [0, 'nine']], g:MyomniFunc2_args) + " cleanup delfunc MyomniFunc1 delfunc MyomniFunc2 @@ -1939,6 +1957,16 @@ func Test_thesaurusfunc_callback() call feedkeys("A\\\", "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\\\", 'x') + call assert_equal([[1, ''], [0, 'ten']], g:MytsrFunc2_args) " cleanup set thesaurusfunc& diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim index 06d074c261..88500db269 100644 --- a/src/nvim/testdir/test_tagfunc.vim +++ b/src/nvim/testdir/test_tagfunc.vim @@ -315,6 +315,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