Live data from Hacker News

“gcc will quietly break nearly half of all the packages that it compiles”

metzdowd.com

81–90 of 110 posts

Re: “gcc will quietly break nearly half of all the packages that it compiles”

#81
post #69

Earlier quoted context omitted.

I'm reminded of http://mjg59.livejournal.com/108257.html : "POSIX says this is fine, so any application that expected this behaviour is already broken by definition. But this is rules lawyering. POSIX says that many things that are not useful are fine, but doesn't exist for the pleasure of sadistic OS implementors. POSIX exists to allow application writers to write useful applications. If you interpret POSIX in such…

And it does that by implementing a compiler for a language with existing standard, which is ANSI. If you don't like the ANSI C standard (and I admit it's not perfect), don't use a compiler for ANSI C. Also, this is not just GCC problem, all the existing C compilers have the issue to some extent. After all, STACK (the MIT tool to detect undefined behavior) is based on clang. And ICC exploits the same UB tricks AFAIK.

> And it does that by implementing a compiler for a language with existing standard, which is ANSI. If you don't like the ANSI C standard (and I admit it's not perfect), don't use a compiler for ANSI C.

I'm not arguing that GCC should violate the ANSI standard; rather it should provide additional guarantees above the what ANSI requires (which was always the intent of the standard; the standard defines the absolute minimum that cross-platform programs can depend on, the reason so much is undefined is to allow compilers to have their own strategies for what should happen in those cases, not to require that compilers blow up in those cases). Honestly I think the ANSI side of things is a red herring; when given the option of some change that will slightly improve performance on some benchmarks, but make a lot of user code silently fail, a responsible developer should know to reject that change whether or not that change violates some standard.

> Also, this is not just GCC problem, all the existing C compilers have the issue to some extent.

The post is claiming that GCC is the worst of them. Certainly my impression is that clang is substantially less aggressive at exploiting UB; I don't know ICC well enough to comment.

Re: “gcc will quietly break nearly half of all the packages that it compiles”

#82

Earlier quoted context omitted.

I understand the problem, I'm saying that it's not GCC's problem. If you don't want undefined behavior, don't put undefined behavior in your code. The code you wrote isn't clear or reasonable, because it relies undefined behavior. It's a valid criticism that this code does appear to be straightforward when it isn't, but that's not a criticism of GCC, it's a criticism of ANSI C. If you don't like it, use a better lang…

> If you don't want undefined behavior, don't put undefined behavior in your code. I'd quip that this is statistically impossible for a sufficiently large codebase. > it's a criticism of ANSI C. If you don't like it, use a better language. This is my basic stance. However, if I'm e.g. in a situation where I have a C or C++ codebase I can't afford to rewrite from scratch, I'd like to use a "Better C" compiler, where "…

Good luck with that. I suspect the only good C is not C.

Re: “gcc will quietly break nearly half of all the packages that it compiles”

#83

Earlier quoted context omitted.

Writing code that depends on the value of unread memory is bad for security.

I think you misunderstood the example -- the memory is cleared after use to ensure that if it's reallocated by someone else, or someone hooks up a debugger, the content can't be examined (except when the compiler removes this clearing attempt because of an optimization). Lets say that chunk of memory held a password -- you'd definitely want to clear it after use, even if you immediately free it and never plan to read…

That's actually a very good example, but I'd argue that this is actually a violation of the standard: memset is defined as setting the value in memory. Most optimizations on undefined behavior don't really fall into this category.

I guess you could group this kind of thing into the category of "dead code elimination" which is useful, but results in parts of the code written not producing the specified executable. I have to think on this example more.

Re: “gcc will quietly break nearly half of all the packages that it compiles”

#84

Earlier quoted context omitted.

> If you don't want undefined behavior, don't put undefined behavior in your code. I'd quip that this is statistically impossible for a sufficiently large codebase. > it's a criticism of ANSI C. If you don't like it, use a better language. This is my basic stance. However, if I'm e.g. in a situation where I have a C or C++ codebase I can't afford to rewrite from scratch, I'd like to use a "Better C" compiler, where "…

Good luck with that. I suspect the only good C is not C.

I don't disagree - but there's value in harm reduction, no?

Re: “gcc will quietly break nearly half of all the packages that it compiles”

#85
post #14
post #6

Since 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"

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

All click-bait titles are direct quotes.

Re: “gcc will quietly break nearly half of all the packages that it compiles”

#86
post #6

Since 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"

When has 40% not been nearly half? What is the minimum threshold for a quantity to quality as nearly half?

So, following that logic. 90% is nearly 100%. 10% is nearly zero. And 40% and 60% are basically the same, because they are both nearly 50%.

Re: “gcc will quietly break nearly half of all the packages that it compiles”

#87
post #81

Earlier quoted context omitted.

And it does that by implementing a compiler for a language with existing standard, which is ANSI. If you don't like the ANSI C standard (and I admit it's not perfect), don't use a compiler for ANSI C. Also, this is not just GCC problem, all the existing C compilers have the issue to some extent. After all, STACK (the MIT tool to detect undefined behavior) is based on clang. And ICC exploits the same UB tricks AFAIK.

