]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
rs6000: Avoid undefined behavior caused by overflow and invalid shifts
authorKishan Parmar <kishan@linux.ibm.com>
Thu, 31 Jul 2025 11:35:02 +0000 (17:05 +0530)
committerKishan Parmar <kishan@linux.ibm.com>
Thu, 31 Jul 2025 11:35:02 +0000 (17:05 +0530)
While building GCC with --with-build-config=bootstrap-ubsan on
powerpc64le-unknown-linux-gnu, multiple UBSAN runtime errors were
encountered in rs6000.cc and rs6000.md due to undefined behavior
involving left shifts on negative values and shift exponents equal to
or exceeding the type width.

The issue was in bit pattern recognition code
(in can_be_rotated_to_negative_lis and can_be_built_by_li_and_rldic),
where signed values were shifted without handling negative inputs or
guarding against shift counts equal to the type width, causing UB.
The fix ensures shifts and rotations are done unsigned HOST_WIDE_INT,
and casting back only where needed (like for arithmetic right shifts)
with proper guards to prevent shift-by-64.

2025-07-31  Kishan Parmar  <kishan@linux.ibm.com>

gcc:
PR target/118890
* config/rs6000/rs6000.cc (can_be_rotated_to_negative_lis): Avoid left
shift of negative value and guard shift count.
(can_be_built_by_li_and_rldic): Likewise.
(rs6000_emit_set_long_const): Likewise.
* config/rs6000/rs6000.md (splitter for plus into two 16-bit parts): Fix
UB from overflow in addition.

gcc/config/rs6000/rs6000.cc
gcc/config/rs6000/rs6000.md

index 1c60695ff8cab6e5f0021c156848fc226207e699..764b4992fb56485727b0bd1cda01db89d147ae80 100644 (file)
@@ -10320,15 +10320,18 @@ can_be_rotated_to_negative_lis (HOST_WIDE_INT c, int *rot)
 
   /* case b. xx0..01..1xx: some of 15 x's (and some of 16 0's) are
      rotated over the highest bit.  */
-  int pos_one = clz_hwi ((c << 16) >> 16);
-  middle_zeros = ctz_hwi (c >> (HOST_BITS_PER_WIDE_INT - pos_one));
-  int middle_ones = clz_hwi (~(c << pos_one));
-  if (middle_zeros >= 16 && middle_ones >= 33)
+  unsigned HOST_WIDE_INT uc = c;
+  int pos_one = clz_hwi ((HOST_WIDE_INT) (uc << 16) >> 16);
+  if (pos_one != 0)
     {
-      *rot = pos_one;
-      return true;
+      middle_zeros = ctz_hwi (c >> (HOST_BITS_PER_WIDE_INT - pos_one));
+      int middle_ones = clz_hwi (~(uc << pos_one));
+      if (middle_zeros >= 16 && middle_ones >= 33)
+       {
+         *rot = pos_one;
+         return true;
+       }
     }
-
   return false;
 }
 
@@ -10445,7 +10448,8 @@ can_be_built_by_li_and_rldic (HOST_WIDE_INT c, int *shift, HOST_WIDE_INT *mask)
   if (lz >= HOST_BITS_PER_WIDE_INT)
     return false;
 
-  int middle_ones = clz_hwi (~(c << lz));
+  unsigned HOST_WIDE_INT uc = c;
+  int middle_ones = clz_hwi (~(uc << lz));
   if (tz + lz + middle_ones >= ones
       && (tz - lz) < HOST_BITS_PER_WIDE_INT
       && tz < HOST_BITS_PER_WIDE_INT)
@@ -10479,7 +10483,7 @@ can_be_built_by_li_and_rldic (HOST_WIDE_INT c, int *shift, HOST_WIDE_INT *mask)
   if (!IN_RANGE (pos_first_1, 1, HOST_BITS_PER_WIDE_INT-1))
     return false;
 
-  middle_ones = clz_hwi (~c << pos_first_1);
+  middle_ones = clz_hwi ((~(unsigned HOST_WIDE_INT) c) << pos_first_1);
   middle_zeros = ctz_hwi (c >> (HOST_BITS_PER_WIDE_INT - pos_first_1));
   if (pos_first_1 < HOST_BITS_PER_WIDE_INT
       && middle_ones + middle_zeros < HOST_BITS_PER_WIDE_INT
@@ -10581,7 +10585,8 @@ rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c, int *num_insns)
     {
       /* li/lis; rldicX */
       unsigned HOST_WIDE_INT imm = (c | ~mask);
-      imm = (imm >> shift) | (imm << (HOST_BITS_PER_WIDE_INT - shift));
+      if (shift != 0)
+       imm = (imm >> shift) | (imm << (HOST_BITS_PER_WIDE_INT - shift));
 
       count_or_emit_insn (temp, GEN_INT (imm));
       if (shift != 0)
index 9c718ca2a2260db77063f4762d89cbb29060f875..e31ee40aa87070ebda6a7df4f3b1f27bf4ab96bf 100644 (file)
   [(set (match_dup 0) (plus:GPR (match_dup 1) (match_dup 3)))
    (set (match_dup 0) (plus:GPR (match_dup 0) (match_dup 4)))]
 {
-  HOST_WIDE_INT val = INTVAL (operands[2]);
+  unsigned HOST_WIDE_INT val = UINTVAL (operands[2]);
   HOST_WIDE_INT low = sext_hwi (val, 16);
   HOST_WIDE_INT rest = trunc_int_for_mode (val - low, <MODE>mode);