Bug 105533 - UBSAN: gcc/expmed.cc:3272:26: runtime error: signed integer overflow: -9223372036854775808 - 1 cannot be represented in type 'long int'
Summary: UBSAN: gcc/expmed.cc:3272:26: runtime error: signed integer overflow: -922337...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 13.0
: P3 normal
Target Milestone: ---
Assignee: Jakub Jelinek
URL:
Keywords:
Depends on:
Blocks: ubsan
  Show dependency treegraph
 
Reported: 2022-05-09 11:27 UTC by Zdenek Sojka
Modified: 2024-03-08 08:29 UTC (History)
4 users (show)

See Also:
Host:
Target: x86_64-pc-linux-gnu
Build:
Known to work:
Known to fail: 13.0
Last reconfirmed:


Attachments
reduced testcase (165 bytes, text/plain)
2022-05-09 11:27 UTC, Zdenek Sojka
Details
gcc14-pr105533.patch (718 bytes, patch)
2024-03-06 15:16 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Zdenek Sojka 2022-05-09 11:27:56 UTC
Created attachment 52943 [details]
reduced testcase

Compiler output:
$ x86_64-pc-linux-gnu-gcc -O testcase.c -c
/repo/gcc-trunk/gcc/expmed.cc:3272:26: runtime error: signed integer overflow: -9223372036854775808 - 1 cannot be represented in type 'long int'

(gdb) bt
#0  __ubsan::Diag::~Diag (this=this@entry=0x7fffffffc250, __in_chrg=<optimized out>) at /repo/gcc-trunk/libsanitizer/ubsan/ubsan_diag.cpp:343
#1  0x00000000074e14ec in handleIntegerOverflowImpl<__ubsan::Value> (Data=Data@entry=0x85fd8e0, LHS=LHS@entry=9223372036854775808, Operator=Operator@entry=0x7b7c28d "-", RHS=..., Opts=...)
    at /repo/gcc-trunk/libsanitizer/ubsan/ubsan_handlers.cpp:227
#2  0x00000000074e478d in __ubsan::__ubsan_handle_sub_overflow (Data=Data@entry=0x85fd8e0, LHS=LHS@entry=9223372036854775808, RHS=RHS@entry=1) at /repo/gcc-trunk/libsanitizer/ubsan/ubsan_handlers.cpp:244
#3  0x0000000001384d5d in choose_mult_variant (mode=E_DImode, val=<optimized out>, alg=0x7fffffffc6b0, variant=0x7fffffffc6ac, mult_cost=16) at /repo/gcc-trunk/gcc/expmed.cc:3272
#4  0x0000000001385946 in mult_by_coeff_cost (coeff=-9223372036854775808, mode=E_DImode, speed=<optimized out>) at /repo/gcc-trunk/gcc/expmed.cc:3610
#5  0x00000000068ae3ac in create_add_ssa_cand (gs=0x7ffff7851a50, base_in=0x7ffff7852ea0, addend_in=<optimized out>, subtract_p=false, speed=true) at /repo/gcc-trunk/gcc/gimple-ssa-strength-reduction.cc:1311
#6  0x00000000068b4668 in slsr_process_add (speed=true, rhs2=0x7ffff7852ea0, rhs1=<optimized out>, gs=<optimized out>) at /repo/gcc-trunk/gcc/gimple-ssa-strength-reduction.cc:1484
#7  find_candidates_dom_walker::before_dom_children (this=<optimized out>, bb=<optimized out>) at /repo/gcc-trunk/gcc/gimple-ssa-strength-reduction.cc:1803
#8  0x000000000665b9e5 in dom_walker::walk (this=<optimized out>, bb=0x7ffff76f4478) at /repo/gcc-trunk/gcc/domwalk.cc:309
#9  0x000000000689d07a in (anonymous namespace)::pass_strength_reduction::execute (this=<optimized out>, fun=0x7ffff784c0b8) at /repo/gcc-trunk/gcc/gimple-ssa-strength-reduction.cc:4039
#10 0x00000000021af318 in execute_one_pass (pass=0xc6701b0) at /repo/gcc-trunk/gcc/passes.cc:2638
#11 0x00000000021b2902 in execute_pass_list_1 (pass=0xc6701b0) at /repo/gcc-trunk/gcc/passes.cc:2738
#12 0x00000000021b2945 in execute_pass_list_1 (pass=0xc66dcc0) at /repo/gcc-trunk/gcc/passes.cc:2739
#13 0x00000000021b29f9 in execute_pass_list (fn=0x7ffff784c0b8, pass=<optimized out>) at /repo/gcc-trunk/gcc/passes.cc:2749
#14 0x0000000000f8cebc in cgraph_node::expand (this=0x7ffff784f110) at /repo/gcc-trunk/gcc/cgraphunit.cc:1835
#15 0x0000000000f92108 in expand_all_functions () at /repo/gcc-trunk/gcc/cgraphunit.cc:1999
#16 symbol_table::compile (this=0x7ffff76ec000) at /repo/gcc-trunk/gcc/cgraphunit.cc:2349
#17 0x0000000000fa02ad in symbol_table::compile (this=0x7ffff76ec000) at /repo/gcc-trunk/gcc/cgraphunit.cc:2262
#18 symbol_table::finalize_compilation_unit (this=0x7ffff76ec000) at /repo/gcc-trunk/gcc/cgraphunit.cc:2530
#19 0x00000000026d34a3 in compile_file () at /repo/gcc-trunk/gcc/toplev.cc:479
#20 0x0000000000917d45 in do_compile (no_backend=false) at /repo/gcc-trunk/gcc/toplev.cc:2168
#21 toplev::main (this=this@entry=0x7fffffffd27e, argc=<optimized out>, argc@entry=17, argv=<optimized out>, argv@entry=0x7fffffffd3c8) at /repo/gcc-trunk/gcc/toplev.cc:2320
#22 0x000000000091bb6b in main (argc=17, argv=0x7fffffffd3c8) at /repo/gcc-trunk/gcc/main.cc:39

