summaryrefslogtreecommitdiff
path: root/pp_hot.c
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2012-12-10 05:43:41 -0800
committerFather Chrysostomos <sprout@cpan.org>2012-12-11 08:59:42 -0800
commit632b9d6f6a19a87ee168ebb81494b7df13c2eeb0 (patch)
tree3e38ee3efe01d8efd0a51d300aea8deb646ed1f3 /pp_hot.c
parent88e2091baeacd9a40eab4dac8601e88b3797e385 (diff)
downloadperl-632b9d6f6a19a87ee168ebb81494b7df13c2eeb0.tar.gz
pp_hot.c:pp_aassign: mortalise variable only if we have to
This affects the hash-assignment path. Don’t mortalise the value to protect it from a magical key making the hv_store_ent call die, as that could unduly extend the mortals stack. Instead, copy the key if it is magical. Based on a patch by Ruslan Zakirov.
Diffstat (limited to 'pp_hot.c')
-rw-r--r--pp_hot.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/pp_hot.c b/pp_hot.c
index 76a9e43b20..16fcd23ecb 100644
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -1096,10 +1096,18 @@ PP(pp_aassign)
while (relem < lastrelem+odd) { /* gobble up all the rest */
HE *didstore;
assert(*relem);
- sv = lval ? sv_mortalcopy(*relem) : *relem;
+ /* Copy the key if aassign is called in lvalue context,
+ to avoid having the next op modify our rhs. Copy
+ it also if it is gmagical, lest it make the
+ hv_store_ent call below croak, leaking the value. */
+ sv = lval || SvGMAGICAL(*relem)
+ ? sv_mortalcopy(*relem)
+ : *relem;
relem++;
assert(*relem);
- tmpstr = sv_mortalcopy( *relem++ ); /* value */
+ SvGETMAGIC(*relem);
+ tmpstr = newSV(0);
+ sv_setsv_nomg(tmpstr,*relem++); /* value */
if (gimme == G_ARRAY) {
if (hv_exists_ent(hash, sv, 0))
/* key overwrites an existing entry */
@@ -1112,8 +1120,10 @@ PP(pp_aassign)
}
}
didstore = hv_store_ent(hash,sv,tmpstr,0);
- if (didstore) SvREFCNT_inc_simple_void_NN(tmpstr);
- if (magic) SvSETMAGIC(tmpstr);
+ if (magic) {
+ if (!didstore) sv_2mortal(tmpstr);
+ SvSETMAGIC(tmpstr);
+ }
TAINT_NOT;
}
LEAVE;