Kristian Nielsen (kristiannielsen) wrote,
Kristian Nielsen

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: c, compiler, database, freesoftware, mysql, programming
  • Post a new comment


    default userpic

    Your reply will be screened

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.