summaryrefslogtreecommitdiff
path: root/hv.c
diff options
context:
space:
mode:
authorTon Hospel <me-02@ton.iguana.be>2011-05-16 08:33:07 -0700
committerFather Chrysostomos <sprout@cpan.org>2011-05-19 17:09:46 -0700
commitae19993915a27e9fb5ee79b7a2624dba0bd876cd (patch)
tree3d471cddcbb95cc0b57649a01d89ad8aa84c4cfc /hv.c
parent57f6eff5f803b7abef8aa97b0d7609a181b9b4d7 (diff)
downloadperl-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.c12
1 files changed, 10 insertions, 2 deletions
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. */