Live data from Hacker News

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

metzdowd.com

41–50 of 110 posts

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

#41

Inknow 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 :(

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

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

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

#42

Earlier quoted context omitted.

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

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

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

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

#43
post #37
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,…

Umm wait. Undefined behavior is where the language specification is not 100% precise, and compiler implementations can differ on produced code. Go and Rust only have single implementations. The specification for both are very brief. Are you claiming that a clean box implementation of Go and Rust would always behave identically? I have only one thing to say: I clicked through the Golang spec for 30 seconds and found t…

Undefined behavior has a very specific meaning for C. It doesn't mean "not 100% precise". It means 100% imprecise. The C standard only gives any guarantees on what your program will do if you never invoke UB. If you do, well then it can do literally whatever it wants including deleting all your files.

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

#44

Inknow 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 :(

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

Compilers will do things like remove a memset clearing a chunk of memory to zero (because it detects that the variable isn't read again). That sort of thing is bad for security.

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

#45

Inknow 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 :(

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

The problem is that it's often perfectly clear, reasonable code on all the systems it was intended to run on. For example, on all Unix-like systems, pointer arithmetic is simply arithmetic and behaves like it. (C's predecessor didn't even have separate pointer and integer types.) So prior to compiler optimisations, this series of operations is safe and well-behaved on all architectures Linux supports even if a is NULL:

  int *b = &a->something; // pointer arithmetic, doesn't dereference a.
  if(a == NULL) return 0;
  else something_critical = a->somethingelse;
However, some non-Unix address models that Linux doesn't support don't permit pointer arithmetic on NULL pointers. So the ANSI C standards committee declared it undefined. Which means that gcc can - and eventually did - eliminate the NULL pointer check. This has resulted in privilege escalation vulnerabilities in Linux that didn't exist until gcc decided to optimise the code, some of them quite well-hidden.

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

#46
post #37
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,…

Umm wait. Undefined behavior is where the language specification is not 100% precise, and compiler implementations can differ on produced code. Go and Rust only have single implementations. The specification for both are very brief. Are you claiming that a clean box implementation of Go and Rust would always behave identically? I have only one thing to say: I clicked through the Golang spec for 30 seconds and found t…

Your definition of undefined behaviour is actually the definition for unspecified behaviour.

Unspecified behaviour is usually intentional ambiguity either to give wiggle room for an optimizer, or to accommodate platform variance. Writing a program with that invokes unspecified behaviour isn't normally a problem, as long as you're not relying on a specific result. Order of argument evaluation is a common example.

Relying on undefined behaviour is almost always bad, and almost always avoidable. That's where the nasal demons come from. Dereferencing null, alias-creating pointer typecasting, etc.

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

#47
post #30

Earlier quoted context omitted.

There's a parable where a man goes to the doctor and says, "Doctor, whenever I drink my coffee with the spoon in the cup, the spoon handle pokes me in the eye and it hurts." And the doctor says, "Well, stop doing that." If you wrote `foo(bar(), baz())` and `baz()` relies on state mutated by `bar()`, your code is bad, and you should feel bad, because experiencing those bad feelings is the way you learn to not write ba…

If bar() returned the mutated state, he could just do: foo(baz(bar()))

I think a better design would be to separate mutators from pure functions. If a procedure mutates state, it should have a void return type, and if a procedure returns a value it should be a pure function that doesn't mutate state.

This is, of course, a rule of thumb, not a hard law. Some exceptions:

1. I think it's okay (and in fact, idiomatic in C) to mutate state and return some sort of information about what occurred (i.e. a success flag, a number of characters written, etc.).

2. Isolated mutations such as logging sometimes make sense in an otherwise pure function.

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

#48

Earlier quoted context omitted.

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

Compilers will do things like remove a memset clearing a chunk of memory to zero (because it detects that the variable isn't read again). That sort of thing is bad for security.

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

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

#49
post #31

Earlier quoted context omitted.

There's a parable where a man goes to the doctor and says, "Doctor, whenever I drink my coffee with the spoon in the cup, the spoon handle pokes me in the eye and it hurts." And the doctor says, "Well, stop doing that." If you wrote `foo(bar(), baz())` and `baz()` relies on state mutated by `bar()`, your code is bad, and you should feel bad, because experiencing those bad feelings is the way you learn to not write ba…

Uhh... The question was "what's an example of undefined behaviour". I gave one, specifically of an example that could realistically break by relying on undefined behaviour. That's it. Simple education. In the context of this post I think that's a good thing because not everyone will understand the topic. You, however, seen to have read some sort of agenda in the question, which I find a little baffling...

But that's not "undefined behavior" either. The post misleads people.

Certain things are unspecified. It can call in any order for example.

Other things are undefined. If you do them your program is no longer valid at all, and can crash or corrupt.

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

#50
post #23

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

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

Yes, the C language specification is a bit shit, it leaves too much leeway to compilers so that C compiler for broken, niche architectures can be written. However, I disagree with you. All this badness in the C specification did not stop people from writing reasonable C compilers for reasonable architectures for decades.

The real problem here is competition. Gcc is in a competition with clang to produce fast code which makes the gcc developers feel justified when they exploit undefined behaviours for marginal optimizations.

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.

Post reply on HN