From ae19993915a27e9fb5ee79b7a2624dba0bd876cd Mon Sep 17 00:00:00 2001 From: Ton Hospel Date: Mon, 16 May 2011 08:33:07 -0700 Subject: [perl #85026] deleting elements in a HASH iterator Internally a perl HASH is an array of single linked chains of entries. Deleting an element means removing the correct chain entry by replacing the pointer to the removed entry with a pointer to the next entry and then freeing the deleted entry However, if the deleted element is the current entry the deleted entry is kept after removing it from the chain and the LAZYDEL flag is set. Only on the next iteration is the element actually removed and the iterator is set to the next entry. However, if you delete the current iterator and then delete the next element in the same chain the "next" pointer of the iterator is not updated because the iterator is not on the chain anymore. That means that when the next iteration looks up the iterator next pointer it will point to the freed memory of the second element. This patch fixes the places where the delete is done. Drawback is that you may never forget to do the lazydel fixup in at any place where the entry chain gets shortened. --- hv.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'hv.c') diff --git a/hv.c b/hv.c index f9eda830ec..a445a2f76d 100644 --- a/hv.c +++ b/hv.c @@ -1063,8 +1063,12 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen, *oentry = HeNEXT(entry); if (SvOOK(hv) && entry == HvAUX(hv)->xhv_eiter /* HvEITER(hv) */) HvLAZYDEL_on(hv); - else + else { + if (SvOOK(hv) && HvLAZYDEL(hv) && + entry == HeNEXT(HvAUX(hv)->xhv_eiter)) + HeNEXT(HvAUX(hv)->xhv_eiter) = HeNEXT(entry); hv_free_ent(hv, entry); + } xhv->xhv_keys--; /* HvTOTALKEYS(hv)-- */ if (xhv->xhv_keys == 0) HvHASKFLAGS_off(hv); @@ -1623,8 +1627,12 @@ S_clear_placeholders(pTHX_ HV *hv, U32 items) *oentry = HeNEXT(entry); if (entry == HvEITER_get(hv)) HvLAZYDEL_on(hv); - else + else { + if (SvOOK(hv) && HvLAZYDEL(hv) && + entry == HeNEXT(HvAUX(hv)->xhv_eiter)) + HeNEXT(HvAUX(hv)->xhv_eiter) = HeNEXT(entry); hv_free_ent(hv, entry); + } if (--items == 0) { /* Finished. */ -- cgit v1.2.1