3 ms·
My "favorite" bit of code that gcc eventually started treating as undefined (after working for probably literally 20 years up to that point): A 3x4 matrix type
by jepler 11y ago
My "favorite" bit of code that gcc eventually started treating as undefined (after working for probably literally 20 years up to that point): A 3x4 matrix type declared as
typedef float xform_3d[4][3];
was routinely serialized by (I'm taking liberties here, this certainly not how the 20-year-old code read!)
float *f = &x[0][0];
for(int i=0; i<12; i++) write_float(f[i]);
i.e., the code treated the same storage as a 2D-array of floats and then as a 1D-array of floats in different places.
In the "pointers are just CPU registers" world, this worked fine. But more clever compilers know that the code dereferences past the end of the array (e.g., memory at f[4] is always accessed) so the whole loop is undefined behavior.
We fixed it once we started seeing the compiler warning, so I don't know exactly how we would have been punished for the undefined behavior in this case. So hooray for compiler warnings!
- jibsen 11y agoEven computing pointers that point outside an array can be undefined behavior, like f - 1 + 1. It is easy for people who understand the relatively strong correlation between C and the assembly language it generates to assume such things work. Also, compiler vendors did let us get away with them for many years.
- dzdt 11y agoSeriously? I have seen this code many places, even written it.
- jepler 11y ago#include <stdio.h> typedef float xform_3d[4][3]; int main() { xform_3d x = { {1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}, {0.f, 0.f, 0.f} }; for(int i=0; i<12; i++) printf("%f\n", x[0][i]); } with g++ 4.9 (debian jessie -O3 -Wall): xf.c: In function ‘int main()’: xf.c:12:50: warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations] for(int i=0; i<12; i++) printf("%f\n", x[0][i]); ^ xf.c:12:5: note: containing loop for(int i=0; i<12; i++) printf("%f\n", x[0][i]); ^ (yes, this is slightly different than the code I posted first---"laundering" &x[0][0] through an intermediate pointer variable f makes the warning go away in the compiler I tested, but my suspicion is that if one is undefined behavior the other probably is too) Edited to add: and when built and run, it printed all x[0][i] but x[0][0] as 0.0, so gcc did emit code that doesn't do what the programmer wants.
- slavik81 11y ago> my suspicion is that if one is undefined behavior the other probably is too I would suspect otherwise. I have no particularly good reason for that, but the cases with and without warnings match to my expectations of what's valid and what's not. Intuitively, I'd expect pointers can point to any valid memory, while array accesses can only be done within the dimensions of the array.
- jepler 11y agoYou may be right. I haven't gone and read the standard yet, but I also fed related programs to cbmc 4.9, a C-program verifier, using the -bounds-check flag. It tags x[0][3] as an out-of-bounds access, but not f = &x[0][0]; f[3]; (also (x[0]+i) is OK according to cbmc, despite what we all learned about (a+b) and a[b] being the same!) Neither variant is picked up as illegal at runtime by -fsanitize=address and gcc-4.9 or clang-3.5.