]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 7.4.1639 v7.4.1639
authorBram Moolenaar <Bram@vim.org>
Tue, 22 Mar 2016 21:34:03 +0000 (22:34 +0100)
committerBram Moolenaar <Bram@vim.org>
Tue, 22 Mar 2016 21:34:03 +0000 (22:34 +0100)
Problem:    Invoking garbage collection may cause a double free.
Solution:   Don't free the dict in a partial when recursive is FALSE.

src/eval.c
src/version.c

index d922e6aef943bd6dd1d71e9784df5b12abe41957..990fa8c05bda98ae092c7070a43a8443279a2ec0 100644 (file)
@@ -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
  */
index 1a02fbe921cdd7c84495eabad3b3c924681c9090..6fffeeb7617a39d39c7518ef6c4e552743819562 100644 (file)
@@ -748,6 +748,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1639,
 /**/
     1638,
 /**/