Live data from Hacker News

Undefined behavior in C is a reading error

yodaiken.com

271–280 of 503 posts

Re: Undefined behavior in C is a reading error

#271
post #218

Earlier quoted context omitted.

> In the C89 standard, undefined behavior becomes undefined UPON USE OF the thing that is undefined. Is that still the case for current C standards, or did something change in C99/C11?

I don't have the C11 standard. But that part of the passage remained unchanged in C99. In C89 there was a list of PERMISSIBLE things that compilers could do upon encountering undefined behavior. In C99 that was changed to a list of POSSIBLE things. And compilers have taken full advantage of that.

Ah. That sounds like the argument made in "One Word Broke C" [0, 1]. I can't say I agree with that argument, though.

As pointed out here and in the HN comments on that article, the phrase "ignoring the situation completely with unpredictable results" is present in both those standards, and is arguably what allows aggressive compiler optimizations to be made, since to a first approximation those optimizations rely on ignoring control flow that encounters UB.

[0]: https://news.quelsolaar.com/2020/03/16/how-one-word-broke-c/

[1]: https://news.ycombinator.com/item?id=22589657

Re: Undefined behavior in C is a reading error

#272
Let‘s just agree that C is not a good programming language according to modern standards: basic integer types are a complete mess with varying sizes and confusing implicit casts. The advantages of null terminated strings do not matter anymore, but the disadvantages matter more and more. The compilation model with headers is a mess compared to module systems. The C Preprocessor is an abomination. The complexity of the language is surprising.

Re: Undefined behavior in C is a reading error

#273
post #197
post #59

Earlier quoted context omitted.

> Vanilla C no longer gives them the tools to do that on a modern processor Can you elaborate on this point?

C was created during a time where instructions were executed linearly with no vectorization, memory was a flat space with no CPU caches, and there wasn’t a branch predictor that may or may not execute the correct program branch in advance. The list goes on but the rest is beyond my scope. C was designed for a now obsolete computer architecture model and over the years this old model has essentially become an abstract…

Thanks for the link. Excellent article. Those are all great points and I like having them summarized in one place, because I am indeed a little behind in my modern architecture theory.

However it has always been acknowledged that C was by definition a sort of simplified computing model. For example, when I first learned see the 8086 architecture was popular but it was competing with many others and it was already dramatically different from the PDP-11 virtual machine you describe. The 286, 386 and so on had funky indexing modes and address space weirdness but so did just about every other processor of the time.

There is likely never to be a single unified architecture that anyone agrees on, and the developers of C understood this, certainly by the time the 1989 standard was hammered out. So compiler directives, pragmas, and maybe even language extensions were expected on a per CPU basis, no?

Re: Undefined behavior in C is a reading error

#274

Earlier quoted context omitted.

You are mistaken, the C standard is quite clear that it does not make any guarantees regarding the behavior of programs that exhibit undefined behavior, and that signed integer overflow is undefined behavior.

They're not mistaken. What compilers will do is assume that UB don't happen. If no UB happens, that means `param + 16` never overflowed, therefore there are always exactly 16 operations.

Or they assume "param + 16" will never overflow, so they emit an ADD instruction and use whatever result it yields.

Saying that a compiler "assumes" anything is anthropomorphic. A compiler may behave (generate code) in a manner that does not take the presence or absence of undefined behavior into account. If you just say it assumes something, that doesn't tell you what it will do based on that assumption.

Generating code that yields exactly 16 iterations is one of infinitely many possible consequences of undefined behavior.

If the mathematical value of `param + 16` exceeds `INT_MAX`, then the code has undefined behavior. The C standard says nothing at all about how the program will behave. A conforming compiler can generate code that iterates 42 times and then whistles Dixie. The non-normative note under the definition of "undefined behavior" does not constrain what a conforming implementation is allowed to do.

"imposes no requirements" means "imposes no requirements".

Re: Undefined behavior in C is a reading error

#275
post #149

Because there seems to be some confusion in this thread: - "Implementation-defined behavior" means that the C standard specifies the allowable behaviors that a C implementation must choose from, and the implementation must document its particular choice. - "Unspecified behavior" means that the C standard places no particular restrictions on the behavior, but a C implementation must pick a behavior and document its ch…

I noticed that I mixed up "implementation-defined behavior" and "unspecified behavior" a bit. Here are the actual definitions:

implementation-defined behavior: unspecified behavior where each implementation documents how the choice is made

unspecified behavior: use of an unspecified value, or other behavior where this International Standard provides two or more possibilities and imposes no further requirements on which is chosen in any instance

Re: Undefined behavior in C is a reading error

#276
post #48

Earlier quoted context omitted.

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?

About your edit:

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

That is actually not so useless at all: if you try to compile and link a program that declares and calls a function but does not define it, you will typically get a linker error about an unresolved reference. If the name matches a non-ISO C library function, however, the implementation cannot know whether your program is in error or whether you want to use that library function, and will usually accept it. For this reason, the C standard does actually make it clear that using write(2) is UB to make it clear that implementations are not required to diagnose that as an error.

Re: Undefined behavior in C is a reading error

#277

Earlier quoted context omitted.

