Live data from Hacker News

Undefined behavior in C is a reading error

yodaiken.com

41–50 of 503 posts

Re: Undefined behavior in C is a reading error

#41

Earlier quoted context omitted.

The C standards have the perfectly fine name "implementation dependent" to describe those things. Undefined behavior is much less constrained than implementation dependent, adn thus more problematic.

> The C standards have the perfectly fine name "implementation dependent" to describe those things. That term is not used by the C standards. Do you mean "implementation-defined"? asm is not among the explicitly specified implementation-defined behaviors, it's listed under "Common extensions". I don't see any mention at all of syscalls in C99. (I'm working with http://www.dragonwins.com/courses/ECE1021/STATIC/REFEREN…

I'm not sure why syscalls would be UB; it's just not something defined by the C standard.

Edit: To clarify, I meant UB in the sense it is typically used in these discussions, where the standard more-or-less explicitly says "If you do X, the behavior is undefined." Not in the literal sense of "ISO C does not say anything about write(2), hence using write(2) is undefined behavior according to the C standard", which seems like a rather tautological and useless statement to me.

Re: Undefined behavior in C is a reading error

#42
> license for the kinds of dramatic and unintuitive transformations we’ve seen from the compilers, and any indication that undefined behavior should be a vehicle for permitting optimizations.

Does anyone have an example of a time where Clang or GCC actually did something bad upon witnessing undefined behavior, rather than simply doing nothing, as the standard proposes? I ask because every time I've seen people get mad about UB it's always because they envisioned that the compiler would do something, but instead the compiler did nothing.

Re: Undefined behavior in C is a reading error

#43
post #24

I think the real problem stems from the mismatch between modern processors and the processors C was originally designed for. C programmers want their code to be fast. Vanilla C no longer gives them the tools to do that on a modern processor. Either the language needs to be extended or the compiler needs to get more creative in interpreting the existing language. The latter is the least disruptive and it doesn't stop…

When compiler writers have get "creative" with C undefined behavior, programming C no longer produces predictable results.

> least disruptive

Like starting to optimize away loop checks that can "never happen" because signed integer overflow is UB, suddenly changing the behavior of programs that were fine for years?

I wish I could just fence off this insanity by never starting another project in C. Unfortunately, C is ubiquitous in the ecosystem so all of us are stuck cleaning it up.

Re: Undefined behavior in C is a reading error

#44
I'm not convinced. The argument seems to hinge to a very large extent on the sentence:

> Permissible undefined behavior ranges from A, to B, to C.

The observation that "Permissible" has a specific meaning is important and interesting. But what about "ranges from ... to ..."? The author reads this as "Permissible undefined behavior is either A or B or C.", but that seems like a stretch to me. (Unless ISO defines "ranges" to mean just this.)

Also, in the above, A = "ignoring the situation completely with unpredictable results". Both "ignoring" and "unpredictable" do a lot of heavy lifting here. In the signed integer overflow case discussed in the linked GCC bug report (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=30475), one could very well argue that "ignoring" a case where signed arithmetic overflows is exactly GCC is doing. It "ignores" the possibility that a + 100 might overflow, hence obviously a + 100 > a is true, leading to results that the reporter considers "unpredictable". Somehow the author seems to think that this is not what the wording intended, but they also fail to explain what they think should happen here.

Sounds to me like the wording has always been very vague.

Also:

> Returning a pointer to indeterminate value data, surely a “use”, is not undefined behavior because the standard mandates that malloc will do that.

Yes, malloc will do that, but using a pointer to indeterminate data is not the same as using the indeterminate data itself. The author is doing themselves a disservice by misreading this.

Re: Undefined behavior in C is a reading error

#45
post #31

The author suggests that the text following the definition of "undefined behavior", listing the permitted or possible range of undefined behavior, should be read to restrict the consequences. But the first possibility listed is "ignoring the situation completely with unpredictable results". Surely that covers any possible consequences. The author also says: > Returning a pointer to indeterminate value data, surely a…

> For example, if signed integer overflow yielded an unspecified result rather than causing undefined behavior, I wonder if any implementations would be adversely affected.

I suspect so - makes it harder to reason about loop counts because the compiler can't necessarily guarantee that an incremented loop counter won't become negative and thus the loop needs to iterate more.

E.g. something like for (int i=param; i That's not an excuse for but having any way to do proper overflowing operations on signed integers though.

Re: Undefined behavior in C is a reading error

#46
I'm afraid the author themselves is misreading the definition of undefined behavior. Undefined behavior is not "behavior upon use of a nonportable or erroneous program construct". That rephrasing completely changed the meaning. Undefined behavior is, as the C standard states, "behavior [, ...,] for which the Standard imposes no requirements". The whole ", upon use of...," part is just exemplifying situations in which undefined behavior can occur. The standard will sometimes say that a certain construct results in undefined behavior but more importantly any construct for which the standard does not specify a certain behavior has (what else?) undefined behavior.

Re: Undefined behavior in C is a reading error

#47
> C can accommodate significant optimization while regaining semantic coherence – if only the standard and the compilers stop a lazy reliance on a mistaken reading of “impose no requirements”.

That wouldn't be enough, because, sadly for us, rewriting history is only an option in Git repositories and speculative fiction. On this timeline, it doesn't much matter how the standard should have been interpreted (I'll refrain from opining on that), what matters is how the standard was interpreted, and its influence on the behavior of major compilers.

Given the current situation, it seems to me that reaping the benefits of such an enterprise would require getting everyone on board, a mode that's based on the new interpretation, leaving it turned off by default, and then basically never actually using it for fear of breaking compatibility with toolchains that have to use an older version of the compiler for a good decade or two while we wait on them to age out of existence.

I'm not sure the world actually has much practical use for an unapproachable ivory tower dialect of C. Personally, I'd much rather have a go at a language like Zig that's taken upon itself to dream even bigger.

Re: Undefined behavior in C is a reading error

#48

Earlier quoted context omitted.

> The C standards have the perfectly fine name "implementation dependent" to describe those things. That term is not used by the C standards. Do you mean "implementation-defined"? asm is not among the explicitly specified implementation-defined behaviors, it's listed under "Common extensions". I don't see any mention at all of syscalls in C99. (I'm working with http://www.dragonwins.com/courses/ECE1021/STATIC/REFEREN…

I'm not sure why syscalls would be UB; it's just not something defined by the C standard. Edit: To clarify, I meant UB in the sense it is typically used in these discussions, where the standard more-or-less explicitly says "If you do X, the behavior is undefined." Not in the literal sense of "ISO C does not say anything about write(2), hence using write(2) is undefined behavior according to the C standard", which see…

What do you think UB is if not something where the behaviour is not defined?

Re: Undefined behavior in C is a reading error

#49

Earlier quoted context omitted.

> The C standards have the perfectly fine name "implementation dependent" to describe those things. That term is not used by the C standards. Do you mean "implementation-defined"? asm is not among the explicitly specified implementation-defined behaviors, it's listed under "Common extensions". I don't see any mention at all of syscalls in C99. (I'm working with http://www.dragonwins.com/courses/ECE1021/STATIC/REFEREN…

I'm not sure why syscalls would be UB; it's just not something defined by the C standard. Edit: To clarify, I meant UB in the sense it is typically used in these discussions, where the standard more-or-less explicitly says "If you do X, the behavior is undefined." Not in the literal sense of "ISO C does not say anything about write(2), hence using write(2) is undefined behavior according to the C standard", which see…

So if it is behavior that is not defined by the C standard, would that not make it undefined behavior?

Re: Undefined behavior in C is a reading error

#50
I think the reasoning here is flowing backwards.

The writer wants to believe that C is a well-designed language suitable for writing large programs (because programmers understandably use it that way; there's not really an alternative to C), and so people reading the spec and finding a minefield _must_ be reading the spec wrong. So many important programs are written in C, and so many of them, with a very strict reading of the C standard, can hit cases where their behavior is "undefined". This _is_ scary, if the C-language-lawyers are right!

The C language was originally largely descriptive, rather than prescriptive. Early "C" compilers disagreed on what to do in strange cases (e.g., one might wrap integer overflow, one might saturate, one might have wider ints). Even when using the less-chaotic "implementation defined behavior", behavior can still diverge wildly: `x == x + 1` is definitely `false` under some of those interpretations and maybe `true` in some of those interpretations.

However, the C spec clearly says that the compiler may "ignore the situation" that "the result is ... not in the range of representable values for its type"; it is "permissible" that `x == x + 1` is replaced with `false` despite the "actual" possibility that adding 1 to x produces the same value, if `+` was compiled to be a saturating add.

This has significant practical consequences, even without the "poisoning" result commonly understood of undefined behavior. Since the value is known statically to be `false`, that might be inlined into a call into a function. That function may _dynamically_ re-check `x == x + 1` and find that it is `true`; obviously that function doesn't have a `if (true && false) {` case, so it results in the function misbehaving arbitrarily (maybe it causes a buffer overrun to the argument of a syscall!).

'Intuition' does not make a programming-language semantics. You need to write down all the rules. If you want to have a language without undefined behavior, you need to write down the rules for what must happen, keeping in mind that many examples of undefined behavior, like dereferencing out-of-bounds pointers, _cannot_ be detected dynamically in C without massive performance costs. To detect if a pointer is out-of-bounds, you need to always pair it with information about its provenance; you need to track whether or not the object has been freed, or the stack-frame it came from has expired. Is replacing all pointers with fat-pointers indicating their provenance and doing multiple comparisons before every dereference the "right" way to compile C?

Post reply on HN