forked from aniani/vim
patch 8.0.0297: double free on exit when using a closure
Problem: Double free on exit when using a closure. (James McCoy) Solution: Split free_al_functions in two parts. (closes #1428)
This commit is contained in:
@@ -1337,6 +1337,7 @@ typedef struct
|
|||||||
int uf_varargs; /* variable nr of arguments */
|
int uf_varargs; /* variable nr of arguments */
|
||||||
int uf_flags;
|
int uf_flags;
|
||||||
int uf_calls; /* nr of active calls */
|
int uf_calls; /* nr of active calls */
|
||||||
|
int uf_cleared; /* func_clear() was already called */
|
||||||
garray_T uf_args; /* arguments */
|
garray_T uf_args; /* arguments */
|
||||||
garray_T uf_lines; /* function lines */
|
garray_T uf_lines; /* function lines */
|
||||||
#ifdef FEAT_PROFILE
|
#ifdef FEAT_PROFILE
|
||||||
|
@@ -1075,12 +1075,17 @@ func_remove(ufunc_T *fp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free a function and remove it from the list of functions.
|
* Free all things that a function contains. Does not free the function
|
||||||
|
* itself, use func_free() for that.
|
||||||
* When "force" is TRUE we are exiting.
|
* When "force" is TRUE we are exiting.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
func_free(ufunc_T *fp, int force)
|
func_clear(ufunc_T *fp, int force)
|
||||||
{
|
{
|
||||||
|
if (fp->uf_cleared)
|
||||||
|
return;
|
||||||
|
fp->uf_cleared = TRUE;
|
||||||
|
|
||||||
/* clear this function */
|
/* clear this function */
|
||||||
ga_clear_strings(&(fp->uf_args));
|
ga_clear_strings(&(fp->uf_args));
|
||||||
ga_clear_strings(&(fp->uf_lines));
|
ga_clear_strings(&(fp->uf_lines));
|
||||||
@@ -1089,16 +1094,35 @@ func_free(ufunc_T *fp, int force)
|
|||||||
vim_free(fp->uf_tml_total);
|
vim_free(fp->uf_tml_total);
|
||||||
vim_free(fp->uf_tml_self);
|
vim_free(fp->uf_tml_self);
|
||||||
#endif
|
#endif
|
||||||
|
funccal_unref(fp->uf_scoped, fp, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free a function and remove it from the list of functions. Does not free
|
||||||
|
* what a function contains, call func_clear() first.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
func_free(ufunc_T *fp)
|
||||||
|
{
|
||||||
/* only remove it when not done already, otherwise we would remove a newer
|
/* only remove it when not done already, otherwise we would remove a newer
|
||||||
* version of the function */
|
* version of the function */
|
||||||
if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0)
|
if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0)
|
||||||
func_remove(fp);
|
func_remove(fp);
|
||||||
|
|
||||||
funccal_unref(fp->uf_scoped, fp, force);
|
|
||||||
|
|
||||||
vim_free(fp);
|
vim_free(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free all things that a function contains and free the function itself.
|
||||||
|
* When "force" is TRUE we are exiting.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
func_clear_free(ufunc_T *fp, int force)
|
||||||
|
{
|
||||||
|
func_clear(fp, force);
|
||||||
|
func_free(fp);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are two kinds of function names:
|
* There are two kinds of function names:
|
||||||
* 1. ordinary names, function defined with :function
|
* 1. ordinary names, function defined with :function
|
||||||
@@ -1120,10 +1144,40 @@ free_all_functions(void)
|
|||||||
hashitem_T *hi;
|
hashitem_T *hi;
|
||||||
ufunc_T *fp;
|
ufunc_T *fp;
|
||||||
long_u skipped = 0;
|
long_u skipped = 0;
|
||||||
long_u todo;
|
long_u todo = 1;
|
||||||
|
long_u used;
|
||||||
|
|
||||||
/* Need to start all over every time, because func_free() may change the
|
/* First clear what the functions contain. Since this may lower the
|
||||||
* hash table. */
|
* reference count of a function, it may also free a function and change
|
||||||
|
* the hash table. Restart if that happens. */
|
||||||
|
while (todo > 0)
|
||||||
|
{
|
||||||
|
todo = func_hashtab.ht_used;
|
||||||
|
for (hi = func_hashtab.ht_array; todo > 0; ++hi)
|
||||||
|
if (!HASHITEM_EMPTY(hi))
|
||||||
|
{
|
||||||
|
/* Only free functions that are not refcounted, those are
|
||||||
|
* supposed to be freed when no longer referenced. */
|
||||||
|
fp = HI2UF(hi);
|
||||||
|
if (func_name_refcount(fp->uf_name))
|
||||||
|
++skipped;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
used = func_hashtab.ht_used;
|
||||||
|
func_clear(fp, TRUE);
|
||||||
|
if (used != func_hashtab.ht_used)
|
||||||
|
{
|
||||||
|
skipped = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--todo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now actually free the functions. Need to start all over every time,
|
||||||
|
* because func_free() may change the hash table. */
|
||||||
|
skipped = 0;
|
||||||
while (func_hashtab.ht_used > skipped)
|
while (func_hashtab.ht_used > skipped)
|
||||||
{
|
{
|
||||||
todo = func_hashtab.ht_used;
|
todo = func_hashtab.ht_used;
|
||||||
@@ -1138,7 +1192,7 @@ free_all_functions(void)
|
|||||||
++skipped;
|
++skipped;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
func_free(fp, TRUE);
|
func_free(fp);
|
||||||
skipped = 0;
|
skipped = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1356,7 +1410,7 @@ call_func(
|
|||||||
if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
|
if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
|
||||||
/* Function was unreferenced while being used, free it
|
/* Function was unreferenced while being used, free it
|
||||||
* now. */
|
* now. */
|
||||||
func_free(fp, FALSE);
|
func_clear_free(fp, FALSE);
|
||||||
if (did_save_redo)
|
if (did_save_redo)
|
||||||
restoreRedobuff();
|
restoreRedobuff();
|
||||||
restore_search_patterns();
|
restore_search_patterns();
|
||||||
@@ -2756,7 +2810,7 @@ ex_delfunction(exarg_T *eap)
|
|||||||
fp->uf_flags |= FC_DELETED;
|
fp->uf_flags |= FC_DELETED;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
func_free(fp, FALSE);
|
func_clear_free(fp, FALSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2785,7 +2839,7 @@ func_unref(char_u *name)
|
|||||||
/* Only delete it when it's not being used. Otherwise it's done
|
/* Only delete it when it's not being used. Otherwise it's done
|
||||||
* when "uf_calls" becomes zero. */
|
* when "uf_calls" becomes zero. */
|
||||||
if (fp->uf_calls == 0)
|
if (fp->uf_calls == 0)
|
||||||
func_free(fp, FALSE);
|
func_clear_free(fp, FALSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2801,7 +2855,7 @@ func_ptr_unref(ufunc_T *fp)
|
|||||||
/* Only delete it when it's not being used. Otherwise it's done
|
/* Only delete it when it's not being used. Otherwise it's done
|
||||||
* when "uf_calls" becomes zero. */
|
* when "uf_calls" becomes zero. */
|
||||||
if (fp->uf_calls == 0)
|
if (fp->uf_calls == 0)
|
||||||
func_free(fp, FALSE);
|
func_clear_free(fp, FALSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -764,6 +764,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 */
|
||||||
|
/**/
|
||||||
|
297,
|
||||||
/**/
|
/**/
|
||||||
296,
|
296,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user