0
0
mirror of https://github.com/vim/vim.git synced 2025-09-23 03:43:49 -04:00

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.
This commit is contained in:
Bram Moolenaar
2022-09-17 16:27:39 +01:00
parent d5bc762dea
commit acd6b9976b
4 changed files with 31 additions and 19 deletions

View File

@@ -4876,6 +4876,8 @@ partial_unref(partial_T *pt)
{ {
if (pt != NULL) if (pt != NULL)
{ {
int done = FALSE;
if (--pt->pt_refcount <= 0) if (--pt->pt_refcount <= 0)
partial_free(pt); partial_free(pt);
@@ -4883,9 +4885,12 @@ partial_unref(partial_T *pt)
// only reference and can be freed if no other partials reference it. // only reference and can be freed if no other partials reference it.
else if (pt->pt_refcount == 1) 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) if (pt->pt_funcstack != NULL)
funcstack_check_refcount(pt->pt_funcstack); done = funcstack_check_refcount(pt->pt_funcstack);
if (pt->pt_loopvars != NULL)
if (!done && pt->pt_loopvars != NULL)
loopvars_check_refcount(pt->pt_loopvars); loopvars_check_refcount(pt->pt_loopvars);
} }
} }

View File

@@ -1,7 +1,7 @@
/* vim9execute.c */ /* vim9execute.c */
void to_string_error(vartype_T vartype); void to_string_error(vartype_T vartype);
void update_has_breakpoint(ufunc_T *ufunc); 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 set_ref_in_funcstacks(int copyID);
int in_def_function(void); int in_def_function(void);
ectx_T *clear_currrent_ectx(void); ectx_T *clear_currrent_ectx(void);

View File

@@ -703,6 +703,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
487,
/**/ /**/
486, 486,
/**/ /**/

View File

@@ -797,16 +797,19 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
* funcstack may be the only reference to the partials in the local variables. * 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 * Go over all of them, the funcref and can be freed if all partials
* referencing the funcstack have a reference count of one. * 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) funcstack_check_refcount(funcstack_T *funcstack)
{ {
int i; int i;
garray_T *gap = &funcstack->fs_ga; garray_T *gap = &funcstack->fs_ga;
int done = 0; int done = 0;
typval_T *stack;
if (funcstack->fs_refcount > funcstack->fs_min_refcount) if (funcstack->fs_refcount > funcstack->fs_min_refcount)
return; return FALSE;
for (i = funcstack->fs_var_offset; i < gap->ga_len; ++i) for (i = funcstack->fs_var_offset; i < gap->ga_len; ++i)
{ {
typval_T *tv = ((typval_T *)gap->ga_data) + i; typval_T *tv = ((typval_T *)gap->ga_data) + i;
@@ -816,18 +819,20 @@ funcstack_check_refcount(funcstack_T *funcstack)
&& tv->vval.v_partial->pt_refcount == 1) && tv->vval.v_partial->pt_refcount == 1)
++done; ++done;
} }
if (done == funcstack->fs_min_refcount) if (done != funcstack->fs_min_refcount)
{ return FALSE;
typval_T *stack = gap->ga_data;
// All partials referencing the funcstack have a reference count of stack = gap->ga_data;
// one, thus the funcstack is no longer of use.
for (i = 0; i < gap->ga_len; ++i) // All partials referencing the funcstack have a reference count of
clear_tv(stack + i); // one, thus the funcstack is no longer of use.
vim_free(stack); for (i = 0; i < gap->ga_len; ++i)
remove_funcstack_from_list(funcstack); clear_tv(stack + i);
vim_free(funcstack); vim_free(stack);
} remove_funcstack_from_list(funcstack);
vim_free(funcstack);
return TRUE;
} }
/* /*