From 640be82afb9c53a7c1c14d80eee36b6081304db0 Mon Sep 17 00:00:00 2001 From: TAKAI Kousuke <62541129+t-a-k@users.noreply.github.com> Date: Mon, 5 Apr 2021 23:59:14 +0900 Subject: 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). --- pp.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'pp.c') diff --git a/pp.c b/pp.c index baf0777a47..68b4e46156 100644 --- a/pp.c +++ b/pp.c @@ -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)); } -- cgit v1.2.1