From: Christian Brabandt Date: Mon, 27 Apr 2026 18:14:49 +0000 (+0000) Subject: patch 9.2.0403: Vim9: def function sandbox bypass X-Git-Tag: v9.2.0403^0 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f1a9449206b019f6443915c4d56ee0cc51492a87;p=thirdparty%2Fvim.git patch 9.2.0403: Vim9: def function sandbox bypass Problem: Vim9: def function sandbox bypass (Srinivas Piskala Ganesh Babu) Solution: Check for sandbox flag in call_user_func() and call_dfunc() when executing Vim9 script functions closes: #20071 Supported by AI Signed-off-by: Christian Brabandt --- diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index 9c652101ae..e6142c2df3 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -4629,4 +4629,41 @@ func Test_uriencoding() call v9.CheckLegacyAndVim9Success(lines) endfunc +" Note: legacy func, not vim9 def, to avoid the test file being vim9script +func Test_vim9_def_fc_sandbox() + sandbox def! g:Bad() + system('echo unsafe') + enddef + + call assert_fails('call g:Bad()', 'E48:') + delfunction g:Bad +endfunc + +func Test_vim9_def_call_dfunc_fc_sandbox() + " Inner has FC_SANDBOX, outer does not. + " Calling outer goes through call_dfunc into inner, exercising + " the sandbox handling in call_dfunc and func_return. + sandbox def! g:Inner() + system('echo unsafe') + enddef + + def! g:Outer() + g:Inner() + enddef + + call assert_fails('call g:Outer()', 'E48:') + delfunction g:Inner + delfunction g:Outer +endfunc + +" Deferred body inside a sandboxed def function must still run sandboxed. +func Test_vim9_def_defer_fc_sandbox() + sandbox def! g:BadDefer() + defer system('echo unsafe') + enddef + + call assert_fails('call g:BadDefer()', 'E48:') + delfunction g:BadDefer +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/userfunc.c b/src/userfunc.c index e861a9eacb..2615185386 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3038,6 +3038,11 @@ call_user_func( if (fp->uf_def_status != UF_NOT_COMPILED) { + if (fp->uf_flags & FC_SANDBOX) + { + using_sandbox = TRUE; + ++sandbox; + } #ifdef FEAT_PROFILE ufunc_T *caller = fc->fc_caller == NULL ? NULL : fc->fc_caller->fc_func; #endif @@ -3050,6 +3055,8 @@ call_user_func( if (call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial, funcexe->fe_object, fc, rettv) == FAIL) retval = FCERR_FAILED; + if (using_sandbox) + --sandbox; funcdepth_decrement(); #ifdef FEAT_PROFILE if (do_profiling == PROF_YES && (fp->uf_profiling diff --git a/src/version.c b/src/version.c index 45f5719329..72f88365f5 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 */ +/**/ + 403, /**/ 402, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index d83b7968b2..e1ddb7c1de 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -742,6 +742,9 @@ call_dfunc( else ectx->ec_outer_ref = NULL; + if (ufunc->uf_flags & FC_SANDBOX) + ++sandbox; + ++ufunc->uf_calls; // Set execution state to the start of the called function. @@ -1290,6 +1293,9 @@ func_return(ectx_T *ectx) if (dfunc->df_defer_var_idx > 0) invoke_defer_funcs(ectx); + if (dfunc->df_ufunc->uf_flags & FC_SANDBOX) + --sandbox; + // No check for uf_refcount being zero, cannot think of a way that would // happen. --dfunc->df_ufunc->uf_calls;