Skip to content

Commit 03ff9bc

Browse files
committed
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)
1 parent fd8983b commit 03ff9bc

File tree

3 files changed

+69
-12
lines changed

3 files changed

+69
-12
lines changed

src/structs.h

+1
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,7 @@ typedef struct
13371337
int uf_varargs; /* variable nr of arguments */
13381338
int uf_flags;
13391339
int uf_calls; /* nr of active calls */
1340+
int uf_cleared; /* func_clear() was already called */
13401341
garray_T uf_args; /* arguments */
13411342
garray_T uf_lines; /* function lines */
13421343
#ifdef FEAT_PROFILE

src/userfunc.c

+66-12
Original file line numberDiff line numberDiff line change
@@ -1075,12 +1075,17 @@ func_remove(ufunc_T *fp)
10751075
}
10761076

10771077
/*
1078-
* Free a function and remove it from the list of functions.
1078+
* Free all things that a function contains. Does not free the function
1079+
* itself, use func_free() for that.
10791080
* When "force" is TRUE we are exiting.
10801081
*/
10811082
static void
1082-
func_free(ufunc_T *fp, int force)
1083+
func_clear(ufunc_T *fp, int force)
10831084
{
1085+
if (fp->uf_cleared)
1086+
return;
1087+
fp->uf_cleared = TRUE;
1088+
10841089
/* clear this function */
10851090
ga_clear_strings(&(fp->uf_args));
10861091
ga_clear_strings(&(fp->uf_lines));
@@ -1089,16 +1094,35 @@ func_free(ufunc_T *fp, int force)
10891094
vim_free(fp->uf_tml_total);
10901095
vim_free(fp->uf_tml_self);
10911096
#endif
1097+
funccal_unref(fp->uf_scoped, fp, force);
1098+
}
1099+
1100+
/*
1101+
* Free a function and remove it from the list of functions. Does not free
1102+
* what a function contains, call func_clear() first.
1103+
*/
1104+
static void
1105+
func_free(ufunc_T *fp)
1106+
{
10921107
/* only remove it when not done already, otherwise we would remove a newer
10931108
* version of the function */
10941109
if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0)
10951110
func_remove(fp);
10961111

1097-
funccal_unref(fp->uf_scoped, fp, force);
1098-
10991112
vim_free(fp);
11001113
}
11011114

1115+
/*
1116+
* Free all things that a function contains and free the function itself.
1117+
* When "force" is TRUE we are exiting.
1118+
*/
1119+
static void
1120+
func_clear_free(ufunc_T *fp, int force)
1121+
{
1122+
func_clear(fp, force);
1123+
func_free(fp);
1124+
}
1125+
11021126
/*
11031127
* There are two kinds of function names:
11041128
* 1. ordinary names, function defined with :function
@@ -1120,10 +1144,40 @@ free_all_functions(void)
11201144
hashitem_T *hi;
11211145
ufunc_T *fp;
11221146
long_u skipped = 0;
1123-
long_u todo;
1147+
long_u todo = 1;
1148+
long_u used;
1149+
1150+
/* First clear what the functions contain. Since this may lower the
1151+
* reference count of a function, it may also free a function and change
1152+
* the hash table. Restart if that happens. */
1153+
while (todo > 0)
1154+
{
1155+
todo = func_hashtab.ht_used;
1156+
for (hi = func_hashtab.ht_array; todo > 0; ++hi)
1157+
if (!HASHITEM_EMPTY(hi))
1158+
{
1159+
/* Only free functions that are not refcounted, those are
1160+
* supposed to be freed when no longer referenced. */
1161+
fp = HI2UF(hi);
1162+
if (func_name_refcount(fp->uf_name))
1163+
++skipped;
1164+
else
1165+
{
1166+
used = func_hashtab.ht_used;
1167+
func_clear(fp, TRUE);
1168+
if (used != func_hashtab.ht_used)
1169+
{
1170+
skipped = 0;
1171+
break;
1172+
}
1173+
}
1174+
--todo;
1175+
}
1176+
}
11241177

1125-
/* Need to start all over every time, because func_free() may change the
1126-
* hash table. */
1178+
/* Now actually free the functions. Need to start all over every time,
1179+
* because func_free() may change the hash table. */
1180+
skipped = 0;
11271181
while (func_hashtab.ht_used > skipped)
11281182
{
11291183
todo = func_hashtab.ht_used;
@@ -1138,7 +1192,7 @@ free_all_functions(void)
11381192
++skipped;
11391193
else
11401194
{
1141-
func_free(fp, TRUE);
1195+
func_free(fp);
11421196
skipped = 0;
11431197
break;
11441198
}
@@ -1356,7 +1410,7 @@ call_func(
13561410
if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
13571411
/* Function was unreferenced while being used, free it
13581412
* now. */
1359-
func_free(fp, FALSE);
1413+
func_clear_free(fp, FALSE);
13601414
if (did_save_redo)
13611415
restoreRedobuff();
13621416
restore_search_patterns();
@@ -2756,7 +2810,7 @@ ex_delfunction(exarg_T *eap)
27562810
fp->uf_flags |= FC_DELETED;
27572811
}
27582812
else
2759-
func_free(fp, FALSE);
2813+
func_clear_free(fp, FALSE);
27602814
}
27612815
}
27622816
}
@@ -2785,7 +2839,7 @@ func_unref(char_u *name)
27852839
/* Only delete it when it's not being used. Otherwise it's done
27862840
* when "uf_calls" becomes zero. */
27872841
if (fp->uf_calls == 0)
2788-
func_free(fp, FALSE);
2842+
func_clear_free(fp, FALSE);
27892843
}
27902844
}
27912845

@@ -2801,7 +2855,7 @@ func_ptr_unref(ufunc_T *fp)
28012855
/* Only delete it when it's not being used. Otherwise it's done
28022856
* when "uf_calls" becomes zero. */
28032857
if (fp->uf_calls == 0)
2804-
func_free(fp, FALSE);
2858+
func_clear_free(fp, FALSE);
28052859
}
28062860
}
28072861

src/version.c

+2
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,8 @@ static char *(features[]) =
764764

765765
static int included_patches[] =
766766
{ /* Add new patch number below this line */
767+
/**/
768+
297,
767769
/**/
768770
296,
769771
/**/

0 commit comments

Comments
 (0)