summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2016-03-22 22:34:03 +0100
committerBram Moolenaar <Bram@vim.org>2016-03-22 22:34:03 +0100
commit5f436fcf9960c95702820d5ac1b8b612995f6c04 (patch)
tree5599b31e2da0509bf8f561965e3abdcf1044a97c
parente4eb6ff089e79e659acf33c17dd0fda7177de526 (diff)
downloadvim-git-5f436fcf9960c95702820d5ac1b8b612995f6c04.tar.gz
patch 7.4.1639v7.4.1639
Problem: Invoking garbage collection may cause a double free. Solution: Don't free the dict in a partial when recursive is FALSE.
-rw-r--r--src/eval.c77
-rw-r--r--src/version.c2
2 files changed, 50 insertions, 29 deletions
diff --git a/src/eval.c b/src/eval.c
index d922e6aef..990fa8c05 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -209,7 +209,9 @@ static hashtab_T func_hashtab;
/* The names of packages that once were loaded are remembered. */
static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
-/* list heads for garbage collection */
+/* List heads for garbage collection. Although there can be a reference loop
+ * from partial to dict to partial, we don't need to keep track of the partial,
+ * since it will get freed when the dict is unused and gets freed. */
static dict_T *first_dict = NULL; /* list of all dicts */
static list_T *first_list = NULL; /* list of all lists */
@@ -7130,9 +7132,14 @@ set_ref_in_item(
list_T *ll;
int abort = FALSE;
- if (tv->v_type == VAR_DICT)
+ if (tv->v_type == VAR_DICT || tv->v_type == VAR_PARTIAL)
{
- dd = tv->vval.v_dict;
+ if (tv->v_type == VAR_DICT)
+ dd = tv->vval.v_dict;
+ else if (tv->vval.v_partial != NULL)
+ dd = tv->vval.v_partial->pt_dict;
+ else
+ dd = NULL;
if (dd != NULL && dd->dv_copyID != copyID)
{
/* Didn't see this dict yet. */
@@ -7184,6 +7191,32 @@ set_ref_in_item(
return abort;
}
+ static void
+partial_free(partial_T *pt, int free_dict)
+{
+ int i;
+
+ for (i = 0; i < pt->pt_argc; ++i)
+ clear_tv(&pt->pt_argv[i]);
+ vim_free(pt->pt_argv);
+ if (free_dict)
+ dict_unref(pt->pt_dict);
+ func_unref(pt->pt_name);
+ vim_free(pt->pt_name);
+ vim_free(pt);
+}
+
+/*
+ * Unreference a closure: decrement the reference count and free it when it
+ * becomes zero.
+ */
+ void
+partial_unref(partial_T *pt)
+{
+ if (pt != NULL && --pt->pt_refcount <= 0)
+ partial_free(pt, TRUE);
+}
+
/*
* Allocate an empty header for a dictionary.
*/
@@ -7275,7 +7308,18 @@ dict_free(
hash_remove(&d->dv_hashtab, hi);
if (recurse || (di->di_tv.v_type != VAR_LIST
&& di->di_tv.v_type != VAR_DICT))
- clear_tv(&di->di_tv);
+ {
+ if (!recurse && di->di_tv.v_type == VAR_PARTIAL)
+ {
+ partial_T *pt = di->di_tv.vval.v_partial;
+
+ /* We unref the partial but not the dict it refers to. */
+ if (pt != NULL && --pt->pt_refcount == 0)
+ partial_free(pt, FALSE);
+ }
+ else
+ clear_tv(&di->di_tv);
+ }
vim_free(di);
--todo;
}
@@ -12011,31 +12055,6 @@ f_function(typval_T *argvars, typval_T *rettv)
}
}
- static void
-partial_free(partial_T *pt)
-{
- int i;
-
- for (i = 0; i < pt->pt_argc; ++i)
- clear_tv(&pt->pt_argv[i]);
- vim_free(pt->pt_argv);
- dict_unref(pt->pt_dict);
- func_unref(pt->pt_name);
- vim_free(pt->pt_name);
- vim_free(pt);
-}
-
-/*
- * Unreference a closure: decrement the reference count and free it when it
- * becomes zero.
- */
- void
-partial_unref(partial_T *pt)
-{
- if (pt != NULL && --pt->pt_refcount <= 0)
- partial_free(pt);
-}
-
/*
* "garbagecollect()" function
*/
diff --git a/src/version.c b/src/version.c
index 1a02fbe92..6fffeeb76 100644
--- a/src/version.c
+++ b/src/version.c
@@ -749,6 +749,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1639,
+/**/
1638,
/**/
1637,