summaryrefslogtreecommitdiff
path: root/pp.c
diff options
context:
space:
mode:
authorTAKAI Kousuke <62541129+t-a-k@users.noreply.github.com>2021-04-05 23:59:14 +0900
committerKarl Williamson <khw@cpan.org>2021-04-16 17:16:24 -0600
commit640be82afb9c53a7c1c14d80eee36b6081304db0 (patch)
treec458b5b69d184c77e86fd0cccb746bc423dd358e /pp.c
parent50352f1687ab2f02685a380668c889111cdeeee9 (diff)
downloadperl-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.c20
1 files changed, 18 insertions, 2 deletions
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));
}