26

I am getting:

warning: assuming signed overflow does not occur when assuming that (X + c) < X is always false [-Wstrict-overflow]

on this line:

if ( this->m_PositionIndex[in] < this->m_EndIndex[in] ) 

m_PositionIndex and m_EndIndex of type itk::Index (http://www.itk.org/Doxygen/html/classitk_1_1Index.html), and their operator[] returns a signed long.

(it is line 37 here: https://github.com/Kitware/ITK/blob/master/Modules/Core/Common/include/itkImageRegionConstIteratorWithIndex.hxx for context)

Can anyone explain what would cause that warning here? I don't see the pattern (x+c) < x anywhere - as this is simply a signed long comparison.

I tried to reproduce it in a self-contained example:

#include <iostream>

namespace itk
{
  struct Index
  {
    signed long data[2];

    Index()
    {
      data[0] = 0;
      data[1] = 0;
    }

    signed long& operator[](unsigned int i)
    {
      return data[i];
    }
  };
}
int main (int argc, char *argv[])
{
  itk::Index positionIndex;
  itk::Index endIndex;

  for(unsigned int i = 0; i < 2; i++)
  {
    positionIndex[i]++;
    if ( positionIndex[i] < endIndex[i] )
    {
      std::cout << "something" << std::endl;
    }
  }

  return 0;
}

but I do not get the warning there. Any thoughts as to what is different between my demo and the real code, or what could be causing the warning in the real code? I get the warning with both gcc 4.7.0 and 4.7.2 with the -Wall flag.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
David Doria
  • 9,873
  • 17
  • 85
  • 147

6 Answers6

16

To simply disable this warning, use -Wno-strict-overflow. To instead disable the specific optimization that triggers this warning, use -fno-strict-overflow or -fwrapv.

The gcc manpage describes that this warning can be controlled with levels: -Wstrict-overflow=n.

If this is stopping your build due to -Werror, you can work-around without hiding the warnings by using -Wno-error=strict-overflow (or just -Wno-error to override -Werror).


Analysis and commentary...

I got the same warning and spent a couple of hours trying to reproduce it in a smaller example, but never succeeded. My real code involved calling an inline function in a templated class, but the algorithm simplifies to the following...

int X = some_unpredictable_value_well_within_the_range_of_int();
for ( int c=0; c<4; c++ ) assert( X+c >= X ); ## true unless (X+c) overflows

In my case the warning was somehow correlated with the optimizer unrolling the for loop, so I was able to work-around by declaring volatile int c=0. Another thing that fixed it was to declare unsigned int c=0, but I'm not exactly sure why that makes a difference. Another thing that fixed it was making the loop count large enough that the loop wouldn't be unrolled, but that's not a useful solution.

So what is this warning really saying? Either it is saying that the optimizer has modified the semantics of your algorithm (by assuming no overflow), or it is simply informing you that the optimizer is assuming that your code doesn't have the undefined behavior of overflowing a signed integer. Unless overflowing signed integers is part of the intended behavior of your program, this message probably does not indicate a problem in your code -- so you will likely want to disable it for normal builds. If you get this warning but aren't sure about the code in question, it may be safest to just disable the optimization with -fwrapv.

By the way, I ran into this issue on GCC 4.7, but the same code compiled without warning using 4.8 -- perhaps indicating that the GCC developers recognized the need to be a bit less "strict" in the normal case (or maybe it was just due to differences in the optimizer).


Philosophy...

In [C++11:N3242$5.0.4], it states...

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

This means that a conforming compiler can simply assume that overflow never occurs (even for unsigned types).

In C++, due to features such as templates and copy constructors, elision of pointless operations is an important optimizer capability. Sometimes though, if you are using C++ as a low-level "system language", you probably just want the compiler to do what you tell it to do -- and rely on the behavior of the underlying hardware. Given the language of the standard, I'm not sure how to achieve this in a compiler-independent fashion.

Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
  • 2
    Essentially, the program tells you that you have an unnecessary conditional. In your case, the compiler can deduce that c is always non-negative, so your assertion is true by the language rules, and thus unnecessary. If you want to detect overflow, do it within the language rules (`INT_MAX - c >= X`). – Sebastian Redl Jun 20 '13 at 17:26
  • 1
    @SebastianRedl: `assuming signed overflow does not occur when assuming that (X + c) < X is always false`. The warning is not about the fact the the conditional is unnecessary -- it is about the assumptions being made in order to optimize away the conditional. Good suggestion about the overflow checking, but I think my way is more clear (and OK since it is only a comment). – Brent Bradburn Jun 20 '13 at 17:40
  • My example code looks a bit silly, but that's because I simplified it. In the real code, the assert was phrased differently and buried in a function call. In the general case it is a meaningful assert, but in this particular case the optimizer was smart enough to figure out that it could optimize it away. Unfortunately, this resulted in a pointless warning message that forced me to do an investigation. – Brent Bradburn Jun 20 '13 at 17:41
  • 2
    Yes, the real problem is that GCC emits this warning after inlining, which leads to situations where it can deduce that in this particular instance, the conditional is always false. The reason the warning is there is that there have been critical security bugs caused by developers testing overflow this way and GCC simply optimizing the check away because the code is undefined. – Sebastian Redl Jun 20 '13 at 17:51
  • TODO: Does this agree on "even for unsigned types": http://stackoverflow.com/a/38862427/86967. Probably `unsigned` is well-defined based on wrap-around. I don't know what I missed before -- I guess interpreting language standards isn't that easy. See also the table [here](https://en.wikipedia.org/w/index.php?title=Integer_overflow&oldid=729674438#Security_ramifications). – Brent Bradburn Aug 10 '16 at 01:46
  • 1
    'just disable warnings' In C, this is unacceptable -1 – cat Dec 14 '16 at 15:34
  • @cat: How does your comment related to the extended analysis in the answer? – Brent Bradburn Dec 14 '16 at 18:37
  • TBH, gcc should not BY DEFAULT assume that user code will not overflow. There should be an option to turn such optimistic assumption on, but it should be off by default. – quant_dev Aug 10 '17 at 15:26
  • `volatile int c=0` tells the compiler that `c` can be modified outside of the code, so the compiler should make no assumptions about it. In other words, this would disable the optimization of code involving `c`, and we probably don't want that (well the warning is triggered by an optimization, so I guess the code is built with -Osomething, so we do want to optimize) – brembo Oct 03 '17 at 07:06
  • @quant_dev C++ itself assumes that. Signed overflow is undefined behaviour, so code that does it is invalid. – Yksisarvinen Jul 28 '20 at 13:38
9

The compiler is telling you that it has enough static knowledge of that snippet to know that test will always succeed if it can optimize the test assuming that no signed operation will overflow.

In other words, the only way x + 1 < x will ever return true when x is signed is if x is already the maximum signed value. [-Wstrict-overflow] let's the compiler warn when it can assume that no signed addition will overflow; it's basically telling you it's going to optimize away the test because signed overflow is undefined behavior.

If you want to suppress the warning, get rid of the test.

MSN
  • 53,214
  • 7
  • 75
  • 105
4

Despite the age of your question, since you didn't change that part of your code yet, I assume the problem still exists and that you still didn't get a useful explanation on what's actually going on here, so let my try it:

In C (and C++), if adding two SIGNED integers causes an overflow, the behavior of the entire program run is UNDEFINED. So, the environment that executes your program can do whatever it wants (format your hard disk or start a nuklear war, assuming the necessary hardware is present).

gcc usually does neither, but it does other nasty things (that could still lead to either one in unlucky circumstances). To demonstrate this, let me give you a simple example from Felix von Leitner @ http://ptrace.fefe.de/int.c:

#include <assert.h>
#include <stdio.h>

int foo(int a) {
  assert(a+100 > a);
  printf("%d %d\n",a+100,a);
  return a;
}

int main() {
  foo(100);
  foo(0x7fffffff);
}

Note: I added stdio.h, to get rid of the warning about printf not being declared.

Now, if we run this, we would expect the code to assert out on the second call of foo, because it creates an integer overflow and checks for it. So, let's do it:

$ gcc -O3 int.c -o int && ./int
200 100
-2147483549 2147483647

WTF? (WTF, German for "Was täte Fefe" - "what would Fefe do", Fefe being the nick name of Felix von Leitner, which I borrowed the code example from). Oh, and, btw, Was täte Fefe? Right: Write a bug report in 2007 about this issue! https://gcc.gnu.org/bugzilla/show_bug.cgi?id=30475

Back to your question. If you now try to dig down, you could create an assembly output (-S) and investigate only to figure out, that the assert was completely removed

$ gcc -S -O3 int.c -o int.s && cat int.s
[...]
foo:
        pushq   %rbx             // save "callee-save" register %rbx
        leal    100(%rdi), %edx  // calc a+100 -> %rdx for printf
        leaq    .LC0(%rip), %rsi // "%d %d\n" for printf
        movl    %edi, %ebx       // save `a` to %rbx
        movl    %edi, %ecx       // move `a` to %rcx for printf
        xorl    %eax, %eax       // more prep for printf
        movl    $1, %edi         // and even more prep
        call    __printf_chk@PLT // printf call
        movl    %ebx, %eax       // restore `a` to %rax as return value
        popq    %rbx             // recover "callee-save" %rbx
        ret                      // and return

No assert here at any place.

Now, let's turn on warnings during compilation.

$ gcc -Wall -O3 int.c -o int.s
int.c: In function 'foo':
int.c:5:2: warning: assuming signed overflow does not occur when assuming that (X + c) >= X is always true [-Wstrict-overflow]
  assert(a+100 > a);
  ^~~~~~

So, what this message actually says is: a + 100 could potentially overflow causing undefined behavior. Because you are a highly skilled professional software developer, who never does anything wrong, I (gcc) know for sure, that a + 100 will not overflow. Because I know that, I also know, that a + 100 > a is always true. Because I know that, I know that the assert never fires. And because I know that, I can eliminate the entire assert in 'dead-code-elimination' optimization.

And that is exactly, what gcc does here (and warns you about).

Now, in your small example, the data flow analysis can determine, that this integer in fact does not overflow. So, gcc does not need to assume it to never overflow, instead, gcc can prove it to never overflow. In this case, it's absolutely ok to remove the code (the compiler could still warn about dead code elimination here, but dce happen so often, that probably nobody wants those warnings). But in your "real world code", the data flow analysis fails, because not all necessary information is present for it to take place. So, gcc doesn't know, whether a++ overflows. So it warns you, that it assumes that never happens (and then gcc removes the entire if statement).

One way to solve (or hide !!!) the issue here would be to assert for a < INT_MAX prior to doing the a++. Anyway, I fear, you might have some real bug there, but I would have to investigate much more to figure it out. However, you can figure it out yourself, be creating your MVCE the proper way: Take that source code with the warning, add anything necessary from include files to get the source code stand-alone (gcc -E is a little bit extreme but would do the job), then start removing anything that doesn't make the warning disappear until you have a code where you can't remove anything anymore.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Bodo Thiesen
  • 2,476
  • 18
  • 32
1

In my case, this was a good warning from the compiler. I had a stupid copy/paste bug where I had a loop inside a loop, and each was the same set of conditions. Something like this:

for (index = 0; index < someLoopLimit; index++)
{
    // blah, blah, blah.....
    for (index = 0; index < someLoopLimit; index++)
    {
        //  More blah, blah, blah....
    }
}

This warning saved me time debugging. Thanks, gcc!!!

user2246302
  • 386
  • 4
  • 11
0

I think that the compiler changed

positionIndex[i]++;
if ( positionIndex[i] < endIndex[i] )

into something more optimized like

if ( positionIndex[i]++ < endIndex[i] )   <-- see my comment below, this statement is wrong.

so

if ( positionIndex[i] + 1 < endIndex[i] )

that causes undefined behavior in case of overflow (thanks Seth).

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Jack
  • 131,802
  • 30
  • 241
  • 343
  • Jack - you mean that if 'positionIndex[i] + 1' overflows, then the comparison will obviously be invalid? Any suggestions on how to avoid/suppress this warning? – David Doria Oct 20 '12 at 03:05
  • Yes, if positionIndex overflows it will become a negative number, this will make the comparison true even if it's not the case. Do you need to have negative indices? Otherwise you can just turn them to unsigned. – Jack Oct 20 '12 at 03:06
  • Yes, the values do need to possibly be negative. – David Doria Oct 20 '12 at 03:08
  • and what happens if the index is really at the end? (positionIndex = 2^63-1). In that case do you really want it to evaluate comparison to true? – Jack Oct 20 '12 at 03:10
  • 2
    No, causing a signed type to overflow is undefined behaviour, not wraparound like it is for unsigned types. – Seth Carnegie Oct 20 '12 at 03:11
  • 1
    @Jack - no, but in practice the index will never be anywhere near that value. (probably more like from -1000 to 1000 haha), and also, should it always produce the warning for that one extreme corner case? That's why I'm wondering if there is something to say "Don't warn me here", or better, a change to the code that will actually fix the warning, because it is difficult to pick out real warnings in the sea of these that I'm getting. – David Doria Oct 20 '12 at 03:13
  • To fix the warning, if you are sure that it will stay in a smaller range then you should cast the values to smaller types and increment only if you are not going over `std::numeric_limits::max()`. You should try something like `(int)positionIndex + 1 < endIndex` and increment afterwards. – Jack Oct 20 '12 at 03:15
  • I don't think that's the issue, only because it's assuming that it's always false (as per the warning). Which means it believes that the both sides are the same variable, which it's trying to optimize out. – Dave S Oct 20 '12 at 03:23
  • @Jack why would I cast a long to an int? Wouldn't that make the problem worse? and wouldn't if((int)positionIndex + 1 < endIndex) have the same potential overflow problem? Also, why is the warning not produced in my self-contained demo? – David Doria Oct 20 '12 at 03:24
  • @Dave S: yes, the warning seem to point in that direction but that seems strange by looking at the code. Which optimization flags do you have enabled? – Jack Oct 20 '12 at 03:30
  • @Jack I am compiling with -O3 -Wall in both cases. – David Doria Oct 20 '12 at 03:31
  • 1
    Because it could be that `m_PositionIndex == m_EndIndex` in _itkImageRegionConstIteratorWithIndex.hxx_ for some reason. In that case the warning would have much more sense. – Jack Oct 20 '12 at 03:32
  • @Jack - they are equal in the demo too though right (both equal (0,0) )? – David Doria Oct 20 '12 at 03:40
  • As a side note: your 2nd statement is incorrect. It requires a pre-increment: `if ( ++positionIndex[i] < endIndex[i] )`. Otherwise the +1 would not be effective and the test would not generate a warning. – Alexis Wilke Jun 10 '14 at 00:38
0

This is almost certainly an error, even though I cannot tell which of three possible errors it is.

I believe, that gcc is somehow figuring out what the values in 'this->m_PositionIndex[in]' etc. are computed from, namely that both values are derived from the same value, one with an offset it can also prove to be positive. Consequently it figures, that one branch of the if/else construct is unreachable code.

This is bad. Definitely. Why? Well, there are exactly three possibilities:

  1. gcc is right in that the code is unreachable, and you overlooked that this is the case. In this case you simply have bloated code. Best of the three possibilities, but still bad in terms of code quality.

  2. gcc is right in that the code is unreachable, but you wanted it to be reachable. In this case, you need to take a second look at why it is not reachable and fix the bug in your logic.

  3. gcc is wrong, you have stumbled on a bug in it. Quite unlikely, but possible, and unfortunately very hard to prove.

The really bad thing is, that you absolutely need to either find out exactly why the code is unreachable, or you need to disprove that gcc is right (which is easier said than done). According to Murphy's law, assuming case 1. or 3. without proving it will make sure that it was case 2., and that you will have to hunt for the bug a second time...

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106