> e.g. removing a check for for overflow is definitely NOT ignoring the behavior. Deleting write because it would be undefined behavior for a pointer to point at some location is also NOT ignoring the behavior. Depending on how you look at it, this is ignoring the behavior. For example, say you have this: int f(int a) { if (a + 1 You have 2 situations: 1. a + 1 overflows 2. a + 1 does not overflow Situation 1 contain…

More than slightly convoluted. The obvious intention is that the compiler ignores overflow and lets the processor architecture make the decision. Assuming that overflow doesn't happen is assuming something false. There's no excuse for that and it doesn't "optimize" anything.

> The obvious intention is that the compiler ignores overflow and lets the processor architecture make the decision.

If that were the case, wouldn't signed overflow be implementation-defined or unspecified behavior, instead of undefined behavior?

> Assuming that overflow doesn't happen is assuming something false.

It's "false" in the same way that assuming two restrict pointers don't alias is "false". It may not be universally true for every single program and/or execution, but the compiler is explicitly allowed to disregard cases where the assumption may not hold (i.e., the compiler is allowed to "ignor[e] the situation completely").

And again, the compiler is allowed to make this assumption because undefined behavior has no defined semantics. If the compiler assumes that no undefined behavior occurs, and undefined behavior does occur, whatever happens at that point is still conforming, since the Standard says that it imposes no requirements on said program.

> it doesn't "optimize" anything.

...But it does allow for optimizations? For example, assuming signed overflow can allow the compiler to unroll/vectorize loops when the loop index is not the size of a machine word [0]. Godbolt example at [1].

[0]: https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759...

[1]: https://godbolt.org/z/sP6WYPeT7

Re: Undefined behavior in C is a reading error

#278
post #48

Earlier quoted context omitted.

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

UB, in this context, is very explicitly used in the standard: it is undefined behavior related to a construct that the standard describes.

> behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

That is literally the definition of UB from the C standard. It is explicitly also about constructs that the standard does not describe. That makes sense: the standard does not and cannot define the behaviour for any construct not in the standard, so cannot impose any requirements for such constructs, and that is all UB is: something where the standard imposes no requirements.

Re: Undefined behavior in C is a reading error

#279
post #149

Because there seems to be some confusion in this thread: - "Implementation-defined behavior" means that the C standard specifies the allowable behaviors that a C implementation must choose from, and the implementation must document its particular choice. - "Unspecified behavior" means that the C standard places no particular restrictions on the behavior, but a C implementation must pick a behavior and document its ch…

I would note that the article is explicitly contesting the definition of UB that you are giving here (though you are absolutely right that this is the de facto definition used by all major compilers, and the commtitee). Basically the article is arguing that UB should be similar to Unspecified behavior - behavior that the implementation leaves up to the hardware and/or OS. I'm not sure where I fall to this issue, thou…

> In particular, nothing in the wording of the standard expliclty says that an implementation is expected to assume UB doesn't happen, or that a standard-conforming program can't have UB.

An implementation isn't expected to assume that UB doesn't occur, but it is allowed to assume that.

With regard to programs, the C standard has two different notions of conformance (cf. chapter 4 Conformance). There are strictly conforming programs, which may not rely on anything that would depend on the specifics of a particular C implementation, which of course includes UB. Strictly conforming programs are thus guaranteed to have the same behavior on all conforming C implementations. Then there is the larger class of conforming programs, which is defined as programs acceptable to a (particular) conforming C implementation.

Strictly conforming C programs are severly limited in what they can do. It has been argued that there are hardly any useful strictly conforming C progams.

Non-strictly conforming programs are conforming with respect to a specific C implementation. It is then the job of the C implementation to define what programs it accepts beyond strictly conforming ones, and how it handles (or doesn't handle) undefined behavior. Everything is possible here, from the infamous DeathStation 9000 (with the most insidious UB behavior) to a fully deterministic C implementation that processes any program in the most unsurprising developer-friendly fashion.

There is arguabley a misconception that C is a single language. It is effectively rather a family of languages, for which the C standard only defines a common denominator.

Re: Undefined behavior in C is a reading error

#280
post #119

Earlier quoted context omitted.

Anecdotally, I find about as many bugs in my Java code at work, as I do in my hobby C code, including "dereferencing null pointers" aka NullPointerExceptions.

"Undefined behavior" is a term of art relevant to C, meaning that the standard no longer has any comment about what happens. Thus the comments about launching nukes, or destroying your machine, etc., being standards complaint, even though obvious real compilers won't actually emit code that does that. Dereferencing a nil pointer in Java is not undefined behavior. It is defined; it throws a NullPointerException, and "…

In single-threaded programs, Java will neatly do what you expected, it lacks this excitement from C, when you try to dereference a null pointer, iterate past the end of an array or whatever in Java you'll just get some sort of Throwable...

But in concurrent programs things get exciting again. Java would like to be somewhat fast on actual hardware, and the actual hardware behaviour of memory is exciting, so, Java is also exciting, depending on how your hardware works.

You won't get C's full-blown "Undefined behaviour", but you can get some pretty astonishing outcomes, for example time travel can happen. If one thread changes A, then B, then C, it is permissible in Java that from the perspective of another concurrent thread the value A is unchanged, after B and/or C have changed.

It turns out humans are bad at coping with this even though it's much less scary than Undefined Behaviour, and so some more modern languages than Java offer ways to get memory behaviour that doesn't hurt your brain as much in exchange for reduced performance.

Post reply on HN