# HG changeset patch # User Bram Moolenaar # Date 1663428604 -7200 # Node ID 6a1ed021a0c0aa2cb0d6fec285f03d552f10dd28 # Parent 7191366d2d85cb99cf479cf257d8e1fcfbc9e3a6 patch 9.0.0487: using freed memory with combination of closures Commit: https://github.com/vim/vim/commit/acd6b9976bd939035025a16ceb4213a680827927 Author: Bram Moolenaar Date: Sat Sep 17 16:27:39 2022 +0100 patch 9.0.0487: using freed memory with combination of closures Problem: Using freed memory with combination of closures. Solution: Do not use a partial after it has been freed through the funcstack. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -4876,6 +4876,8 @@ partial_unref(partial_T *pt) { if (pt != NULL) { + int done = FALSE; + if (--pt->pt_refcount <= 0) partial_free(pt); @@ -4883,9 +4885,12 @@ partial_unref(partial_T *pt) // only reference and can be freed if no other partials reference it. else if (pt->pt_refcount == 1) { + // careful: if the funcstack is freed it may contain this partial + // and it gets freed as well if (pt->pt_funcstack != NULL) - funcstack_check_refcount(pt->pt_funcstack); - if (pt->pt_loopvars != NULL) + done = funcstack_check_refcount(pt->pt_funcstack); + + if (!done && pt->pt_loopvars != NULL) loopvars_check_refcount(pt->pt_loopvars); } } diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro --- a/src/proto/vim9execute.pro +++ b/src/proto/vim9execute.pro @@ -1,7 +1,7 @@ /* vim9execute.c */ void to_string_error(vartype_T vartype); void update_has_breakpoint(ufunc_T *ufunc); -void funcstack_check_refcount(funcstack_T *funcstack); +int funcstack_check_refcount(funcstack_T *funcstack); int set_ref_in_funcstacks(int copyID); int in_def_function(void); ectx_T *clear_currrent_ectx(void); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 487, +/**/ 486, /**/ 485, diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -797,16 +797,19 @@ handle_closure_in_use(ectx_T *ectx, int * funcstack may be the only reference to the partials in the local variables. * Go over all of them, the funcref and can be freed if all partials * referencing the funcstack have a reference count of one. + * Returns TRUE if the funcstack is freed, the partial referencing it will then + * also have been freed. */ - void + int funcstack_check_refcount(funcstack_T *funcstack) { - int i; - garray_T *gap = &funcstack->fs_ga; - int done = 0; + int i; + garray_T *gap = &funcstack->fs_ga; + int done = 0; + typval_T *stack; if (funcstack->fs_refcount > funcstack->fs_min_refcount) - return; + return FALSE; for (i = funcstack->fs_var_offset; i < gap->ga_len; ++i) { typval_T *tv = ((typval_T *)gap->ga_data) + i; @@ -816,18 +819,20 @@ funcstack_check_refcount(funcstack_T *fu && tv->vval.v_partial->pt_refcount == 1) ++done; } - if (done == funcstack->fs_min_refcount) - { - typval_T *stack = gap->ga_data; - - // All partials referencing the funcstack have a reference count of - // one, thus the funcstack is no longer of use. - for (i = 0; i < gap->ga_len; ++i) - clear_tv(stack + i); - vim_free(stack); - remove_funcstack_from_list(funcstack); - vim_free(funcstack); - } + if (done != funcstack->fs_min_refcount) + return FALSE; + + stack = gap->ga_data; + + // All partials referencing the funcstack have a reference count of + // one, thus the funcstack is no longer of use. + for (i = 0; i < gap->ga_len; ++i) + clear_tv(stack + i); + vim_free(stack); + remove_funcstack_from_list(funcstack); + vim_free(funcstack); + + return TRUE; } /*