From cc25b2f13cff2b743cefbc09c7330e28d10bc289 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 18 Aug 2026 09:03:27 +0800 Subject: [PATCH] vim-patch:9.2.0960: double-free in string_reduce() (#41357) Problem: string_reduce() copies *rettv into argv[0] before calling eval_expr_typval(). When the evaluator fails early, rettv is never reset and still aliases argv[0] v_string. clear_tv(&argv[0]) frees it, leaving rettv dangling and when in vim9script get_func_tv() frees it again (Ave Dva). Solution: Set rettv->v_type = VAR_UNKNOWN like what is done in list_reduce() and tuple_reduce(), use tv_get_string_strict() in f_reduce() closes: vim/vim#21048 Supported by AI. https://github.com/vim/vim/commit/cd59994c455a20d39d5cc41b4978ecfd2bf4be2e Co-authored-by: Christian Brabandt --- src/nvim/eval/funcs.c | 1 + test/old/testdir/test_listdict.vim | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index a54056164e..9d6f7d972c 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -5321,6 +5321,7 @@ static void reduce_string(typval_T *argvars, typval_T *expr, typval_T *rettv) for (; *p != NUL; p += len) { typval_T argv[3]; argv[0] = *rettv; + rettv->v_type = VAR_UNKNOWN; len = utfc_ptr2len(p); argv[1] = (typval_T){ .v_type = VAR_STRING, diff --git a/test/old/testdir/test_listdict.vim b/test/old/testdir/test_listdict.vim index 78d3a19cfb..f40a4290cc 100644 --- a/test/old/testdir/test_listdict.vim +++ b/test/old/testdir/test_listdict.vim @@ -1066,6 +1066,14 @@ func Test_reduce() " call assert_fails('echo reduce([1], test_null_function())', 'E1132:') " Nvim doesn't have null partials " call assert_fails('echo reduce([1], test_null_partial())', 'E1132:') + + " did cause double free + function! OuterReduce() + vim9 echo reduce('ab', 42) + endfunction + "call assert_fails('call OuterReduce()', 'E1024:') + call assert_fails("echo reduce('ab', 'NoSuchFunc')", 'E117:') + delfunc OuterReduce endfunc " splitting a string to a List using split()