Live data from Hacker News

Everything in C is undefined behavior

blog.habets.se

491–500 of 748 posts

Re: Everything in C is undefined behavior

#491
post #15

> A problem with this is that in order to confirm the findings, you’ll need an expert human. But generally expert humans are busy doing other things. The article suggests using LLMs to identify and fix UB. However as per the above, I think the issue is that we need more expert humans. LLM generated code will eventually contain UB. EDIT: added "eventually"

It would already help a lot when the C and C++ standards start to clean up the list of Undefined Behaviour (e.g. there's a lot of nonsense UB currently in the C standard which could easily become Defined Behaviour - like the "file doesn't end in a new-line character" thing): https://gist.github.com/Earnestly/7c903f481ff9d29a3dd1

The C committee is cleaning up a lot of UB (check https://www.open-std.org/jtc1/sc22/wg14/www/wg14_document_lo... for paper titles like "slaying earthly demons").

But don't misunderstand the goal of that: C and C++ will never get rid of UB. The result of dereferencing an invalid pointer is UB, will always remain UB, and really cannot be anything other than UB.

Re: Everything in C is undefined behavior

#492
post #441

Earlier quoted context omitted.

Where did I say I'm not following the standard?

I interpreted these words: > If you want to be standards correct, yes you have to know the standard well. to mean that being standards-correct is optional. It's not. Every C programmer needs to know every possible UB by heart and never introduce any of it to their code, or else they'll be constantly introducing subtle, hard to debug bugs that contradict the actual code they wrote. Maybe you meant something different…

Of course it's optional (although I didn't mean to imply that). Even using computers at all is optional. I never said that I don't aim to follow the standard, have a clean compiling program without warnings and without UB, etc. I do strive to achieve all of that.

But it's not entirely black and white, either. In practice I'm fine accepting that some bugs are technically UB but whatever, we've found a bug by whatever manifestation (like NULL dereference most likely leading to segfault in practice). I just fix the bug as a bug, and life goes on.

The standard is not perfect, it does have shortcomings. It can be improved. And it can be interpreted to fix some issues. Let's not hold theory over practicality, and let's expect the compiler writers also strive to do the reasonable thing.

Re: Everything in C is undefined behavior

#493

Earlier quoted context omitted.

Couldn’t you just define that function arguments are evaluated left to right? Or just throw an error.

I meant reading the uninitialized variable

There is no uninitialized variable, I explicitly initialized it to 5.

And yes indeed, C could do what Rust does and define the order of evaluation for function arguments.

If the argument expressions are indeed side-effect-less, the compiler can always make use of the "as-if" rule and legally reorder the computation anyway, for example to alleviate register pressure.

Re: Everything in C is undefined behavior

#494

Earlier quoted context omitted.

Can you unravel this further (for those of us who don’t know compilers)? I’ve always assumed access past the end of an array can’t always be detected in C, so I don’t see how those instructions could be eliminated. For example, a dynamically linked library that takes in a pointer, and then writes to the 10 ints after it—whether or not this behavior is defined is determined after that library is compiled, right?

I think the disconnect here is that you're operating on the assumptions built by using common architectures that have solved these problems in implementation specific forms, and you're used to those solutions. But just because those forms are common, doesn't mean the behavior is actually defined. Ex - I might be using a vendor specific compiler for custom embedded devices where dynamic linking isn't available at all,…

I’m not sure there’s a disconnect at all (note that I’m not saagarjah, they and lelanthran seem to be pushing back on each other’s opinions; I’m just asking a clarifying question).

Re: Everything in C is undefined behavior

#495

Earlier quoted context omitted.

Author here. > It barely scratches the surface. I agree. The point of the post is not to enumerate and explain the implications of all 283 uses of the word "undefined" in the standard. Nor enumerate all the things that are undefined by omission. The point of the post is to say it's not possible to avoid them. Or at least, no human since the invention of C in 1972 has. And if it's not succeeded for 54 years, "try hard…

> The point of the post is to say it's not possible to avoid them. Or at least, no human since the invention of C in 1972 has. What are you talking about? UB was coined only in the first C standard, in 1989. Prior to that there was no "If you do this, anything can happen". It was "If you do this, that will happen".

More like, "if you do this, what happens depends on your particular combination of hardware, operating system, and compiler. Don't ask us."

Re: Everything in C is undefined behavior

#496

Earlier quoted context omitted.

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?

IIRC computation of the address is done by computing offset from base pointer as a multiplication in (32-bit) int, (like p + (i * sizeof (Foo)). The right term might overflow, but due to signed overflow being UB, the compiler is able to assume that it does not, so the transformation to do the arithmetic entirely in (64-bit) pointer space is valid.

Re: Everything in C is undefined behavior

#497
post #108

Earlier quoted context omitted.

Fixing easy cases makes the list shorter, so enables more focus on harder cases. And it also signals that you actually do want to improve, just a little bit of boy scout rule goes a long way.

The issue is that the list is infinite (anything not specified is UB), so actually removing any finite amount of UB from the list won't make it shorter. (only slightly tongue-in-cheek, I do believe that removing silly things is worthwhile).

The list of unspecified behaviour is infinite, but the list of undefined behaviour is well defined and finite ;)

Re: Everything in C is undefined behavior

#498
post #303

Earlier quoted context omitted.

Not sure why you're being downvoted. That's completely right. The example is silly. The code is obviously bad, doesn't matter if it's UB or not. I'm also not convinced (yet) that the example really is UB: I agree reading a volatile is "a side effect" in some sense, and GP cited a paragraph that says just that. But GP doesn't clearly quote that it's a side effect on the object (or how a side effect on an object is def…

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…

I understand, that's why I said the code is obviously broken. The problem is not about order of evaluation. It's not about an UB arising from unsequenced volatile reads or whatever.

The problem is simply that the there are two volatile reads where only one was intended. It doesn't matter if there is UB or not. The code doesn't express the intention either way. All you need to know to understand that is that volatile might be modified concurrently (a little bit similar but not the same semantics as atomics).

Re: Everything in C is undefined behavior

#499

Earlier quoted context omitted.

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

That's fair that "spill" probably isn't quite the right word.

Re: Everything in C is undefined behavior

#500

The examples aren't really undefined behavior. They are examples that could become UB based on input/circumstances. Which if you are going to be that generous, every function call is UB because it could exceed stack space. Which is basically true in any language (up to the equivalent def of UB in that language). I feel like c has enough actual rough edges that deserve attention that sensationalism like this muddies f…

The examples are unequivocally UB. Full stop. How to think of this properly is that when you have UB, you are no longer under the auspices of a language standard. Things may work fine for a time, indefinitely even. But what happens instead is you unknowingly become subject to whimsies of your toolchain (swap/upgrade compilers), architecture, or runtime (libc version differences). You end up building a foundation on q…

The first example is dereferencing an integer pointer. That is a valid operation. Now if that pointer isn't valid (and being unaligned is one of many reasons it could be invalid) then calling the function with that invalid pointer will be UB.

An honest discussion would be something more like 'dereferencing pointers can lead to UB on invalid pointers. Here are N examples of that. Maybe avoid using pointers. Maybe consider how other languages avoid pointers. Maybe these shouldn't be UB and instead some other class of error.' And then even more honest discussion would present the upsides of having pointers and the upsides of having these errors be UB.

Instead, the article (and your comment) take this valid operation and presents it as invalid. Imagine you're a new programmer, you are just starting to wrap your head around pointers and you stumble across this article. You see the first example and it looks exactly what you would expect a dereference to look like. But the article claims it's wrong, and now you're confused. So you dig into the article more closely and are exposed to all these terms like UB, alignment, type coercion etc and come away more confused and scared and disinclined to understand pointers. This is classic FUD. This is a technique to manipulate, not educate.

Pointers have pros and cons. UB has pros and cons. Let's try to educate people about them.

Post reply on HN