summaryrefslogtreecommitdiff
path: root/pp.c
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2017-03-31 13:44:58 +0100
committerDavid Mitchell <davem@iabyn.com>2017-03-31 14:13:24 +0100
commit1b92e6949b737e92f61827f9c92afce9218e30ba (patch)
treedc6337456cd4e6bd0fe98c168602bcad4a2bedb1 /pp.c
parent1190bf17f4131bb86c6cd460c77e9d02894ca2ec (diff)
downloadperl-1b92e6949b737e92f61827f9c92afce9218e30ba.tar.gz
vec(): defer lvalue out-of-range croaking
RT #131083 Recent commits v5.25.10-81-gd69c430 and v5.25.10-82-g67dd6f3 added out-of-range/overflow checks for the offset arg of vec(). However in lvalue context, these croaks now happen before the SVt_PVLV was created, rather than when its set magic was called. This means that something like sub f { $x = $_[0] } f(vec($s, -1, 8)) now croaks even though the out-of-range value never ended up getting used in lvalue context. This commit fixes things by, in pp_vec(), rather than croaking, just set flag bits in LvFLAGS() to indicate that the offset is -Ve / out-of-range. Then in Perl_magic_getvec(), return 0 if these flags are set, and in Perl_magic_setvec() croak with a suitable error.
Diffstat (limited to 'pp.c')
-rw-r--r--pp.c40
1 files changed, 14 insertions, 26 deletions
diff --git a/pp.c b/pp.c
index a6b30412b5..cc4cb59f7d 100644
--- a/pp.c
+++ b/pp.c
@@ -3477,41 +3477,29 @@ PP(pp_vec)
SV * const src = POPs;
const I32 lvalue = PL_op->op_flags & OPf_MOD || LVRET;
SV * ret;
- UV retuv = 0;
- STRLEN offset;
+ UV retuv;
+ STRLEN offset = 0;
+ char errflags = 0;
/* extract a STRLEN-ranged integer value from offsetsv into offset,
- * or die trying */
+ * or flag that its out of range */
{
IV iv = SvIV(offsetsv);
/* avoid a large UV being wrapped to a negative value */
- if (SvIOK_UV(offsetsv) && SvUVX(offsetsv) > (UV)IV_MAX) {
- if (!lvalue)
- goto return_val; /* out of range: return 0 */
- Perl_croak_nocontext("Out of memory!");
- }
-
- if (iv < 0) {
- if (!lvalue)
- goto return_val; /* out of range: return 0 */
- Perl_croak_nocontext("Negative offset to vec in lvalue context");
- }
-
+ if (SvIOK_UV(offsetsv) && SvUVX(offsetsv) > (UV)IV_MAX)
+ errflags = 4; /* out of range */
+ else if (iv < 0)
+ errflags = (1|4); /* negative offset, out of range */
#if PTRSIZE < IVSIZE
- if (iv > Size_t_MAX) {
- if (!lvalue)
- goto return_val; /* out of range: return 0 */
- Perl_croak_nocontext("Out of memory!");
- }
+ else if (iv > Size_t_MAX)
+ errflags = 4; /* out of range */
#endif
-
- offset = (STRLEN)iv;
+ else
+ offset = (STRLEN)iv;
}
- retuv = do_vecget(src, offset, size);
-
- return_val:
+ retuv = errflags ? 0 : do_vecget(src, offset, size);
if (lvalue) { /* it's an lvalue! */
ret = sv_2mortal(newSV_type(SVt_PVLV)); /* Not TARG RT#67838 */
@@ -3520,6 +3508,7 @@ PP(pp_vec)
LvTARG(ret) = SvREFCNT_inc_simple(src);
LvTARGOFF(ret) = offset;
LvTARGLEN(ret) = size;
+ LvFLAGS(ret) = errflags;
}
else {
dTARGET;
@@ -3527,7 +3516,6 @@ PP(pp_vec)
ret = TARG;
}
-
sv_setuv(ret, retuv);
if (!lvalue)
SvSETMAGIC(ret);