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 thesmallest 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 uis
u = (unsigned long)0 - (unsigned long)v. Unsignedoverflow 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 theLinux 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!