> And it does that by implementing a compiler for a language with existing standard, which is ANSI. If you don't like the ANSI C standard (and I admit it's not perfect), don't use a compiler for ANSI C. I'm not arguing that GCC should violate the ANSI standard; rather it should provide additional guarantees above the what ANSI requires (which was always the intent of the standard; the standard defines the absolute mi…

> I'm not arguing that GCC should violate the ANSI standard; rather it should provide additional guarantees above the what ANSI requires ...

The problem with that approach is that it introduces dependency on the compiler. The original code was ANSI C and thus should compile fine on all compilers compatible with ANSI C, the new code is not as each compiler will decide to handle undefined behavior differently. Either you'll make the exact compiler a hard dependency (i.e. it always has to be compiled with gcc and fails to build with everything else), or it will produce "correct" binaries on some compilers and "incorrect" binaries on others. That's hardly an improvement.

The only way out of this is either to abandon C and use a language with stronger guarantees, or make the ANSI C more strict by adding the guarantees to the standard. Which is not going to happen, I guess.

> The post is claiming that GCC is the worst of them. Certainly my impression is that clang is substantially less aggressive at exploiting UB; I don't know ICC well enough to comment.

GCC is also the most widely, so people tend to spot issues more often.

All this "problem" is a direct consequence of using C without really understanding what guarantees it does and does not provide, and instead driving by a simplified model of the environment. And then getting angry that the simplified model is not really correct.

Re: “gcc will quietly break nearly half of all the packages that it compiles”

#88
post #14

Earlier quoted context omitted.

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

All click-bait titles are direct quotes.

Yes, and the submission guidelines say "Otherwise please use the original title, unless it is misleading or linkbait." The difference between "40%" and "nearly half" is not significantly misleading.

Re: “gcc will quietly break nearly half of all the packages that it compiles”

#89
post #66

Earlier quoted context omitted.

> This is a case of following the letter of the law (in this case the C standard) while disregarding its spirit: all the undefined behaviour was so that C compilers could accomodate for odd architectures while remaining close to the metal, not so that compiler programmers could go out of their way to turn their compiler into a mine field. Computers don't have spirits; they work as you tell them to work, to the letter…

>Computers don't have spirits But standards committee do. >Rust is extremely impressive because they've found so many ways to do high-level programming while maintaining low-level performance. But they can only do that because they have the benefit of the 4 decades of programming language research that have occurred since the basics of C were designed. I doubt rust could be ported to a 8bit PIC microcontroller, or to…

> I doubt rust could be ported to a 8bit PIC microcontroller, or to a 6502 keeping reasonable performance characteristics

I don't believe this is correct. Most of why Rust avoids UB is that it uses static types much more effectively than C does. Static types are an abstraction between the programmer and the compiler for conveying intent, that cease to exist at runtime. So the runtime processor architecture should be irrelevant.

For instance, in C, dereferencing a null pointer is UB. This allows a compiler to optimize out checks for null pointers if it "knows" that the pointer can't be null, and it "knows" that a pointer can't be null if the programmer previously dereferenced it. This is, itself, a form of communication between the programmer and the compiler, but an imperfect one. In Rust, safe pointers (references) cannot be null. A nullable pointer is represented with the Option type, which has two variants, Some(T) and None. In order to extract an &Something from an Option, a programmer has to explicitly check for these two cases. Once you have a &Something, both you and the compiler know it can't be null.

But at the output-code level, a documented compiler optimization allows Option to be stored as just a single pointer -- since &Something cannot be null, a null-valued pointer must represent None, not Some(NULL). So the resulting code from the Rust compiler looks exactly like the resulting code from the C compiler, both in terms of memory usage and in terms of which null checks are present and which can be skipped. But the communication is much clearer, preventing miscommunications like the Linux kernel's

    int flags = parameter->flags;
    if (parameter == NULL)
        return -EINVAL;
Here the compiler thinks that the first line is the programmer saying "Hey, parameter cannot be null". But the programmer did not actually intend that. In Rust, the type system requires that the programmer write the null check before using the value, so that miscommunication is not possible.

There are similar stories for bounds checks and for loops, branches and indirect jumps and the match statement, etc. And none of this differs whether you're writing for a Core i7 or for a VAX.

> or letting the programmer take advantage of the platform quirks.

I'm not deeply familiar with that level of embedded systems, but at least on desktop-class processors, compilers are generally better than humans at writing stupidly-optimized code that's aware of particular opcode sequences that work better, etc.

(There are a few reasons why porting Rust to an older processor would be somewhat less than trivial, but they mostly involve assumptions made in the language definition about things like size_t and uintptr_t being the same, etc. You could write a language with a strong type system but C's portability assumptions, perhaps even a fork of Rust, if there were a use case / demand for it.)

Re: “gcc will quietly break nearly half of all the packages that it compiles”

#90
post #29
post #23

Earlier quoted context omitted.

IMO, 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,…

You can't compare traditional GC languages like "Go" to C. They inhabit two different universes.

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

Post reply on HN