diff options
author | Karl Williamson <khw@cpan.org> | 2019-05-03 13:57:47 -0600 |
---|---|---|
committer | Karl Williamson <khw@cpan.org> | 2019-05-24 17:09:30 -0600 |
commit | 814735a391b874af8f00eaf89469e5ec7f38cd4a (patch) | |
tree | b7f652cf8c5eac3042fcc8304ff8df3418f573c7 /pp.c | |
parent | bae047b68c92622bb4bb04499e36cdaa48138909 (diff) | |
download | perl-814735a391b874af8f00eaf89469e5ec7f38cd4a.tar.gz |
Remove undefined behavior from IV shifting
It is undefined behavior to shift a negative integer to the left. This
commit avoids that by treating the value as unsigned, then casting back
to integer for return.
Diffstat (limited to 'pp.c')
-rw-r--r-- | pp.c | 21 |
1 files changed, 20 insertions, 1 deletions
@@ -1991,11 +1991,30 @@ static IV S_iv_shift(IV iv, int shift, bool left) shift = -shift; left = !left; } + if (UNLIKELY(shift >= IV_BITS)) { return iv < 0 && !left ? -1 : 0; } - return left ? iv << shift : iv >> shift; + /* For left shifts, perl 5 has chosen to treat the value as unsigned for + * the * purposes of shifting, then cast back to signed. This is very + * different from perl 6: + * + * $ perl6 -e 'say -2 +< 5' + * -64 + * + * $ ./perl -le 'print -2 << 5' + * 18446744073709551552 + * */ + if (left) { + if (iv == IV_MIN) { /* Casting this to a UV is undefined behavior */ + return 0; + } + return (IV) (((UV) iv) << shift); + } + + /* Here is right shift */ + return iv >> shift; } #define UV_LEFT_SHIFT(uv, shift) S_uv_shift(uv, shift, TRUE) |