From 7e5b390a008ccad094a39c350f385d58e8a5102a Mon Sep 17 00:00:00 2001 From: Karl Williamson Date: Fri, 3 May 2019 13:57:47 -0600 Subject: [PATCH] Remove undefined behavior from IV shifting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. Petr Písař: Ported to 5.30.0 from 814735a391b874af8f00eaf89469e5ec7f38cd4aa. Signed-off-by: Petr Písař --- asan_ignore | 5 ----- pp.c | 21 ++++++++++++++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/asan_ignore b/asan_ignore index e0f5685..f520546 100644 --- a/asan_ignore +++ b/asan_ignore @@ -18,11 +18,6 @@ fun:Perl_pp_i_* -# Perl's << is defined as using the underlying C's << operator, with the -# same undefined behaviour for shifts greater than the word size. -# (UVs normally, IVs with 'use integer') - -fun:Perl_pp_left_shift # this function numifies the field width in eg printf "%10f". # It has its own overflow detection, so don't warn about it diff --git a/pp.c b/pp.c index 7afb090..3ca04e1 100644 --- a/pp.c +++ b/pp.c @@ -1991,10 +1991,29 @@ 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) -- 2.20.1