]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0777: Memory leak in add_defer() on alloc failure v9.2.0777
authorChristian Brabandt <cb@256bit.org>
Thu, 2 Jul 2026 19:54:49 +0000 (19:54 +0000)
committerChristian Brabandt <cb@256bit.org>
Thu, 2 Jul 2026 19:54:49 +0000 (19:54 +0000)
Problem:  Memory leak in add_defer() on allocation failure (Ao Xijie)
Solution: Free saved_name when ga_grow() fails, propagate failure from
          add_defer_function() to the caller, add a test for alloc
          failure

related: #20668

Supported by AI.

Signed-off-by: Christian Brabandt <cb@256bit.org>
src/alloc.h
src/testdir/test_user_func.vim
src/userfunc.c
src/version.c

index 651e01f2418a1bb888e5a3755c0901a012086ea2..f3a27f621288439c3f71d2b4eeb1f7df27f32b38 100644 (file)
@@ -45,5 +45,6 @@ typedef enum {
     aid_newtabpage_tvars,
     aid_blob_alloc,
     aid_get_func,
+    aid_defer,
     aid_last
 } alloc_id_T;
index 56b21a481b09484806db16a37edfb86d24ef1ea6..c7e58e9e1ec40b5b68daff4af22a2a33bfdd585c 100644 (file)
@@ -1087,4 +1087,29 @@ func Test_func_return_in_try_verbose()
   delfunc TryReturnOverlongString
 endfunc
 
+" Memory allocation failure when registering a deferred function: the ga_grow()
+" in add_defer() fails, so the deferred function must not be registered and
+" "saved_name" must not leak
+func Test_defer_alloc_fail()
+  let g:defer_log = []
+  func DeferLogTarget()
+    call add(g:defer_log, 'ran')
+  endfunc
+  func CallWithDefer()
+    defer DeferLogTarget()
+  endfunc
+
+  call test_alloc_fail(GetAllocId('defer'), 0, 0)
+  call assert_fails('call CallWithDefer()', 'E342:')
+  call assert_equal([], g:defer_log)
+
+  " Without the injected failure it registers and runs at function exit.
+  call CallWithDefer()
+  call assert_equal(['ran'], g:defer_log)
+
+  delfunc DeferLogTarget
+  delfunc CallWithDefer
+  unlet g:defer_log
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 66b55449e3db2b1487eafe8063c0e0e20e134a44..de1a8e84e0fc650ec1de94451e03b0ad5b93b0b9 100644 (file)
@@ -6684,14 +6684,20 @@ add_defer(char_u *name, int argcount_arg, typval_T *argvars)
     if (in_def_function())
     {
        if (add_defer_function(saved_name, argcount, argvars) == OK)
+       {
            argcount = 0;
+           ret = OK;
+       }
     }
     else
     {
        if (current_funccal->fc_defer.ga_itemsize == 0)
            ga_init2(&current_funccal->fc_defer, sizeof(defer_T), 10);
-       if (ga_grow(&current_funccal->fc_defer, 1) == FAIL)
+       if (ga_grow_id(&current_funccal->fc_defer, 1, aid_defer) == FAIL)
+       {
+           vim_free(saved_name);
            goto theend;
+       }
        dr = ((defer_T *)current_funccal->fc_defer.ga_data)
                                          + current_funccal->fc_defer.ga_len++;
        dr->dr_name = saved_name;
@@ -6701,8 +6707,8 @@ add_defer(char_u *name, int argcount_arg, typval_T *argvars)
            --argcount;
            dr->dr_argvars[argcount] = argvars[argcount];
        }
+       ret = OK;
     }
-    ret = OK;
 
 theend:
     while (--argcount >= 0)
index 1e7eba70afeba73eefa40ddd96b32365aca81540..70c83e88ae944b562f95381ddbba8701d91dbcd6 100644 (file)
@@ -759,6 +759,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    777,
 /**/
     776,
 /**/