Live data from Hacker News

Falsehoods programmers believe about undefined behavior

predr.ag

81–90 of 233 posts

Re: Falsehoods programmers believe about undefined behavior

#81

Earlier quoted context omitted.

I don't think this proves the point, because foo() will not invoke UB unless it is called with a NULL argument. It is true that when foo() is called with a NULL argument, the UB will be realized before bar() has had a chance to run (EDIT: this doesn't actually appear to be true for the given code sample, neither Clang nor GCC elide this NULL check: https://godbolt.org/z/v9v74c3jd , which makes sense as bar() could ca…

> It is true that when foo() is called with a NULL argument, the UB will be realized before bar() has had a chance to run. But the line with UB has already been proven reachable prior to the actual dereference, so the UB has in a sense already been invoked. The point of those numbered lines was to refer to how people normally think about UB. Someone seeing that function may be tempted to say that it's not UB to call…

> Someone seeing that function may be tempted to say that it's not UB to call with a NULL value as long as the dereference doesn't happen (say, bar() could call exit() or it could run an infinite loop).

An infinite loop is UB for precisely this reason: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm So an infinite loop cannot prevent the invocation of UB, because an infinite loop is itself UB.

I would expect that a call to exit() inside bar() would prevent eliding of the NULL check. If the program calls exit() before the dereference, it does not invoke UB and therefore should run correctly. I tried your example and Clang and GCC do not actually elide the NULL check in this case, probably for this reason: https://godbolt.org/z/cnWPz3o59

> The standard talks about executions in the sense of theoretical executions by the C abstract machine, not actual executions of the compiled binary. I thought the other comment was rendering to the latter notion of execution, but perhaps I was wrong and it was also talking about the former.

I don't see how this would change the analysis. Running the binary triggers actual executions of both the abstract machine and the physical binary.

Re: Falsehoods programmers believe about undefined behavior

#82
post #44

Earlier quoted context omitted.

This is an absurd reduction of the current situation. > That interpretation of the specification language is clearly bogus as that means more or less all C and C++ programs can be compiled to a single return instruction - when compiler devs introduced this new new interpretation of UB, it turned out they had to then go through and add a bunch of exceptions for basics like memset, memcpy, and memmove could work withou…

Read the gcc mailing list. There is alot of user hostile "fix your broken program" from language lawyers. I think aliasing rules messes up memcpy if you are dogmatic?

I think memcpy is fine because it operates on a special type that is allowed to alias anything. Strict aliasing has other fun issues like the impossibility of implementing malloc-like functions or the lack of any requirement for access to non-char members of structs to work at all[0].

[0]: https://stackoverflow.com/questions/49298704/how-reason-abou...

Re: Falsehoods programmers believe about undefined behavior

#83
post #29

I am always amazed by articles like these. This guy thoroughly covers this topic. I cannot say I have ever written anything that ran into or caused undefined behavior. And I have programmed for a long time. Is this just an interesting topic or do people actively push the compiler to see what it will do? Maybe I am a boring/simple programmer. :)

> I cannot say I have ever written anything that ran into or caused undefined behavior. But how would you know? :)

These days it is not quite unknowable, if you always runs your programs with the address sanitizer and UB sanitizer on. They aren't perfect but they would provide a pretty good indicator of whether there was any UB or not.

Re: Falsehoods programmers believe about undefined behavior

#84
post #48

It is not explained why points 31 to 36 are valid, unlike HDL synthesis tools, C compilers are pretty deterministic, same input same output, except timestamps, etc. If an UB adopt X behavior one time, a reproducible build will take same X behavior the next time. Of course I am not negating the fact that this undefined in the first instance nor condoning the UB usage.

> C compilers are pretty deterministic, same input same output, except timestamps, etc. "Sometimes gcc will opt to use a randomized model to guess branch probabilities, when none are available from either profiling feedback (-fprofile-arcs) or __builtin_expect. This means that different runs of the compiler on the same program may produce different object code." (from the gcc manual)

Looked into this and it appears it was not the case; the docs were confusingly written, and the docs appear to have been rewritten some time ago

https://marc.info/?l=gcc&m=102068997020453&w=2

Re: Falsehoods programmers believe about undefined behavior

#87
post #22

13. But if the line with UB isn't executed, then the program will work normally as if the UB wasn't there. 14. Okay, but if the line with UB is unreachable (dead) code, then it's as if the UB wasn't there. 15. If the line with UB is unreachable code, then the program won't crash because of the UB. 16. If the line with UB is unreachable code, then the program will at least stop running somehow and at some point. This…

Can someone with more knowledge chip in? If I define

  void bar(int* const p, int n) {
    for (int i=0; i
it might get optimized via loop-invariant code motion to

  void bar(int* const p, int n) {
    int m = *p
    for (int i=0; i
so it could still be UB when called with p=NULL and n=0, right?

Edit: Oops, in the previous version I used a write instead of a read on *p, making the optimization invalid.

Re: Falsehoods programmers believe about undefined behavior

#88

Earlier quoted context omitted.

> But there are better and worse things compilers can do in the presence of UB, and the big one is warning you about it when it possibly can. The problem is that this is really hard to do without producing either too much or too little warnings, as undefined behaviour is usually input-triggered; and the compiler usually doesn't know all constraints on the arguments that the programmer knows about. For example, the fu…

We're re-litigating that other thread... but the function in the other thread had a conditional (trying to check for overflow after the fact) which would never be true, regardless of x. And it could statically prove this. And that proof, the compiler then decided to use to remove the code, ignoring the red flag that the programmer undoubtedly had tried (and failed!) to achieve something with it. I would have preferre…

While I agree that in general a warning about "condition is always true/false"-style checks is useful, it's also a bit tricky to do without false positives. With macros and inlining (and in C++, templates) you can easily end up with conditionals of which any given instance is always true or false, but that aren't superfluous. Portability is another example. Depending on your environment and code style hitting these might be exceptional and warning-worthy, or something that happens all over the place.

Re: Falsehoods programmers believe about undefined behavior

#89
post #15

Earlier quoted context omitted.

No, it isn't. The line with UB has to be reached in the execution for the execution to be undefined.

The compiler isn't required to know if code is unreachable. For example, `if (fermats_last_theorem_is_wrong()) {*p == 1} ` will imply to a modern compiler that p is not NULL in the scope where it is defined, even if we now know that this code will never be reached (assuming that function is correctly written). Or, here is a better example: for(int i=0; i > n;) { } *p=1; Here the compiler will assume that the loop ter…

If a some translation unit defines int fermats_last_theorem_is_wrong(void) { return 0; } then a well-defined program can execute that sequence even with p equal to NULL.

Re: Falsehoods programmers believe about undefined behavior

#90

Earlier quoted context omitted.

That's from the spec POV. On x86 for example, signed int overflow is well defined.

That's irrelevant. A C compiler targetting x86 has no obligation at all to handle signed integer overflow in the same way the processor natively does. Because it is not an assembler - it is compiling against the theoretical C virtual machine. This is for the same sort of reason that unsigned overflow does have a well defined meaning in C, and will always do exactly that even for targets that do not natively do that (…

Compiler developers may not think they have that obligation, but they should
Post reply on HN