From: Barrett Ruth Date: Mon, 27 Apr 2026 17:18:17 +0000 (+0000) Subject: patch 9.2.0400: sandbox callbacks selected through 'complete' X-Git-Tag: v9.2.0400^0 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=dd9b31fb62c0003be6cfd18847982b26efc73d34;p=thirdparty%2Fvim.git patch 9.2.0400: sandbox callbacks selected through 'complete' Problem: Modeline-tainted 'complete' values can invoke completion callbacks outside the sandbox. Solution: Enter the sandbox for both 'complete' callback phases and add a regression test (Barrett Ruth) closes: #20078 Signed-off-by: Barrett Ruth Signed-off-by: Christian Brabandt --- diff --git a/src/insexpand.c b/src/insexpand.c index 0019c7eb45..a603ed7b08 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -3631,6 +3631,9 @@ expand_by_function(int type, char_u *base, callback_T *cb) int save_State = State; int retval; int is_cpt_function = (cb != NULL); + int use_sandbox = is_cpt_function + && was_set_insecurely(curwin, + (char_u *)"complete", OPT_LOCAL); if (!is_cpt_function) { @@ -3652,8 +3655,12 @@ expand_by_function(int type, char_u *base, callback_T *cb) // switching to another window, it should not be needed and may end up in // Insert mode in another buffer. ++textlock; + if (use_sandbox) + ++sandbox; retval = call_callback(cb, 0, &rettv, 2, args); + if (use_sandbox) + --sandbox; // Call a function, which returns a list or dict. if (retval == OK) @@ -6760,6 +6767,9 @@ get_userdefined_compl_info( pos_T pos; int save_State = State; int is_cpt_function = (cb != NULL); + int use_sandbox = is_cpt_function + && was_set_insecurely(curwin, + (char_u *)"complete", OPT_LOCAL); if (!is_cpt_function) { @@ -6782,7 +6792,11 @@ get_userdefined_compl_info( args[2].v_type = VAR_UNKNOWN; pos = curwin->w_cursor; ++textlock; + if (use_sandbox) + ++sandbox; col = call_callback_retnr(cb, 2, args); + if (use_sandbox) + --sandbox; --textlock; State = save_State; diff --git a/src/testdir/test_modeline.vim b/src/testdir/test_modeline.vim index b78a4258f0..6884ab473d 100644 --- a/src/testdir/test_modeline.vim +++ b/src/testdir/test_modeline.vim @@ -283,6 +283,61 @@ func Test_modeline_fails_modelineexpr() call s:modeline_fails('titlestring', 'titlestring=Something()', 'E992:') endfunc +func Test_modeline_complete_uses_sandbox() + let modeline = &modeline + let modelineexpr = &modelineexpr + let modelinestrict = &modelinestrict + + func! ModelineCompletePwnFindstart(findstart, base) + if a:findstart + call writefile(['findstart'], 'Xmodeline_complete_proof') + return 0 + endif + return ['match'] + endfunc + + func! ModelineCompletePwnMatches(findstart, base) + if a:findstart + return 0 + endif + call writefile(['matches'], 'Xmodeline_complete_proof') + return ['match'] + endfunc + + try + set modeline modelineexpr nomodelinestrict + + call writefile([ + \ 'vim: set complete=FModelineCompletePwnFindstart :', + \ 'body', + \ ], 'Xmodeline_complete_attack', 'D') + call delete('Xmodeline_complete_proof') + edit Xmodeline_complete_attack + call cursor(2, 1) + call assert_fails('call feedkeys("i\\", "xt")', 'E48:') + call assert_false(filereadable('Xmodeline_complete_proof')) + bwipe! + + call writefile([ + \ 'vim: set complete=FModelineCompletePwnMatches :', + \ 'body', + \ ], 'Xmodeline_complete_attack', 'D') + call delete('Xmodeline_complete_proof') + edit Xmodeline_complete_attack + call cursor(2, 1) + call assert_fails('call feedkeys("i\\", "xt")', 'E48:') + call assert_false(filereadable('Xmodeline_complete_proof')) + bwipe! + finally + let &modeline = modeline + let &modelineexpr = modelineexpr + let &modelinestrict = modelinestrict + call delete('Xmodeline_complete_proof') + delfunc ModelineCompletePwnFindstart + delfunc ModelineCompletePwnMatches + endtry +endfunc + func Test_modeline_setoption_verbose() let modeline = &modeline set modeline diff --git a/src/version.c b/src/version.c index 4541539553..8dfe9b8f78 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 400, /**/ 399, /**/