diff options
author | TAKAI Kousuke <62541129+t-a-k@users.noreply.github.com> | 2021-04-05 23:59:14 +0900 |
---|---|---|
committer | Karl Williamson <khw@cpan.org> | 2021-04-16 17:16:24 -0600 |
commit | 640be82afb9c53a7c1c14d80eee36b6081304db0 (patch) | |
tree | c458b5b69d184c77e86fd0cccb746bc423dd358e /pp.c | |
parent | 50352f1687ab2f02685a380668c889111cdeeee9 (diff) | |
download | perl-640be82afb9c53a7c1c14d80eee36b6081304db0.tar.gz |
pp.c: Clip shift amount of <<, >> to avoid overflow
Previously the right operand of bitwise shift operators (shift amount)
was implicitly cast from IV to int, but it might lead wrong results
if IV does not fit in int.
And also, shifting INT_MIN bits used to yield the shiftee unchanged
(treated as 0-bit shift instead of negative shift).
Diffstat (limited to 'pp.c')
-rw-r--r-- | pp.c | 20 |
1 files changed, 18 insertions, 2 deletions
@@ -1973,6 +1973,22 @@ PP(pp_subtract) #define IV_BITS (IVSIZE * 8) +/* Taking the right operand of bitwise shift operators, returns an int + * indicating the shift amount clipped to the range [-IV_BITS, +IV_BITS]. + */ +static int +S_shift_amount(pTHX_ SV *const svr) +{ + const IV iv = SvIV_nomg(svr); + + /* Note that [INT_MIN, INT_MAX] cannot be used as the clipping bound; + * INT_MIN will cause overflow in "shift = -shift;" in S_{iv,uv}_shift. + */ + if (SvIsUV(svr)) + return SvUVX(svr) > IV_BITS ? IV_BITS : (int)SvUVX(svr); + return iv < -IV_BITS ? -IV_BITS : iv > IV_BITS ? IV_BITS : (int)iv; +} + static UV S_uv_shift(UV uv, int shift, bool left) { if (shift < 0) { @@ -2026,7 +2042,7 @@ PP(pp_left_shift) svr = POPs; svl = TOPs; { - const IV shift = SvIV_nomg(svr); + const int shift = S_shift_amount(aTHX_ svr); if (PL_op->op_private & HINT_INTEGER) { SETi(IV_LEFT_SHIFT(SvIV_nomg(svl), shift)); } @@ -2044,7 +2060,7 @@ PP(pp_right_shift) svr = POPs; svl = TOPs; { - const IV shift = SvIV_nomg(svr); + const int shift = S_shift_amount(aTHX_ svr); if (PL_op->op_private & HINT_INTEGER) { SETi(IV_RIGHT_SHIFT(SvIV_nomg(svl), shift)); } |