vim-patch:7.4.1715

Problem:    Double free when a partial is in a cycle with a list or dict.
            (Nikolai Pavlov)
Solution:   Do not free a nested list or dict used by the partial.

ddecc25947
This commit is contained in:
Michael Ennen 2016-10-28 12:24:03 -07:00 committed by James McCoy
parent c6bc1e7bab
commit 0645787741
2 changed files with 65 additions and 44 deletions

View File

@ -4894,10 +4894,52 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
return OK; return OK;
} }
/* static void partial_free(partial_T *pt, bool recursive)
* Allocate a variable for a List and fill it from "*arg". {
* Return OK or FAIL. int i;
*/
for (i = 0; i < pt->pt_argc; i++) {
typval_T *tv = &pt->pt_argv[i];
if (recursive || (tv->v_type != VAR_DICT && tv->v_type != VAR_LIST)) {
clear_tv(&pt->pt_argv[i]);
}
}
xfree(pt->pt_argv);
if (recursive) {
dict_unref(pt->pt_dict);
}
func_unref(pt->pt_name);
xfree(pt->pt_name);
xfree(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);
}
}
/// Like clear_tv(), but do not free lists or dictionaries.
/// This is when called via free_unref_items().
static void clear_tv_no_recurse(typval_T *tv) {
if (tv->v_type == VAR_PARTIAL) {
partial_T *pt = tv->vval.v_partial;
// We unref the partial but not the dict or any list it refers to
if (pt != NULL && --pt->pt_refcount == 0) {
partial_free(pt, false);
}
} else if (tv->v_type != VAR_LIST && tv->v_type != VAR_DICT) {
clear_tv(tv);
}
}
/// Allocate a variable for a List and fill it from "*arg".
/// Return OK or FAIL.
static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
{ {
list_T *l = NULL; list_T *l = NULL;
@ -5009,9 +5051,11 @@ list_free (
for (item = l->lv_first; item != NULL; item = l->lv_first) { for (item = l->lv_first; item != NULL; item = l->lv_first) {
/* Remove the item before deleting it. */ /* Remove the item before deleting it. */
l->lv_first = item->li_next; l->lv_first = item->li_next;
if (recurse || (item->li_tv.v_type != VAR_LIST if (recurse) {
&& item->li_tv.v_type != VAR_DICT))
clear_tv(&item->li_tv); clear_tv(&item->li_tv);
} else {
clear_tv_no_recurse(&item->li_tv);
}
xfree(item); xfree(item);
} }
xfree(l); xfree(l);
@ -6055,6 +6099,16 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
} }
} }
} }
if (tv->v_type == VAR_PARTIAL) {
partial_T *pt = tv->vval.v_partial;
int i;
if (pt != NULL) {
for (i = 0; i < pt->pt_argc; i++) {
abort = set_ref_in_item(&pt->pt_argv[i], copyID, ht_stack, list_stack);
}
}
}
break; break;
} }
@ -6137,31 +6191,6 @@ static inline bool set_ref_dict(dict_T *dict, int copyID)
return false; return false;
} }
static void partial_free(partial_T *pt, bool free_dict)
{
int i;
for (i = 0; i < pt->pt_argc; i++) {
clear_tv(&pt->pt_argv[i]);
}
xfree(pt->pt_argv);
if (free_dict) {
dict_unref(pt->pt_dict);
}
func_unref(pt->pt_name);
xfree(pt->pt_name);
xfree(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. * Allocate an empty header for a dictionary.
*/ */
@ -6263,18 +6292,10 @@ dict_free (
* something recursive causing trouble. */ * something recursive causing trouble. */
di = HI2DI(hi); di = HI2DI(hi);
hash_remove(&d->dv_hashtab, hi); hash_remove(&d->dv_hashtab, hi);
if (recurse || (di->di_tv.v_type != VAR_LIST if (recurse) {
&& di->di_tv.v_type != VAR_DICT)) { clear_tv(&di->di_tv);
if (!recurse && di->di_tv.v_type == VAR_PARTIAL) { } else {
partial_T *pt = di->di_tv.vval.v_partial; clear_tv_no_recurse(&di->di_tv);
// 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);
}
} }
xfree(di); xfree(di);
--todo; --todo;

View File

@ -727,7 +727,7 @@ static int included_patches[] = {
// 1718, // 1718,
// 1717 NA // 1717 NA
1716, 1716,
// 1715, 1715,
1714, 1714,
// 1713 NA // 1713 NA
1712, 1712,