Live data from Hacker News

Undefined behavior in C is a reading error

yodaiken.com

31–40 of 503 posts

Re: Undefined behavior in C is a reading error

#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 “use”, is not undefined behavior because the standard mandates that malloc will do that.

Returning a pointer to data is not a use of that data. The fact that its value is indeterminate isn't relevant until you attempt to read it (without first writing it).

It may be worthwhile to reduce the number of constructs whose behavior is undefined, making them implementation-defined or unspecified instead. For example, if signed integer overflow yielded an unspecified result rather than causing undefined behavior, I wonder if any implementations would be adversely affected. (But it would remove the possibility of aborting a program that computes INT_MAX+1.)

I don't think reinterpreting "undefined behavior" as anything other than "the Standard imposes no requirements" is practical. If a program writes through a dangling pointer and, for example, clobbers a function's return address, what constraints could be imposed on what the program might do next?

Re: Undefined behavior in C is a reading error

#32
post #19

Compilers have become more powerful (opening up new ways to exploit undefined behavior) and the primary C compilers are free software with corporate sponsors, not programmer customers (or else perhaps Andrew Pinski would not have been so blithe about ignoring his customer Felix-gcc in the GCC bug report cited above). This is the real problem. We have reached a situation where a small number of compilers dominate the…

Are you arguing that e.g. Turbo C and friends from the 80s were higher quality than modern C compilers?

I believe he’s arguing that they were more sane/pragmatic compilers — they would be inherently less comfortable exploiting UB to do anything other than what people expected or were used to, because there is more real possibility of retribution (GCC could get away with making demons fly out your nose and just lose marketshare [that it doesn’t directly depend on anyways] where a commercial compiler would go out of business)

Re: Undefined behavior in C is a reading error

#33

It's easy to pick on undefined behavior in C when you focus on the more gratuitous undefined behaviors such as signed overflow or oversized shifts. I'm not certain why these are undefined behavior instead of implementation-defined, but my suspicion is that these caused traps on some processors, and traps are inherently undefined behavior. Instead, if you dislike undefined behavior, I challenge you to come up with wor…

> It's easy to pick on undefined behavior in C when you focus on the more gratuitous undefined behaviors such as signed overflow or oversized shifts. I wouldn't even mind the signed integer overflow thing that much if there were a reasonable way in standard C to check whether a signed operation would overflow. It's not impossible to do correctly in a compiler independent way, but ridiculously hard. And slow.

See e.g. https://github.com/postgres/postgres/blob/8bdd6f563aa2456de6...

Re: Undefined behavior in C is a reading error

#34
post #27

It's easy to pick on undefined behavior in C when you focus on the more gratuitous undefined behaviors such as signed overflow or oversized shifts. I'm not certain why these are undefined behavior instead of implementation-defined, but my suspicion is that these caused traps on some processors, and traps are inherently undefined behavior. Instead, if you dislike undefined behavior, I challenge you to come up with wor…

What version of C doesn't require `static int y;` to be initialized to 0?

You're right, it should be automatic storage duration instead of static storage duration.

Re: Undefined behavior in C is a reading error

#36

I don't see any utility in inventing a new reading of the standard. Getting everyone to agree on a new interpretation of a sentence can't possibly be easier than getting everyone to agree on a more clearly worded sentence. The actual thing you'd have to convince everyone of (the utility of the new consensus) and the people you'd have to convince (compiler writers, documentation authors) are the same in both cases.

The difference is that, once decided and written, no party (both old and new) can’t chime in with a new interpretation.

Re: Undefined behavior in C is a reading error

#37

Compilers have become more powerful (opening up new ways to exploit undefined behavior) and the primary C compilers are free software with corporate sponsors, not programmer customers (or else perhaps Andrew Pinski would not have been so blithe about ignoring his customer Felix-gcc in the GCC bug report cited above). This is the real problem. We have reached a situation where a small number of compilers dominate the…

> The C standard is a product of an era where you would pay for your tools and so would demand a refund from any compiler vendor that would treat undefined behavior in an absurd manner.

