]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0403: Vim9: def function sandbox bypass v9.2.0403
authorChristian Brabandt <cb@256bit.org>
Mon, 27 Apr 2026 18:14:49 +0000 (18:14 +0000)
committerChristian Brabandt <cb@256bit.org>
Mon, 27 Apr 2026 18:14:49 +0000 (18:14 +0000)
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 <cb@256bit.org>
src/testdir/test_functions.vim
src/userfunc.c
src/version.c
src/vim9execute.c

index 9c652101aef983e43ec899bbd6cffe98f26f1cc5..e6142c2df3bcbd4ae79dd734429b0d118091f28b 100644 (file)
@@ -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
index e861a9eacbc956c2639b420bf08683397cdc0b01..261518538656b6e0185192c34e7024df2c73db9b 100644 (file)
@@ -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
index 45f5719329135a33059e1e7be7f178f6b948e39e..72f88365f5b900622fd26a21ffc666f3b01fb4a3 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    403,
 /**/
     402,
 /**/
index d83b7968b2619218115604b75e51e7a2052d2195..e1ddb7c1de341b79637f554ed30d388c21106d3a 100644 (file)
@@ -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;