diff options
author | Bram Moolenaar <Bram@vim.org> | 2016-03-22 22:34:03 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2016-03-22 22:34:03 +0100 |
commit | 5f436fcf9960c95702820d5ac1b8b612995f6c04 (patch) | |
tree | 5599b31e2da0509bf8f561965e3abdcf1044a97c /src | |
parent | e4eb6ff089e79e659acf33c17dd0fda7177de526 (diff) | |
download | vim-5f436fcf9960c95702820d5ac1b8b612995f6c04.zip |
patch 7.4.1639
Problem: Invoking garbage collection may cause a double free.
Solution: Don't free the dict in a partial when recursive is FALSE.
Diffstat (limited to 'src')
-rw-r--r-- | src/eval.c | 77 | ||||
-rw-r--r-- | src/version.c | 2 |
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, |