$ x86_64-pc-linux-gnu-gcc -v
Using built-in specs.
COLLECT_GCC=/repo/gcc-trunk/binary-latest-amd64/bin/x86_64-pc-linux-gnu-gcc
COLLECT_LTO_WRAPPER=/repo/gcc-trunk/binary-trunk-r13-167-20220507103325-g0c723bb4be2-checking-release-bootstrap-ubsan-amd64/bin/../libexec/gcc/x86_64-pc-linux-gnu/13.0.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /repo/gcc-trunk//configure --enable-languages=c,c++ --enable-valgrind-annotations --disable-nls --enable-checking=release --with-build-config=bootstrap-ubsan --with-cloog --with-ppl --with-isl --build=x86_64-pc-linux-gnu --host=x86_64-pc-linux-gnu --target=x86_64-pc-linux-gnu --with-ld=/usr/bin/x86_64-pc-linux-gnu-ld --with-as=/usr/bin/x86_64-pc-linux-gnu-as --disable-libstdcxx-pch --prefix=/repo/gcc-trunk//binary-trunk-r13-167-20220507103325-g0c723bb4be2-checking-release-bootstrap-ubsan-amd64
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.0.0 20220507 (experimental) (GCC)
Comment 1 Andrew Pinski 2022-10-31 03:32:38 UTC
choose_mult_variant (machine_mode mode, HOST_WIDE_INT val,

...
  synth_mult (&alg2, val - 1, &limit, mode);


But slsr should not be processing any of these IR I don't think.

  _7 = _4 *  Inf;
  c$imag_8 = (long intD.8) _7;
  # VUSE <.MEM_16>
  i.1_9 = iD.1980;
  # RANGE [-2147483648, 2147483647]
  _10 = (long intD.8) i.1_9;
  c$real_23 = c$real_6 * _10;
  c$imag_24 = c$imag_8 * _10;

Huh? This does not make sense at all.
  float _4;
  float _7;

      if (tree_fits_shwi_p (rhs2))
        return mult_by_coeff_cost (tree_to_shwi (rhs2), lhs_mode, speed);

But I don't see that even call that.
bool
tree_fits_shwi_p (const_tree t)
{
  return (t != NULL_TREE
          && TREE_CODE (t) == INTEGER_CST
          && wi::fits_shwi_p (wi::to_widest (t)));
}
Comment 2 David Binderman 2024-03-05 19:20:46 UTC
I see something similar when attempting a bootstrap with asan & ubsan on
current gcc trunk:

gcc/expmed.cc:3288:26: runtime error: signed integer overflow: -9223372036854775808 - 1 cannot be represented in type 'long int'

Configure is:

../trunk/configure \
    --disable-multilib \
    --disable-werror \
    --with-build-config=bootstrap-asan \
    --with-build-config=bootstrap-ubsan \
    --enable-checking=df,extra,fold,rtl,yes \
    --enable-languages=c,c++,fortran

And there are extra flags added to the top level Makefile:

sed 's;-O2;-O3 -march=native;' < Makefile > Makefile.tmp
diff Makefile Makefile.tmp
mv Makefile.tmp Makefile

I wonder if this is a regression for gcc-14.
Comment 3 David Binderman 2024-03-06 09:30:32 UTC
Asan, most of the checking flags, fortran and the -march setting not required.

Current configure script is:

../trunk.20210101/configure \
	--disable-multilib \
	--disable-werror \
	--with-pkgversion=$HASH \
        --with-build-config=bootstrap-ubsan \
	--enable-checking=yes \
	--enable-languages=c,c++

sed 's;-O2;-O3;' < Makefile > Makefile.tmp
diff Makefile Makefile.tmp
mv Makefile.tmp Makefile

From the output of the bootstrap:

Configuring stage 2 in x86_64-pc-linux-gnu/libgcc
../../trunk.20210101/gcc/poly-int.h:1089:5: runtime error: left shift of negative value -8
../../trunk.20210101/gcc/poly-int.h:1089:5: runtime error: left shift of negative value -8
../../trunk.20210101/gcc/expmed.cc:3288:26: runtime error: signed integer overflow: -9223372036854775808 - 1 cannot be represented in type 'long int'
Configuring stage 2 in x86_64-pc-linux-gnu/libgomp
Comment 4 Jakub Jelinek 2024-03-06 10:33:02 UTC
I see both
/usr/src/gcc/obj5/./gcc/xgcc -B/usr/src/gcc/obj5/./gcc/ -B/usr/local/x86_64-pc-linux-gnu/bin/ -B/usr/local/x86_64-pc-linux-gnu/lib/ -isystem /usr/local/x86_64-pc-linux-gnu/include -isystem /usr/local/x86_64-pc-linux-gnu/sys-include   -fno-checking -g -O3 -O2  -g -O3 -DIN_GCC   -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wold-style-definition  -isystem ./include  -fpic -mlong-double-80 -DUSE_ELF_SYMVER -fcf-protection -mshstk -g -DIN_LIBGCC2 -fbuilding-libgcc -fno-stack-protector  -fpic -mlong-double-80 -DUSE_ELF_SYMVER -fcf-protection -mshstk -I. -I. -I../.././gcc -I../../../libgcc -I../../../libgcc/. -I../../../libgcc/../gcc -I../../../libgcc/../include -I../../../libgcc/config/libbid -DENABLE_DECIMAL_BID_FORMAT -DHAVE_CC_TLS  -DUSE_TLS  -o bid128_add.o -MT bid128_add.o -MD -MP -MF bid128_add.dep -c ../../../libgcc/config/libbid/bid128_add.c
../../gcc/expmed.cc:3288:26: runtime error: signed integer overflow: -9223372036854775808 - 1 cannot be represented in type 'long int'
/usr/src/gcc/obj5/./gcc/xgcc -B/usr/src/gcc/obj5/./gcc/ -B/usr/local/x86_64-pc-linux-gnu/bin/ -B/usr/local/x86_64-pc-linux-gnu/lib/ -isystem /usr/local/x86_64-pc-linux-gnu/include -isystem /usr/local/x86_64-pc-linux-gnu/sys-include   -fno-checking -g -O3 -O2  -g -O3 -DIN_GCC   -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wold-style-definition  -isystem ./include  -fpic -mlong-double-80 -DUSE_ELF_SYMVER -fcf-protection -mshstk -g -DIN_LIBGCC2 -fbuilding-libgcc -fno-stack-protector  -fpic -mlong-double-80 -DUSE_ELF_SYMVER -fcf-protection -mshstk -I. -I. -I../.././gcc -I../../../libgcc -I../../../libgcc/. -I../../../libgcc/../gcc -I../../../libgcc/../include -I../../../libgcc/config/libbid -DENABLE_DECIMAL_BID_FORMAT -DHAVE_CC_TLS  -DUSE_TLS  -o unwind-dw2.o -MT unwind-dw2.o -MD -MP -MF unwind-dw2.dep -fexceptions -c ../../../libgcc/unwind-dw2.c -fvisibility=hidden -DHIDE_EXPORTS
../../gcc/poly-int.h:1089:5: runtime error: left shift of negative value -8
so will have a look at those.
Comment 5 David Binderman 2024-03-06 11:39:47 UTC
The problem with expmed.c happens with -O2 -march=znver3,
so it's more prevalent than I thought.

The problem with poly-int.h seems to require -O3.

So they look like two separate problems.
Comment 6 Jakub Jelinek 2024-03-06 13:37:07 UTC
Testcase for the first compile time UB (-O3):
long long a, b, c;

void
foo (long long e)
{
  long long d = a & 9223372036854775808ULL;
  c = b;
  if (d && e)
    c = c | d;
}
Testcase for the second compile time UB (-O2):
int a[64];
int p;

void
foo (void)
{
  int stack_elt = 1;
  while (p)
    {
      stack_elt -= 11;
      a[stack_elt] != 0;
    }
}
Comment 7 Jakub Jelinek 2024-03-06 13:45:28 UTC
The second UB is on
#2  ao_ref_init_from_vn_reference (ref=<optimized out>, set=1, base_set=1, type=<optimized out>, ops=...) at ../../gcc/tree-ssa-sccvn.cc:1224
1224		    offset += op->off << LOG2_BITS_PER_UNIT;
where op->off is negative.
Isn't this just an unnecessary optimization?  I mean can't we just do
		    offset += op->off * BITS_PER_UNIT;
BITS_PER_UNIT is a constant 8 on all targets we support...
Comment 8 Jakub Jelinek 2024-03-06 13:56:04 UTC
The same function already does
offset += pop->off * BITS_PER_UNIT;
a few lines earlier, so I think doing it here is fine as well.
Or yet another option is to cast pop->off or op->off to offset_int first and do the
left shift in offset_int type where it is well defined.
2024-03-06  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/105533
	* tree-ssa-sccvn.cc (ao_ref_init_from_vn_reference) <case ARRAY_REF>:
	Multiple op->off by BITS_PER_UNIT instead of shifting it left by
	LOG2_BITS_PER_UNIT.

--- gcc/tree-ssa-sccvn.cc.jj	2024-02-28 22:57:18.318658827 +0100
+++ gcc/tree-ssa-sccvn.cc	2024-03-06 14:52:16.819229719 +0100
@@ -1221,7 +1221,7 @@ ao_ref_init_from_vn_reference (ao_ref *r
 	  if (maybe_eq (op->off, -1))
 	    max_size = -1;
 	  else
-	    offset += op->off << LOG2_BITS_PER_UNIT;
+	    offset += op->off * BITS_PER_UNIT;
 	  break;
 
 	case REALPART_EXPR:
Comment 9 Jakub Jelinek 2024-03-06 15:16:05 UTC
Created attachment 57632 [details]
gcc14-pr105533.patch

Untested fix for the other issue.
Comment 10 rguenther@suse.de 2024-03-07 07:32:00 UTC
On Wed, 6 Mar 2024, jakub at gcc dot gnu.org wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105533
> 
> Jakub Jelinek <jakub at gcc dot gnu.org> changed:
> 
>            What    |Removed                     |Added
> ----------------------------------------------------------------------------
>                  CC|                            |rguenth at gcc dot gnu.org
> 
> --- Comment #7 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
> The second UB is on
> #2  ao_ref_init_from_vn_reference (ref=<optimized out>, set=1, base_set=1,
> type=<optimized out>, ops=...) at ../../gcc/tree-ssa-sccvn.cc:1224
> 1224                offset += op->off << LOG2_BITS_PER_UNIT;
> where op->off is negative.
> Isn't this just an unnecessary optimization?  I mean can't we just do
>                     offset += op->off * BITS_PER_UNIT;
> BITS_PER_UNIT is a constant 8 on all targets we support...

It's a habit from dealing with offset_int (but this is poly_int64)
where the multiply is possibly a lot more costly than a shift.

So yeah, a multiply is fine I guess.
Comment 11 GCC Commits 2024-03-07 09:08:31 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:e1bd0f293d8407d4e8149fbafd470612323dc938

commit r14-9353-ge1bd0f293d8407d4e8149fbafd470612323dc938
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Mar 7 10:01:08 2024 +0100

    sccvn: Avoid UB in ao_ref_init_from_vn_reference [PR105533]
    
    When compiling libgcc or on e.g.
    int a[64];
    int p;
    
    void
    foo (void)
    {
      int s = 1;
      while (p)
        {
          s -= 11;
          a[s] != 0;
        }
    }
    sccvn invokes UB in the compiler as detected by ubsan:
    ../../gcc/poly-int.h:1089:5: runtime error: left shift of negative value -40
    The problem is that we still use C++11..C++17 as the implementation language
    and in those C++ versions shifting negative values left is UB (well defined
    since C++20) and above in
               offset += op->off << LOG2_BITS_PER_UNIT;
    op->off is poly_int64 with -40 value (in libgcc with -8).
    I understand the offset_int << LOG2_BITS_PER_UNIT shifts but it is then well
    defined during underlying implementation which is done on the uhwi limbs,
    but for poly_int64 we use
                    offset += pop->off * BITS_PER_UNIT;
    a few lines earlier and I think that is both more readable in what it
    actually does and triggers UB only if there would be signed multiply
    overflow.  In the end, the compiler will treat them the same at least at the
    RTL level (at least, if not and they aren't the same cost, it should).
    
    2024-03-07  Jakub Jelinek  <jakub@redhat.com>
    
            PR middle-end/105533
            * tree-ssa-sccvn.cc (ao_ref_init_from_vn_reference) <case ARRAY_REF>:
            Multiple op->off by BITS_PER_UNIT instead of shifting it left by
            LOG2_BITS_PER_UNIT.
Comment 12 GCC Commits 2024-03-07 09:08:37 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:c655c8d8d845b36c59babb2413ce7aa3584dbeda

commit r14-9354-gc655c8d8d845b36c59babb2413ce7aa3584dbeda
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Mar 7 10:02:00 2024 +0100

    expand: Fix UB in choose_mult_variant [PR105533]
    
    As documented in the function comment, choose_mult_variant attempts to
    compute costs of 3 different cases, val, -val and val - 1.
    The -val case is actually only done if val fits into host int, so there
    should be no overflow, but the val - 1 case is done unconditionally.
    val is shwi (but inside of synth_mult already uhwi), so when val is
    HOST_WIDE_INT_MIN, val - 1 invokes UB.  The following patch fixes that
    by using val - HOST_WIDE_INT_1U, but I'm not really convinced it would
    DTRT for > 64-bit modes, so I've guarded it as well.  Though, arch
    would need to have really strange costs that something that could be
    expressed as x << 63 would be better expressed as (x * 0x7fffffffffffffff) + 1
    In the long term, I think we should just rewrite
    choose_mult_variant/synth_mult etc. to work on wide_int.
    
    2024-03-07  Jakub Jelinek  <jakub@redhat.com>
    
            PR middle-end/105533
            * expmed.cc (choose_mult_variant): Only try the val - 1 variant
            if val is not HOST_WIDE_INT_MIN or if mode has exactly
            HOST_BITS_PER_WIDE_INT precision.  Avoid triggering UB while computing
            val - 1.
    
            * gcc.dg/pr105533.c: New test.
Comment 13 David Binderman 2024-03-08 08:28:01 UTC
Seems fixed to me. 

I built a bootstrap with ASAN and UBSAN
for languages C, C++ and Fortran and changed the usual
-O2 for -O3 -march=znver3 and the bootstrap passed.

I hadn't realised a bootstrap was such a good test of the compiler.
Comment 14 Jakub Jelinek 2024-03-08 08:29:14 UTC
.