From: Christian Brabandt Date: Thu, 2 Jul 2026 19:54:49 +0000 (+0000) Subject: patch 9.2.0777: Memory leak in add_defer() on alloc failure X-Git-Tag: v9.2.0777^0 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e43064830d1b69fe1bac1653e070b13b1c2fef00;p=thirdparty%2Fvim.git patch 9.2.0777: Memory leak in add_defer() on alloc failure 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 --- diff --git a/src/alloc.h b/src/alloc.h index 651e01f241..f3a27f6212 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -45,5 +45,6 @@ typedef enum { aid_newtabpage_tvars, aid_blob_alloc, aid_get_func, + aid_defer, aid_last } alloc_id_T; diff --git a/src/testdir/test_user_func.vim b/src/testdir/test_user_func.vim index 56b21a481b..c7e58e9e1e 100644 --- a/src/testdir/test_user_func.vim +++ b/src/testdir/test_user_func.vim @@ -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 diff --git a/src/userfunc.c b/src/userfunc.c index 66b55449e3..de1a8e84e0 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -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(¤t_funccal->fc_defer, sizeof(defer_T), 10); - if (ga_grow(¤t_funccal->fc_defer, 1) == FAIL) + if (ga_grow_id(¤t_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) diff --git a/src/version.c b/src/version.c index 1e7eba70af..70c83e88ae 100644 --- a/src/version.c +++ b/src/version.c @@ -759,6 +759,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 777, /**/ 776, /**/