Since current compilers aren’t out to do anything malicious with UB, but instead simply treat it as “assume this can’t happen and proceed accordingly”, it’s not clear at all to me what you think paid compilers would do here instead: refuse to compile vast swaths of code that currently compiles? Or compile it very pessimistically, forgoing any optimization opportunities by instead assuming it can happen and inserting a bunch of runtime checks in order to catch it then?

In either case, I doubt there’s any real market for “pay money for this compiler and it either won’t build your code, or it will run more slowly”. I’m just old enough to remember the paid C compiler market and the thing that was driving everybody to pay for the latest and greatest upgrade was “how much better is it at optimization than before?”

Re: Undefined behavior in C is a reading error

#38

Earlier quoted context omitted.

> It's easy to pick on undefined behavior in C when you focus on the more gratuitous undefined behaviors such as signed overflow or oversized shifts. I wouldn't even mind the signed integer overflow thing that much if there were a reasonable way in standard C to check whether a signed operation would overflow. It's not impossible to do correctly in a compiler independent way, but ridiculously hard. And slow.

Another thing is that the wording around some of the UB issues is just plain bad. The most extreme probably is the rules around strict aliasing. That there, for quite a while, was uncertainty whether the rules allow type punning by reading a union member when the last write was to another member of a good example of not taking reality into account. Yes memcpy exists - but it is even less type safe!

The union punning trick is UB in C89 and well-defined in C99 and later, although it was erroneously listed in the (non-normative) Annex listing UBs in C99 (removed by C11).

Strict aliasing is another category of UB that I'd consider gratuitous.

Re: Undefined behavior in C is a reading error

#39
post #6
post #4

The author seems to be missing this essential text: "the implementor may augment the language by providing a definition of the officially undefined behavior." Making a system call is undefined behavior in the C standard, but it's not undefined behavior in clang-on-FreeBSD, because the implementors of clang on FreeBSD have defined what those system calls do. Ditto for "asm" (UD unless/until you're running on a compile…

The thing about UB is that it tends to happen when the C standard refuses to specify when a program segment is erroneous or valid. Some C environments treat memory as a large array of undifferentiated bytes or words, by design. Other C environments have tagged, bounds-checked regions of memory, again by design. (For example, the C compiler for the Lisp machine.) Usually, indirecting through a null pointer or walking…

> The idea that UB is carte blanche for implementations to do whatever is an unintended consequence of the vague language of the standard.

Whether or not this was originally intended, it's certainly become the way the standard is written and used today, so that's kind of beside the point.

Further, this is not some new idea that arose from the C standard. It's a basic, core idea in both software engineering and computer science! You define some meaning for your input, which may or may not cover all possible inputs, so that you can go on to process it without considering inputs that don't make sense.

Now, to be fair, the "guardrail-free" approach where UB is silent is a bit out of the ordinary. A lot of software that makes assumptions about its input will at least try to validate them first, and a lot of programming language research will avoid UB by construction. But C is in a unique place where neither of those approaches fully work.

> The C standard allows for both kinds of environments by stating that these behaviors are undefined, allowing the implementation to error out or do something sensible, depending on the environment.

This is true, but it doesn't mean that "something sensible" is actually something the programmer should rely on! That's just asking too much of UB- programmers need to work with the semantics implemented by their toolchain, not make up an intuitive/"sensible" meaning for their undefined program and then get mad when it doesn't work.

For example, if you want to scan through a bunch of memory, tell the language that's what you're doing. Is that memory at a fixed address? Tell the linker about it so it can show up as a normal global object in the program. Is it dynamic? Memory allocators fabricate new objects in the abstract machine all the time, perhaps your compiler supports an attribute that means "this function returns a pointer to a new object."

The solution is not just to shrug and say "do something sensible but potentially dangerous." It's to precisely define the operations available to the programmer, and then provide tools to help them avoid misuse. If an operation isn't in the language, we can add it! If it's too easy to mess up, we can implement sanitizers and static analyzers, or provide alternatives! Yelling about a supposed misreading of "undefined behavior" is never going to be anywhere near as effective.

Post reply on HN