Live data from Hacker News

Everything in C is undefined behavior

blog.habets.se

461–470 of 748 posts

Re: Everything in C is undefined behavior

#461

Earlier quoted context omitted.

Turning undefined behavior into implementation defined behavior is rarely a fix, though.

It's a fix that removes the most pointy part of UB. "Going past the end of the array results in addressing arbitrary values" I can live with. "Going past the end of an array results in anything happening" is a hard sell.

Are you talking about creating a pointer (more than one item) past an array, or dereferencing that pointer? Both are currently UB.

For the former, I kinda get it. It may need to be there for cases like with segmented address space where p+10 could actually be a value less than p, for the eventually generated assembly. Maybe it should be fine to create such a pointer, but have it be "indeterminate value" or whatever, if you try to compare that pointer to anything? I don't know enough about compiler internals to say one way or the other.

Dereferencing, though, can only be UB. There may not be a "value" behind that address. There may be a motor that's been I/O mapped, or a self destruct button.

Re: Everything in C is undefined behavior

#462
post #113

Yes there is tons of surprising and weird UB in C, but this article doesn't do a great job of showcasing it. It barely scratches the surface. Here's a way weirder example: volatile int x = 5; printf("%d in hex is 0x%x.\n", x, x); This is totally fine if x is just an int, but the volatile makes it UB. Why? 5.1.2.4.1 says any volatile access - including just reading it - is a side effect. 6.5.1.2 says that unsequenced…

Volatile is a type system hack. They should have done a more principled fix, and certainly modern languages should not act as though "C did it" makes it a good idea. The reason for the hack is that very early C compilers just always spill, so you can write MMIO driver code by setting a pointer to point at the MMIO hardware and it actually works because every time you change x the CPU instruction performs a memory wri…

I agree that marking the read/write as special rather than the variable itself would be nice, although it would also be nice if C/C++ was more consistent in the way things like this are done. Maybe given std::atomic and std::mutex as template/library features, supported by compiler intrinsics, it would be nice to have "volatile" supported in a similar way.

As a nit pick, I don't think this is correct use of "spill". Register spilling refers to when a compiler's code generator runs out of registers and needs to store variables in memory instead. In the MMIO case you are reading/writing via a pointer, so this is unrelated to registers and spilling behavior.

Re: Everything in C is undefined behavior

#463

