Skip to content

Instantly share code, notes, and snippets.

@mraleph
Last active August 29, 2015 14:08
Show Gist options
  • Save mraleph/2df42b26b655f1c61f00 to your computer and use it in GitHub Desktop.
Save mraleph/2df42b26b655f1c61f00 to your computer and use it in GitHub Desktop.
#include <cstdio>
#include <stdint.h>
struct value_t {
union {
float fx[2];
int32_t ix[2];
};
value_t& read(const int32_t* v) {
ix[0] = v[0];
ix[1] = v[1];
return *this;
}
};
value_t __attribute__((noinline)) load(const int32_t* v) {
return value_t().read(v);
}
int main() {
const int32_t value = 0xffb15062;
const int32_t values[] = { value, value };
value_t res = load(&values[0]);
if (res.ix[0] != res.ix[1]) {
printf("(╯°□°)╯︵ ┻━┻\n");
return -1;
}
return 0;
}
/**
*
* $ g++ --version
* g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
* $ g++ -O2 -m32 -o test test.cc && ./test
* (╯°□°)╯︵ ┻━┻
* $
* How did this happen? Well it turns out that GCC decides to copy value_t in a
* very funky way: partially through general purpose registers, partially
* through the FPU stack
*
* flds 4(%edx)
* movl (%edx), %edx
* fstps 4(%eax)
* movl %edx, (%eax)
*
* Of course it does not end well for 0xffb15062 which, when interpreted as
* floating point value is actually a signalling NaN - so it turns into a qNaN
* 0xfff15062 with a single bit flip.
*
* GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58416
*
**/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment