Live data from Hacker News

Undefined Behavior in C and C++ (2024)

russellw.github.io

191–200 of 234 posts

Re: Undefined Behavior in C and C++ (2024)

#191

Earlier quoted context omitted.

> I have an execution environment, Wasm, where doing this is pretty well defined, in fact. So if I want to read the memory at address 12345, which is within bounds of the linear memory (and there's a builtin to make sure), why should it be undefined behavior? How would you define it? Especially in a way that is consistent with the rest of the language and allows common optimizations (remember that C supports variable…

Just read whatever is at address 12345 of the linear memory. Doesn't matter what that is. If it's an object, if it was malloc'ed, if it's the "C stack", a "global". It's the only way to interpret *(uint64_t*)(12345) when the standard says that a integer-to-pointer conversion is "intended to be consistent with the addressing structure of the execution environment" . There exists an instruction to do that load in Wasm,…

If a newer version of that value is also stored in a register and not yet flushed to memory, should the compiler know to insert that flush for your or is reading a stale value ok?

For what it’s worth there’s a reason you’re supposed to do this kind of access through memcpy, not by dereferencing made up pointers.

> There exists an instruction to do that load in Wasm, there's a builtin to check that 12345 points to addressable memory, the load is valid at the assembly level, the standard says the implementation should define this to be consistent with the addressing structure of the execution environment, why the heck are we playing games and allowing the compiler to say, "nope, that's not valid, so your entire program is invalid, and we can do what ever we want, no diagnostic required"?

Because the language standard is defined to target a virtual machine as output, not any given implementation. That virtual machine is then implemented on various platforms, but the capabilities of the underlying system aren’t directly accessible - they are only there to implement the C virtual machine. That’s why C can target so many different target machines.

Re: Undefined Behavior in C and C++ (2024)

#192

Earlier quoted context omitted.

Pointer provenance was certainly not here in the 80s. That's a more modern creation seeking to extract better performance from some applications at a cost of making others broken/unimplementable. It's not something that exists in the hardware. It's also not a good idea, though trying to steer people away from it proved beyond my politics.

Pointer provenance probably dates back to the 70s, although not under that name. The essential idea of pointer provenance is that it is somehow possible to enumerate all of the uses of a memory location (in a potentially very limited scope). By the time you need to introduce something like "volatile" to indicate to the compiler that there are unknown uses of a variable, you have to concede the point that the compiler…

My grievance isn't with aliasing or dataflow, it's with a pointer provenance model which makes assumptions which are inconsistent with reality, optimises based on it, then justifies the nonsense that results with UB.

When the hardware behaviour and the pointer provenance model disagree, one should change the model, not change the behavior of the program.

Re: Undefined Behavior in C and C++ (2024)

#193

Earlier quoted context omitted.

Pointer provenance was certainly not here in the 80s. That's a more modern creation seeking to extract better performance from some applications at a cost of making others broken/unimplementable. It's not something that exists in the hardware. It's also not a good idea, though trying to steer people away from it proved beyond my politics.

I'm not a compiler writer, but I don't know how you would be able to implement any optimization while allowing arbitrary pointer forging and without whole-program analysis.

It's an interesting question.

Say you're working with assembly as your medium, on a von neumann machine. Writing to parts of the code section is expected behaviour. What can you optimise in such a world? Whatever cannot be observed. Which might mean replacing instructions with sequences of the same length, or it might mean you can't work out anything at all.

C is much more restricted. The "function code" isn't there, forging pointers to the middle of a function is not a thing, nor is writing to one to change the function. Thus the dataflow is much easier, be a little careful with addresses of starts of functions and you're good.

Likewise the stack pointer is hidden - you can't index into the caller's frame - so the compiler is free to choose where to put things. You can't even index into your own frame so any variable whose address is not taken can go into a register with no further thought.

That's the point of higher level languages, broadly. You rule out forms of introspection, which allows more stuff to change.

C++ has taken this too far with the object model in my opinion but the committee disagrees.

Re: Undefined Behavior in C and C++ (2024)

#194

Earlier quoted context omitted.

It still seems like you have invented some notion of "program" that doesn't really exist. Most suspicious is when you say this: > So what I mean is that no file matching has been presented as part of the external file set given to the implementation for processsing. The thing is, there is no "external file set" that includes header files, so this sentence makes no sense. Note that when the preprocessor is run, the on…

Let's drop the word "program" and use something else, like "project", since the word "program" is normative in ISO C. The "project" is all the files going into a program supplied other than by the implementation. C programs can contain #include directives. Those #include directives can be satisfied in one of three ways: they can reference a standard header which is specified by ISO C and hence effectively built into…

> The "project" is all the files going into a program supplied other than by the implementation.

Most of my most recent comment is addressing the possibility that you meant this.

As I said, there is no such concept to the compiler. It isn't passed any list of files that could be included with #includr, only the .c files actually being compiled, and the directories containing includable files.

The fact that your IDE shows project files is an illusion. Any header files shown there are not treated differently by the compiler/preprocessor to any others. They can't be, because it's not told about them!

It's even possible to add header files to your IDE's project that are not in the include path, and then they wouldn't be picked up by #include. That's how irrelevant project files are to #include.

Re: Undefined Behavior in C and C++ (2024)

#195

Earlier quoted context omitted.

Let's drop the word "program" and use something else, like "project", since the word "program" is normative in ISO C. The "project" is all the files going into a program supplied other than by the implementation. C programs can contain #include directives. Those #include directives can be satisfied in one of three ways: they can reference a standard header which is specified by ISO C and hence effectively built into…

> The "project" is all the files going into a program supplied other than by the implementation. Most of my most recent comment is addressing the possibility that you meant this. As I said, there is no such concept to the compiler. It isn't passed any list of files that could be included with #includr, only the .c files actually being compiled, and the directories containing includable files. The fact that your IDE s…

There is no "compiler", "IDE" or "include path" in the wording of the ISO C standard. A set of files is somehow presented to the implementation in a way that is not specified. Needless to say, a file that is included like "globals.h" but is not the base file of a translation unit will not be indicated to the implementation as the base of a translation unit. Nevertheless it has to be somehow present, if it is required.

It doesn't seem as if you're engaging with the standard-based point I've been making, in spite of detailed elaboration.

> Any header files shown there are not treated differently by the compiler/preprocessor to any others.

This is absolutely false. Headers which are part of the implementation, such as standard-defined headers like need not be implemented as files. When the implementation processes #include , it just has to flip an internal switch which makes certain identifiers appear in their respective scopes as required.

For that reason, if an implementation provides , there need not be such a file anywhere in its installation.

Re: Undefined Behavior in C and C++ (2024)

#196
post #59

Earlier quoted context omitted.

Pattern matching should make the language less verbose, not more. (Similar for many of the other things you mentioned.) > When you know what you are doing with C pointers, the compiler just doesn't get in the way. Alas, it doesn't get in the way of you shooting your own foot off, too. Rust allows unsafe and other shenanigans, if you want that.

> Pattern matching should make the language less verbose, not more. In the most basic cases, yes. It can be used as a more polished switch statement. It's the whole paradigm of "define an ad-hoc Enum here and there", encoding rigid semantic assumptions about a function's behaviour with ADTs, and pattern matching for control-flow. This feels like a very academic approach and modifying such code to alter its opinionate…

How is encoding all the assumptions and invariants badly in eg a bunch of booleans and nullable pointers any better?

Re: Undefined Behavior in C and C++ (2024)

#197
post #55

Earlier quoted context omitted.

I don't think C nor C++ were ever great languages for prototyping? (And definitely not better than Rust.)

Please try not to be obnoxious and turn this into a language war.

How is this obnoxious?

C and C++ have their strengths, but rapid prototyping is generally not seen to be amongst them.

This shouldn't be any more controversial than saying that pure Python is generally slow.

Re: Undefined Behavior in C and C++ (2024)

#198
post #46

Earlier quoted context omitted.

Well, you could also treat Fil-C as a sanitiser, like memory-san or ub-san: Run your test suite and some other workloads under Fil-C for a while, fix any problems report, and if it doesn't report any problems after a while, compile the whole thing with GCC afterwards for your release version.

Right. And of course there are still less-performance-sensitive C/C++ applications (curl, postfix, git, etc.) that could have memory-safe release versions. But the point is also to dispel the conventional wisdom that C/C++ is necessarily intrinsically unsafe. It's a tradeoff between safety, performance and flexibility/compatibility. And you don't necessarily need to jump to a completely different language to get a di…

Even with UB holes plugged, C (and C++) are still unsafe, because there are many assumptions you might want to make that you can not encode in the language.

To get an example that's easy to understand: before the introduction of the 'const' keyword, you just couldn't express that some variable should never be changed. And no amount of UB sanitisers would have fixed this for you: you just couldn't express the concept. There's lots of other areas of these languages that are still in a similar state.

Eg there's no way to express that a function should be pure, ie not have side effects (but is allowed to use mutation internally).

Re: Undefined Behavior in C and C++ (2024)

#199
post #198

Earlier quoted context omitted.

Right. And of course there are still less-performance-sensitive C/C++ applications (curl, postfix, git, etc.) that could have memory-safe release versions. But the point is also to dispel the conventional wisdom that C/C++ is necessarily intrinsically unsafe. It's a tradeoff between safety, performance and flexibility/compatibility. And you don't necessarily need to jump to a completely different language to get a di…

Even with UB holes plugged, C (and C++) are still unsafe, because there are many assumptions you might want to make that you can not encode in the language. To get an example that's easy to understand: before the introduction of the 'const' keyword, you just couldn't express that some variable should never be changed. And no amount of UB sanitisers would have fixed this for you: you just couldn't express the concept.…

Yeah, but C++ now supports "user-defined" annotations which effectively allow you to add the equivalent of any keyword you need, right? (Even if it's not the prettiest syntax.) For example, the scpptool static analyzer supports (and enforces) lifetime annotations with similar meaning to Rust's lifetime annotations.

I believe gcc actually does support `__attribute__ ((pure))` to indicate function purity. (I assume it doesn't actually enforce it, but presumably it theoretically could at some point.)

Re: Undefined Behavior in C and C++ (2024)

#200

Earlier quoted context omitted.

Pointer provenance probably dates back to the 70s, although not under that name. The essential idea of pointer provenance is that it is somehow possible to enumerate all of the uses of a memory location (in a potentially very limited scope). By the time you need to introduce something like "volatile" to indicate to the compiler that there are unknown uses of a variable, you have to concede the point that the compiler…

My grievance isn't with aliasing or dataflow, it's with a pointer provenance model which makes assumptions which are inconsistent with reality, optimises based on it, then justifies the nonsense that results with UB. When the hardware behaviour and the pointer provenance model disagree, one should change the model, not change the behavior of the program.

Give me an example of a program that violates pointer provenance (and only pointer provenance) that you think should be allowed under a reasonable programming model.
Post reply on HN