summaryrefslogtreecommitdiff
path: root/pp.c
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2017-03-15 14:35:59 +0000
committerDavid Mitchell <davem@iabyn.com>2017-03-17 14:13:40 +0000
commitd69c43040e4967294b1195ecfdc4acf0f74b5958 (patch)
tree239ab78aa25726bed3e85e66a6a9e6677d5102f9 /pp.c
parent7e337d2de5bfdccdeeb8d3f2f24f559ff905770a (diff)
downloadperl-d69c43040e4967294b1195ecfdc4acf0f74b5958.tar.gz
Perl_do_vecget(): change offset arg to STRLEN type
... and fix up its caller, pp_vec(). This is part of a fix for RT #130915. pp_vec() is responsible for extracting out the offset and size from SVs on the stack, and then calling do_vecget() with those values. (Sometimes the call is done indirectly by storing the offset in the LvTARGOFF() field of a SVt_PVLV, then later Perl_magic_getvec() passes the LvTARGOFF() value to do_vecget().) Now SvCUR, SvLEN and LvTARGOFF are all of type STRLEN (a.k.a Size_t), while the offset arg of do_vecget() is of type SSize_t (i.e. there's a signed/unsigned mismatch). It makes more sense to make the arg of type STRLEN. So that is what this commit does. At the same time this commit fixes up pp_vec() to handle all the possibilities where the offset value can't fit into a STRLEN, returning 0 or croaking accordingly, so that do_vecget() is never called with a truncated or wrapped offset. The next commit will fix up the internals of do_vecget() and do_vecset(), which have to worry about offset*(2^n) wrapping or being > SvCUR(). This commit is based on an earlier proposed fix by Aaron Crane.
Diffstat (limited to 'pp.c')
-rw-r--r--pp.c40
1 files changed, 38 insertions, 2 deletions
diff --git a/pp.c b/pp.c
index a640995e31..a6b30412b5 100644
--- a/pp.c
+++ b/pp.c
@@ -3473,10 +3473,45 @@ PP(pp_vec)
{
dSP;
const IV size = POPi;
- const IV offset = POPi;
+ SV* offsetsv = POPs;
SV * const src = POPs;
const I32 lvalue = PL_op->op_flags & OPf_MOD || LVRET;
SV * ret;
+ UV retuv = 0;
+ STRLEN offset;
+
+ /* extract a STRLEN-ranged integer value from offsetsv into offset,
+ * or die trying */
+ {
+ 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 PTRSIZE < IVSIZE
+ if (iv > Size_t_MAX) {
+ if (!lvalue)
+ goto return_val; /* out of range: return 0 */
+ Perl_croak_nocontext("Out of memory!");
+ }
+#endif
+
+ offset = (STRLEN)iv;
+ }
+
+ retuv = do_vecget(src, offset, size);
+
+ return_val:
if (lvalue) { /* it's an lvalue! */
ret = sv_2mortal(newSV_type(SVt_PVLV)); /* Not TARG RT#67838 */
@@ -3492,7 +3527,8 @@ PP(pp_vec)
ret = TARG;
}
- sv_setuv(ret, do_vecget(src, offset, size));
+
+ sv_setuv(ret, retuv);
if (!lvalue)
SvSETMAGIC(ret);
PUSHs(ret);