diff options
author | David Mitchell <davem@iabyn.com> | 2017-03-31 13:44:58 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2017-03-31 14:13:24 +0100 |
commit | 1b92e6949b737e92f61827f9c92afce9218e30ba (patch) | |
tree | dc6337456cd4e6bd0fe98c168602bcad4a2bedb1 /pp.c | |
parent | 1190bf17f4131bb86c6cd460c77e9d02894ca2ec (diff) | |
download | perl-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.c | 40 |
1 files changed, 14 insertions, 26 deletions
@@ -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); |