diff options
author | Ton Hospel <me-02@ton.iguana.be> | 2011-05-16 08:33:07 -0700 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2011-05-19 17:09:46 -0700 |
commit | ae19993915a27e9fb5ee79b7a2624dba0bd876cd (patch) | |
tree | 3d471cddcbb95cc0b57649a01d89ad8aa84c4cfc /hv.c | |
parent | 57f6eff5f803b7abef8aa97b0d7609a181b9b4d7 (diff) | |
download | perl-ae19993915a27e9fb5ee79b7a2624dba0bd876cd.tar.gz |
[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.
Diffstat (limited to 'hv.c')
-rw-r--r-- | hv.c | 12 |
1 files changed, 10 insertions, 2 deletions
@@ -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. */ |