Commit: patch 9.2.0960: double-free in string_reduce()

Christian Brabandt <[email protected]>
Newsgroups gmane.editors.vim.devel
Message-ID <[email protected]>
patch 9.2.0960: double-free in string_reduce()

Commit: https://github.com/vim/vim/commit/cd59994c455a20d39d5cc41b4978ecfd2bf4be2e
Author: Christian Brabandt <[email protected]>
Date:   Mon Aug 17 19:05:55 2026 +0000

    patch 9.2.0960: double-free in string_reduce()
    
    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: #21048
    
    Supported by AI.
    
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/src/list.c b/src/list.c
index cf5374de2..0ea2e13bd 100644
--- a/src/list.c
+++ b/src/list.c
@@ -3472,7 +3472,7 @@ f_reduce(typval_T *argvars, typval_T *rettv)
     else if (argvars[1].v_type == VAR_PARTIAL)
 	func_name = partial_name(argvars[1].vval.v_partial);
     else
-	func_name = tv_get_string(&argvars[1]);
+	func_name = tv_get_string_strict(&argvars[1]);
     if (func_name == NULL || *func_name == NUL)
     {
 	emsg(_(e_missing_function_argument));
diff --git a/src/strings.c b/src/strings.c
index 71f9d83e2..fa24586c1 100644
--- a/src/strings.c
+++ b/src/strings.c
@@ -1043,9 +1043,14 @@ string_reduce(
     for ( ; *p != NUL; p += len)
     {
 	argv[0] = *rettv;
+	rettv->v_type = VAR_UNKNOWN;
+
 	len = copy_first_char_to_tv(p, &argv[1]);
 	if (len < 0)
+	{
+	    *rettv = argv[0];
 	    break;
+	}
 
 	r = eval_expr_typval(expr, TRUE, argv, 2, fc, rettv);
 
diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim
index 31b5f268d..88c2e07bd 100644
--- a/src/testdir/test_listdict.vim
+++ b/src/testdir/test_listdict.vim
@@ -1115,6 +1115,14 @@ func Test_reduce()
   " should not crash
   call assert_fails('echo reduce([1], test_null_function())', 'E1132:')
   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()
diff --git a/src/version.c b/src/version.c
index 65bfaa6f0..19542bdde 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    960,
 /**/
     959,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups "vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1ww2na-008wKi-TY%40256bit.org.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.