Tuesday, July 3, 2012

Integer overflow

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).


PlanetMySQL Voting:
Vote UP /
Vote DOWN

DIGITAL JUICE

No comments:

Post a Comment

Thank's!