]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0448: Vim9: dangling cmdline pointer after skip_expr_cctx() v9.2.0448
authorYasuhiro Matsumoto <mattn.jp@gmail.com>
Wed, 6 May 2026 18:02:09 +0000 (18:02 +0000)
committerChristian Brabandt <cb@256bit.org>
Wed, 6 May 2026 18:02:09 +0000 (18:02 +0000)
Problem:  Vim9: dangling cmdline pointer after skip_expr_cctx()
          (Foxe Chen)
Solution: Extract the cmdline restoration logic from compile_lambda into
          a helper restore_cmdline_arg() and call it from
          skip_expr_cctx() too, so a skipped lambda inside an "else"
          branch does not leave "*arg" pointing into freed evalarg
          memory (Yasuhiro Matsumoto).

fixes:  #20147
closes: #20148

Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/testdir/test_vim9_cmd.vim
src/version.c
src/vim9expr.c

index b54a58b5098abda3f61d6108c744a17dab69a5f6..ce05898061c6a9428eca5be7a21e082fc2551d03 100644 (file)
@@ -2094,6 +2094,24 @@ def Test_lambda_crash()
   v9.CheckScriptFailureList(lines, ["E1356:", "E1405:"])
 enddef
 
+def Test_skipped_lambda_after_else()
+  var lines =<< trim END
+    vim9script
+    def g:Warn(msg: string)
+      if has('patch-9.0.0321')
+        echo msg
+      else
+        timer_start(100, (_) => {
+          echohl WarningMsg | echom msg | echohl None
+        }, {repeat: 0})
+      endif
+    enddef
+    defcompile
+  END
+  v9.CheckScriptSuccess(lines)
+  delfunc! g:Warn
+enddef
+
 def s:check_previewpopup(expected_title: string)
   var id = popup_findpreview()
   assert_notequal(id, 0)
index 78efc6c77729fce86880aa9b0639a8afa2000bc2..0c23e6a5189411cc6f0197d58334b27a56e44384 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    448,
 /**/
     447,
 /**/
index a6c4f1ea105ec4335e2a17554f0b7a556f911090..9d6033464c31030765a24463ca8a123d9044c3f7 100644 (file)
@@ -1741,6 +1741,29 @@ compile_tuple(
     return generate_NEWTUPLE(cctx, count, FALSE);
 }
 
+/*
+ * Restore "*arg" from a temporary cmdline copy.
+ */
+    static void
+restore_cmdline_arg(evalarg_T *evalarg, char_u **arg, cctx_T *cctx)
+{
+    garray_T    *gap;
+    char_u     *line;
+    size_t     off;
+
+    if (!evalarg->eval_using_cmdline || cctx == NULL)
+       return;
+
+    gap = &evalarg->eval_tofree_ga;
+    if (gap->ga_len == 0)
+       return;
+
+    off = *arg - ((char_u **)gap->ga_data)[gap->ga_len - 1];
+    line = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum];
+    *arg = line + off;
+    evalarg->eval_using_cmdline = FALSE;
+}
+
 /*
  * Parse a lambda: "(arg, arg) => expr"
  * "*arg" points to the '('.
@@ -1803,18 +1826,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
            compile_def_function(ufunc, FALSE, compile_type, cctx);
     }
 
-    // The last entry in evalarg.eval_tofree_ga is a copy of the last line and
-    // "*arg" may point into it.  Point into the original line to avoid a
-    // dangling pointer.
-    if (evalarg.eval_using_cmdline)
-    {
-       garray_T    *gap = &evalarg.eval_tofree_ga;
-       size_t      off = *arg - ((char_u **)gap->ga_data)[gap->ga_len - 1];
-
-       *arg = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum]
-                                                                        + off;
-       evalarg.eval_using_cmdline = FALSE;
-    }
+    restore_cmdline_arg(&evalarg, arg, cctx);
 
     clear_evalarg(&evalarg, NULL);
 
@@ -2348,6 +2360,7 @@ skip_expr_cctx(char_u **arg, cctx_T *cctx)
     init_evalarg(&evalarg);
     evalarg.eval_cctx = cctx;
     skip_expr(arg, &evalarg);
+    restore_cmdline_arg(&evalarg, arg, cctx);
     clear_evalarg(&evalarg, NULL);
 }