Can anyone explain why this is undefined behaviour? UBSan calls it "indirect call of a function through a function pointer of the wrong type" struct foo {int i;}; int func(struct foo *x) {return x->i;} int main() { int (*funcptr)(void*) = (int (*)(void*)) &func; struct foo foo = { 42 }; return funcptr(&foo); } While this is all kosher per the language lawyers: struct foo {int i;}; int func(void *x) {return ((struct f…

It's undefined behavior due to the "strict aliasing" rule. You're simply not allowed to cast one pointer type to another (ever!) except for the following exceptions:

- casting an object pointer to or from void*

- casting an object pointer to or from char*

You're not doing either of those things. A function pointer is not an object pointer (the standard does not guarantee that the two kinds of pointer even have the same size/representation, and in fact on some esoteric hardware they don't), and even if it were, you aren't casting to or from void* or char*. So it's UB for two separate reasons.

Re: Everything in C is undefined behavior

#464
post #284

Earlier quoted context omitted.

The problem is that a lot of the flexibility introduced by UB doesn't serve the developer . Take signed integer overflow, for example. Making it UB might've made sense in the 1970s when PDP-1 owners would've started a fight over having to do an expensive check on every single addition . But it's 2026 now. Everyone settled on two's complement, and with speculative execution the check is basically free anyways. Leaving…

You can run your code under ASAN and UBSAN nowadays, it will catch many or most of issues as they happen. But that's completely besides the point. UB on signed overflow, or really most of UB, is not unrelated to C flexibility. It is a detail of the spec related to portability and performance. IIRC it is even required to make such trivial optimizations as turning for (int i = 0; i into for (Foo *p = a, *last = a + n;…

How is undefined behavior necessary for this transformation?

Re: Everything in C is undefined behavior

#465

Earlier quoted context omitted.

I do not see there a promise that the cast will produce an invalid pointer, nor anything prohibiting the compiler from rounding the pointer down, thus producing a valid one. “Converted” does not require bit copy. I don’t see how this interpretation is against any section of the spec.

> Otherwise, when converted back again, the result shall compare equal to the original pointer. Doesn't this part exclude the possibility of rounding down?

No cause that requires initial alignment.

Re: Everything in C is undefined behavior

#466
post #154

Earlier quoted context omitted.

I do not see there a promise that the cast will produce an invalid pointer, nor anything prohibiting the compiler from rounding the pointer down, thus producing a valid one. “Converted” does not require bit copy. I don’t see how this interpretation is against any section of the spec.

> rounding the pointer down, thus producing a valid one A "valid" pointer to the wrong object ?

Which is ok since it is UB to deref

Re: Everything in C is undefined behavior

#468
post #458
post #405

Earlier quoted context omitted.

> An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine The execution path has unknown side effects, and so the execution path must be strictly followed. That's uh... The entire point of that section in the C stan…

Previously discussed here: https://news.ycombinator.com/item?id=33770277 UB supersedes volatile, once the compiler hits UB then all bets are off. Compilers can and do optimize out UB branches, which is almost never what you want... yet here we are.

From that thread: https://news.ycombinator.com/item?id=33770905

>> The moment you enter a compilation unit (assuming no link optimizations) with a state which at some point will run into undefined behavior all bets are of. [...] Yes, UB can "time travel"

> Close, but not quite. This is a common misconception in the reverse direction.

> Abstractly, what UB can do is performing the inverse of the preceding instructions, effectively making the abstract machine run in reverse. However, this is only equivalent to "time-traveling" until you get to the point of the last side effect (where "side effect" here refers to predefined operations in the standard that interact with the external world, such as I/O and volatile accesses), because only everything since that point can be optimized away under the as-if rule without altering the externally visible effects of the program.

> As a concrete, practical example, this means the following: if you do fflush(stdout); return INT_MAX + 1; the compiler cannot omit the fflush() call merely because the subsequent statement had undefined behavior. That is, the UB cannot time-travel to before the flush. What the program can do is to write garbage to the file afterward, or attempt to overwrite what you wrote in the file to revert it to its previous state, but the fflush() must still occur before anything wild happens. If nobody observes the in-between state, then the end result can look like time-travel, but if the system blocks on fflush() and the user terminates the program while it's blocked, there is no opportunity for UB.

Re: Everything in C is undefined behavior

#469

Earlier quoted context omitted.

You're missing the point. Volatile forces two loads of a value that may have changed in the middle. So the value of "x" may depend on the time/order of load.

Which is, if I understand correctly, the entire point of volatile. Don't use it if you don't want that behavior. And in fact, in the example given, if there is something (another thread or whatever) that can change the value of x, then you don't know what either number will be. Well, in that circumstance, without volatile, it may print the same number both times, but you still don't know what the number will be (unle…

If that behavior is the entire point, then I think the bigger point is that the spec should reflect that and not call it undefined.

Re: Everything in C is undefined behavior

#470
post #303

Earlier quoted context omitted.

The problem is that the function call as a whole is UB. Having the original example compile to the equivalent of volatile int x; int a = x; int b = x; printf("%x %d\n", a, b); is equally valid as volatile int x; int a = x; int b = x; printf("%x %d\n", b, a); , and neither needs to have the same output as your proposed fix. C could've specified something like "arguments are evaluated left-to-right" or "if two argument…

Not only is "arguments are evaluated left-to-right" less easy to formalize than you think, it would also make all C code run slower, because the compiler would no longer be able to interleave computations for more efficient pipelining. The same goes for "expression is [only evaluated once]/[always evaluated twice]". Of course the developer is navigating a minefield every time they use volatile, that's why it's called…

the compiler can still interleave anything it shows is side-effect free; it’s hard to show that something would benefit from being reordered without analyzing it well enough to determine what side effects it has
Post reply on HN