7 ms·
“gcc will quietly break nearly half of all the packages that it compiles”
- zzalpha 11y agoHeadline is misleading. What's going on here is that the GCC developers feel free to change the generated code for behaviour undefined in the C spec. This means they might alter the behaviour of code since 40% of all packages in Debian (the "half of all packages" mentioned here) contain code whose behaviour is undefined. But the poster seems to define any change in behaviour as "breaking" code, which is ridiculous. How much of that 40% is rare edge cases or similar, such that in reality the behaviour of the code doesn't change in practice? The follow-up email raises this exact point, noting that in the specific example of pointer overflow: In executions where overflow does not happen, the gcc produced binary will match the behavior of the abstract machine in the C spec. Which means that the cited static analysis, while correctly identifying code containing UB, may not actually have an issue at runtime as the condition may never occur in practice. The real problem is any software that truly relies on undefined behaviour for correct operation (which, I'll bet, is far less than the 40% cited here). That code is fundamentally non-portable to other compilers specifically because each compiler may produce semantically different output.
- deleted 11y ago[deleted]
- DanWaterworth 11y agoJust read the email.
- zzalpha 11y agoActually it's still a useful question, I think. A simple example is function argument evaluation order. If you have: foo(bar(), baz()) A compiler is free to call these functions in either order. That means if baz relies on state mutated by bar, the program may behave differently if the compiler chooses to reorder evaluation.
- tedunangst 11y agoA perhaps more interesting, quite realistic example: lock_two_widgets(widget *a, widget *b) { if (a < b) { lock(&a->lock); lock(&b->lock); } else { lock(&b->lock); lock(&a->lock); } }
- just_curioussss 11y agoIf lock calls include a memory barrier, which they should, then they cannot be reordered. Edit: Your code has undefined behavior, unless the two pointers point to the same object, which is unlikely in a realistic example.
- ufo 11y agoYes, the pointer comparison is UB but if the compiler didn't go out of its way to screw you over UB code then it would be a reasonable way to prevent deadlock.
- just_curioussss 11y agoThere is a defined method for comparing such pointers. Take unsigned char pointers to them and inspect their bytes. Then write your code, using that information, that does the comparison, assuming you know your architecture. It is defined behavior to read the bytes of any object using unsigned char.
- kbwt 11y agoAlternatively, if the implementation supports uintptr_t, you can convert to that and then compare the respective integral values.
- DanWaterworth 11y agoRight, because you can get to the end of a non-void function without producing a value. The pointer comparison isn't necessarily UB.
- coherentpony 11y agoNasal demons.
- barrkel 11y agoThe real problem is any software that truly relies on undefined behaviour for correct operation (which, I'll bet, is far less than the 40% cited here). I'd bet it's well into double digits relying on undefined behaviour, and over 50% relying on unspecified behaviour. It's hard to write interesting code in C that doesn't, where interesting means code that couldn't just as profitably be written in a different language with stronger memory safety. Programmers have a mental model of many things - in particular, 2's complement signed numbers, and the idea that pointers are just numbers indexing memory - that are not guaranteed by the C spec. Much of the reason for programming in a lower level language is to take advantage of lower level machine characteristics, like tagged pointers, unsafe unions, structs with blobs of data appended to avoid an indirection, custom memory allocators, etc., but the C abstract machine doesn't necessarily give all the guarantees required, without a lot of care and attention to details.
- makomk 11y agoEven C compiler developers can't always get it right. There have been a few bugs where, for example, one compiler pass optimised a struct initialisation into a single write that wrote into the slack space after the end of the struct, and a subsequent pass detected this undefined behaviour and removed the initialisation altogether. (These optimisation tricks inherently make compiler development more risky, because they mean that composing safe compiler passes is likely to create something unsafe.)
- joosters 11y agoThe article is just complaining about undefined behavior, and what compilers do when they encounter code not written to spec. Rather than rehashing the arguments for and against it, I'd really recommend anyone interested to read these articles: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html http://blog.llvm.org/2011/05/what-every-c-programmer-should-... http://blog.regehr.org/archives/213 http://blog.regehr.org/archives/213
- DanWaterworth 11y agoSince compilers (and by "compilers" I mean gcc mostly) quietly break your code behind your back, you have no way of telling whether you really fixed things or not. Compile your test suite with -fsanitize=undefined.
- deng 11y agoThat will catch a tiny part of undefined behavior.
- MichaelGG 11y agoWhy doesn't the compiler emit a warning for all UB it finds while compiling? Or do regular programs rely on this too much to be feasible? It must be something like that, or there'd not be much performance gains to be had by exploiting UB right?
- DanWaterworth 11y agoIt's not necessarily detectable statically.
- lmm 11y agoAny time you add two (signed) integers, that's potentially undefined behaviour.
- babuskov 11y agoSince when is 40% "nearly half". Can we change the title to something like: "40% of Debian packages might break if GCC changes the way it handles undefined behavior"
- deng 11y agoMore like: Code might break if it depends on undefined behavior. But of course, that might not make it to the front page.
- Dylan16807 11y ago> Since when is 40% "nearly half". ...always? > "40% of Debian packages might break if GCC changes the way it handles undefined behavior" A lot of them are silently broken today. That's the scary part.
- IvyMike 11y agoFrom the article: "(the figure of 40% is a lower bound since STACK doesn't do the same level of analysis that gcc does)." So he adds a fudge factor to estimate actual breakage and comes up with "nearly half". I dunno, seems fair to me.
- dalke 11y agoBecause the title is a direct quote from text on the linked-to page, which is the usual HN practice when there isn't a good title from the source itself.
- deng 11y agoNow that's some flawed logic here: - Some paper says that 40% of Debian packages have undefined behavior in them. - gcc's optimizer is sometimes unforgiving w.r.t. undefined behavior (see also: strict aliasing), changing the intended meaning of the code. - Therefore, it breaks 40% of packages. And boom, there's your clickbait headline...
- copsarebastards 11y agoThe only reasonable thing to say about this was already said upthread of the page, and quoted here: > I have worked on many programs, and whenever I found such a problem (typically called a "portability problem"), where the code was assuming something that the language did not guarantee, I fixed the program rather than bitching about the compiler.
- geofft 11y agoIMO, it's a little subtler than that. It's not the compiler's fault, it's the language's. Plenty of languages have no undefined behavior that can be written by accident. Go and safe Rust, for instance, have just about no undefined behavior at all, and are both performance-competitive with C. (Go has UB if you cause race conditions, and Rust has UB within `unsafe` blocks analogous to C's UB.) A C compiler, meanwhile, has to aggressively take advantage of undefined behavior to get good performance, and the C specification has been keeping behavior undefined for the benefit of compilers. You can hope that you find all such problems in C (which you might not) and "fix the program", but you can also "fix the program" by switching to a better language.
- GPGPU 11y agoYou can't compare traditional GC languages like "Go" to C. They inhabit two different universes.
- geofft 11y agoLeaving aside the fact that I'm also comparing Rust... why not? It's a language that produces fast, static executables. I bet that a good fraction of the Debian archive (not all of it, for sure) could be reimplemented in Go without causing any problems. What "different universes" are these? (To be fair, I haven't written any Go because my personal use cases involve things like shared libraries and C-ABI compatibility, so I'm going off what I've heard about Go, not personal experience. But out of what I've heard about Go, it's a fine language for this purpose, because the requirement here is just portability to all Debian architectures and comparable performance, and whether GC is used is an implementation detail.)
- the_mitsuhiko 11y agoInknow people are quick to complain about programmers relying on UB here but this really is a long standing disagreement with the gcc folk. They are language lawyers of the worst sort and do not consider security implications being a point of discussion :(
- deleted 11y ago[deleted]
- copsarebastards 11y ago> On two occasions I have been asked, — "Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?" In one case a member of the Upper, and in the other a member of the Lower, House put this question. I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question. --Charles Babbage The modern version of this seems to be: "Mr. Babbage, I put the wrong figures into the machine and the wrong answers came out! Please fix it this, this has security implications!" You can't reasonably expect the compiler to make your insecure code secure. Calling them "language lawyers" is some entitled crap. GCC commits to implement the specification of the language. Expecting them to maintain some huge number of undefined behaviors is literally expecting them to do something they never said they would do and couldn't do even if they said they would.
- andrewflnr 11y agoI believe the complaint is actually that the compiler makes secure code insecure by removing checks that rely on undefined behavior (which presumably can't be made any other way).
- copsarebastards 11y agoThat complaint isn't a valid complaint. If the checks relied on undefined behavior, the code wasn't secure. If you want to rely on the behavior of a specific version of a specific compiler, then you need to define that in your dependencies instead of pretending that you've written general-purpose C code. This isn't even just a GCC problem; compiling the code on a different compiler breaks this too.
- jacquesm 11y agoBad craftsmen blame their tools.
- Dylan16807 11y agohttps://c4.staticflickr.com/8/7226/7095238893_5000f6e57d_b.jpg https://c4.staticflickr.com/8/7226/7095238893_5000f6e57d_b.j...