Commit: patch 9.2.0448: Vim9: dangling cmdline pointer after skip_expr_cctx()

2 views
Skip to first unread message

Christian Brabandt

unread,
May 6, 2026, 2:15:16 PM (yesterday) May 6
to vim...@googlegroups.com
patch 9.2.0448: Vim9: dangling cmdline pointer after skip_expr_cctx()

Commit: https://github.com/vim/vim/commit/4bcc8ba93d4d7b6b216dc6a0365e21676b70c715
Author: Yasuhiro Matsumoto <matt...@gmail.com>
Date: Wed May 6 18:02:09 2026 +0000

patch 9.2.0448: Vim9: dangling cmdline pointer after skip_expr_cctx()

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 <matt...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim
index b54a58b50..ce0589806 100644
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -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)
diff --git a/src/version.c b/src/version.c
index 78efc6c77..0c23e6a51 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 */
+/**/
+ 448,
/**/
447,
/**/
diff --git a/src/vim9expr.c b/src/vim9expr.c
index a6c4f1ea1..9d6033464 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -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);
}

Reply all
Reply to author
Forward
0 new messages