Log in

Kristian Nielsen - Integer overflow
July 3rd, 2012
01:17 pm


Previous Entry Share Next Entry
Integer overflow

What do you think of this piece of C code?

  void foo(long v) {
    unsigned long u;
    unsigned sign;
    if (v < 0) {
      u = -v;
      sign = 1;
    } else {
      u = v;
      sign = 0;
Seems pretty simple, right? Then what do you think of this output from MySQL:
  mysql> create table t1 (a bigint) as select '-9223372036854775807.5' as a;
  mysql> select * from t1;
  | a                    |
  | -'..--).0-*(+,))+(0( | 
Yes, that is authentic output from older versions of MySQL. Not just the wrong number, the output is complete garbage! This is my all-time favorite MySQL bug#31799. It was caused by code like the above C snippet.

So can you spot what is wrong with the code? Looks pretty simple, does it not? But the title of this post may give a hint...

It is a little known fact that signed integer overflow is undefined in C! The code above contains such undefined behaviour. The expression -v overflows when v contains the smallest negative integer of the long type (-263 on 64-bit) - the absolute value of this cannot be represented in the type. The correct way to put the absolute value of signed v into unsigned u is u = (unsigned long)0 - (unsigned long)v. Unsigned overflow is well-defined in C, in contrast to signed overflow.

And yes, GCC will generate unexpected (but technically valid) assembler for such code, as seen in the Bug#31799. If you do not like this, then use -fno-strict-overflow like I believe Postgresql and the Linux kernel do.

(But better write correct C code from the start).

Tags: , , , , ,

(2 comments | Leave a comment)

[User Picture]
From:Andrew Hutchings
Date:July 3rd, 2012 01:17 pm (UTC)
I find it interesting that the documentation blames GCC for this: "A gcc problem caused incorrect numeric output due to integer overflow."
From:Alexey Kopytov
Date:July 3rd, 2012 03:09 pm (UTC)
Another interesting fact related to this is that an integer division of INT_MIN / LONG_MIN / LONGLONG_MIN by -1 (i.e. a theoretical equivalent of integer negation) crashes the process with SIGFPE, at least on x86. I discovered it when fixing http://bugs.mysql.com/bug.php?id=8433. Before that fix, the server crashed on the following statement: SELECT -9223372036854775808 MOD -1
Powered by LiveJournal.com