Live data from Hacker News

Defer: Resource cleanup in C with GCCs magic

oshub.org

91–99 of 99 posts

Re: Defer: Resource cleanup in C with GCCs magic

#91
post #90

Earlier quoted context omitted.

I agree with everything you've said, except the conclusion: C can't add proper safe RAII, but being "proper and safe" is not a threshold C even tries to uphold.

But would a destructor that runs automagically when a value goes out of scope, even if it's not properly initialized (and with zero regards for copies or moves) be in any way better than a defer that's explicitly called after it is initialized? Cause, like, that's the entire thread.

Nope, it wouldn't be. I'm just pointing out that it being unsafe or hard to use is no barrier at all to it being in the language.

Re: Defer: Resource cleanup in C with GCCs magic

#92
post #75
post #70

Earlier quoted context omitted.

> it means one simple thing: «give me the contents of a memory cell (a byte, a word, a long word etc) at the address of 0» Well… sometimes. If you set a pointer to literal 0, you do not actually make that pointer point to address zero, from the C language's point of view. No, you are then setting it to be the null pointer. (c99 6.3.2.3 paragraph 3) Now, what is the bit value of a null pointer? That's undefined. So ho…

You are conflating tha language capability with the hardware capability. C/C++ do not place restrictions on dereferencing the 0th address. Consider the following stub: /* -O2 -std=c23 -Wall -fno-inline-functions */ int *ptr0 = 0; int *ptr0p = (int *)0; int main () { return *ptr0 | *ptr0p; } Head over to godbolt, compile it, and check the code. Zero compilation warnings, and the compiler duly obliges to generate the c…

> C/C++ do not place restrictions on dereferencing the 0th address.

It doesn't. Because it doesn't consider 0th address special. Null pointer though, is special. And it's not necessarily address zero.

> Head over to godbolt, compile it, and check the code.

I know what it compiles to on godbolt supported architectures. This is completely irrelevant.

Your code example, and its reasoning, contain so many errors that it's hard to know where to start.

You seem to insist that null pointer points to address zero. And you can try every architecture on godbolt, and it very likely does. But now you're just describing what happens when you do it, not the language and what can happen in the future.

Read this about how strange literal 0 and NULL is: https://c-faq.com/null/ptrtest.html

> So if you run that code on a system before the MMU is activated or on a system without a MMU, «main» will return 0 on all systems

"All systems"? What does that even mean? If you compile and run this on DOS you read from DS:0000 (or is it ES:0000?), which is not physical address zero, even though the MMU is not activated.

If you're using the Large or Huge memory model (https://en.wikipedia.org/wiki/X86_memory_models), then you may think that you'll read absolute value zero. Maybe in practice you do. But there's not necessarily a zero there. It'll be the first entry of the IVT.

But since you created null pointers (not "pointers to address zero"), and deferencing null pointers is UB, anything can happen. Including the compiler removing the code or just hard coding setting the result to zero.

But yeah, I'm not at all surprised that your misguided test gave you zero.

> Zero compilation warnings,

What's that supposed to signify? UB doesn't in any way what so ever imply warnings. -Wall doesn't even turn on all warnings anyway.

You can take ANY UB and "prove to me" what will happen by showing it on godbolt. But that just means you don't understand what UB even means.

> a read from 0 will give you 0 if the first memory page is filled with 0s.

This is also "not even wrong".

> You are most assuredly conflating a pointer to 0 dereferencing with the memory protection/virtual memory management system

Not even remotely.

> MMU is not magic.

You cannot think that I meant magic. Do you not understand what I meant? I meant you don't need an MMU to experience something other than plain naive address space mapped to physical RAM.

I've written a toy OS kernel with virtual memory, I know exactly what an MMU does.

> Respectfully, so far I am yet to see a single compelling argument or tangible piece of evidence to support the claims you have espoused.

So, everything you have said is wrong. A null pointer is not defined to point to address zero (see link above). So your code is nonsense. How am I supposed to provide some counter to that? Aside from where I have already said this, and it's plainly written in the C standard. (or, as it were, left undefined and thoroughly documented internet wide)

Dereferencing a null pointer is invalid. The spec says so:

> Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer[…]

c99 6.5.3.3 footnote 83.

You cannot dereference a null pointer according to the C language. And setting a pointer to literal 0 does not set any defined value of the pointer, other than setting it to the C "null pointer". Which could be any value. (again, see link above)

I "claim" that godbolt showing current compilers on some architectures do something specific is completely irrelevant to a discussion on UB. It could do the "common sense thing" (and almost always does), but it can do anything. You should read up on https://en.wikipedia.org/wiki/Undefined_behavior

When you say that you have not seen a single compelling argument, I think you're being hyperbolic. You said a system without an MMU will allow dereferencing a pointer to address zero (ignoring for now what that even is, in C). I replied that it's perfectly valid to have reads to address zero reset the machine.

What is your claim, that "no, the C standard does not allow resetting the machine when you read from address zero"? Even if a pointer to address zero and null pointer are the same (not defined by C), then per reference above it's invalid to dereference it.

> The only exception that does not initialiase memory with zeroes that I am aware of is AIX

What is this in regards to? It's seems extremely random. You are enumerating architectures whose bootup state have a certain initialized memory layout?

Why are you doing this? What point are you trying to prove?

I mean, it's also wrong. As we know from cold boot attacks, memory isn't just zeroed.

And when an x86 CPU starts up in real mode, address 0 has the interrupt vector table at address zero. So that's where interrupt 0 (divide error) handler far pointer goes. "Initialized to zero" doesn't make any sense. Entire books have been written about the x86 bootup process. Probably at some stages during an x86 boot address zero (virtual and/or physical) contain zero values, but it's not the long term contents.

Is an architecture that maps an I/O device for a temperature sensor at address zero an invalid architecture? Is it fundamentally incompatible with C? Of course not. Modern unix provides a user space where reading at address zero causes SEGV. But that's just one nuanced environment out of many.

> Again, embedded may have a nuance (subject to a specific hardware implementation) as it is a commonplace in embedded systems to not have a contiguous memory space and have holes in it

Vanilla unix user space is also noncontiguous with holes in it. Including usually (nowadays) a hole at address zero.

This is all completely unrelated to C language null pointers. Completely. You say I'm conflating things, but you're conflating null pointers with pointers to address zero. And with if address zero is initialized. And with MMU. None of which are relevant to any of the other things.

> Again, embedded may have a nuance

Everything has nuance. That's the entire point of the C standard. If you code to the C standard abstract machine, the compiler promises a certain observed behavior when run on the target. If you go beyond the standard (such as assuming nullptr points to address zero), then you are making assumptions about the nuances of the specific machine AND the compiler.

Linux has nuance. OpenBSD has nuance. Every embedded platform definitely has nuance. GCC has nuance (e.g. you can disable null check elision optimization. Is that true of MSVC? (I haven't used MSVC since last millenium).

But sure, if you say "my software only supports architectures where nullptr is zero, and built on a compiler that doesn't use this optimization", then that's your choice. It may be hard to enforce that over the lifetime of the program though, so maybe best to just write C code without UB. (which, as any expert will tell you, is nontrivial) Extra hard as future compilers or compiler versions introduce new ways of turning UB into "surprises".

Anyway, I'm done. Like I said, your theories about null pointers, UB, and architectures are intertwined in a web of misunderstanding that would take more than a HN thread to untangle.

Re: Defer: Resource cleanup in C with GCCs magic

#93
post #32

Earlier quoted context omitted.

> on top of C. If we're referring to the "C is a subset of C++" / "C++ is a superset of C" idea, then this just hasn't been the case for some time now, and the two continue to diverge. It came up recently, so I'll link to a previous comment on it ( https://news.ycombinator.com/item?id=45268696 ). I did reply to that with a few of the other current/future ways C is proposing/going to diverge even further from C++, sin…

> personally longing for the day C gets proper namespaces I think in the spirit of C, this should go into the linker, not in the compiler.

Or you can use objcopy --prefix-symbols=Namespace .

Re: Defer: Resource cleanup in C with GCCs magic

#94
post #90

Earlier quoted context omitted.

But would a destructor that runs automagically when a value goes out of scope, even if it's not properly initialized (and with zero regards for copies or moves) be in any way better than a defer that's explicitly called after it is initialized? Cause, like, that's the entire thread.

Nope, it wouldn't be. I'm just pointing out that it being unsafe or hard to use is no barrier at all to it being in the language.

Hell, since RAII is an opt-in type-level infectious (in the sense that if you have an RAII member in a struct the struct is RAII) mechanism you can require that RAII structs be initialised.

Given the compiler would already need to know that a local is of an RAII type in order to insert the drop, move, and copy glues it’s not exactly a big requirement.

Re: Defer: Resource cleanup in C with GCCs magic

#95

Earlier quoted context omitted.

Yes, it can. Why would you be checking the pointer for nullptr after you have dereferenced it? It makes no sense at all, so, compiler indeed can elide the nullptr check before dereferencing the ptr exactly because it is free to _always_ assume that the program is free of UB. To be more precise GCC says "eliminate useless checks for null pointers" and what I am saying that you can never be sure what in your code ended…

> Yes, it can. I don't think so. If it could, then this code would reliably crash: char *mystr = strdup (oldstr); if (mystr) *mystr = 0; // Truncate string That never crashes.

https://godbolt.org/z/aPcr1bfPe

Re: Defer: Resource cleanup in C with GCCs magic

#96
post #69

Earlier quoted context omitted.

I think where you are talking past each other is, that one is talking about temporal after and the other about causal after. The null check can be eliminated if the dereference happens temporally after, but causally before: if (ptr == NULL) { ... } ... int foo = ptr->some_field;

Ah yes, unless that conditional returns, UB indeed has time travelling properties. (I mean, spec wise. Manifesting as the compiler "reasoning" that "well I could legally put that load before the check, and that means it's not null, which means I don't need the check") But this brings us back to the article: Why does the author say that there's no way to check for NULL in the free function? Maybe they are hinting at s…

I found the old discussion here on HackerNews although I haven't seen it before. I only found it now because I wanted to double-check my hypothesis and see if my understanding is really off. Seems like not: https://news.ycombinator.com/item?id=42387013

https://godbolt.org/z/aPcr1bfPe

Re: Defer: Resource cleanup in C with GCCs magic

#97
post #75
post #70

Earlier quoted context omitted.

> it means one simple thing: «give me the contents of a memory cell (a byte, a word, a long word etc) at the address of 0» Well… sometimes. If you set a pointer to literal 0, you do not actually make that pointer point to address zero, from the C language's point of view. No, you are then setting it to be the null pointer. (c99 6.3.2.3 paragraph 3) Now, what is the bit value of a null pointer? That's undefined. So ho…

You are conflating tha language capability with the hardware capability. C/C++ do not place restrictions on dereferencing the 0th address. Consider the following stub: /* -O2 -std=c23 -Wall -fno-inline-functions */ int *ptr0 = 0; int *ptr0p = (int *)0; int main () { return *ptr0 | *ptr0p; } Head over to godbolt, compile it, and check the code. Zero compilation warnings, and the compiler duly obliges to generate the c…

Oh, it's too late to edit my comment, but one more thing:

> You are conflating tha language capability with the hardware capability

No. I'm talking about the C language and the abstract machine that it defines. I'm very much NOT talking about specific hardware, unlike when you bring up MMU and other irrelevant specific hardware capability.

Because specific hardware capability is not part of the language.

Re: Defer: Resource cleanup in C with GCCs magic

#98
post #69

Earlier quoted context omitted.

Ah yes, unless that conditional returns, UB indeed has time travelling properties. (I mean, spec wise. Manifesting as the compiler "reasoning" that "well I could legally put that load before the check, and that means it's not null, which means I don't need the check") But this brings us back to the article: Why does the author say that there's no way to check for NULL in the free function? Maybe they are hinting at s…

I found the old discussion here on HackerNews although I haven't seen it before. I only found it now because I wanted to double-check my hypothesis and see if my understanding is really off. Seems like not: https://news.ycombinator.com/item?id=42387013 https://godbolt.org/z/aPcr1bfPe

That godbolt illustrates what I've been saying, yes. The godbolt example is exactly what I meant when I said "If you check after dereferencing it, yes it can [remove the check]".

In fact we can make the example even shorter: https://godbolt.org/z/8fv4GKMse

And here's one with a "time travelling" removal of a branch because of UB: https://godbolt.org/z/PjYzqKxs4

Remove the memcpy, and it adds the comparison back in: https://godbolt.org/z/r1GTvGnnf

This one is fun too: Explicitly crash on null pointer: https://godbolt.org/z/sGzhK7zM4

As this comment (https://news.ycombinator.com/item?id=45442103) says, it looks like we may have just been talking past each other.

When I said "If you check for null pointer before you dereference, then no the compiler cannot elide the check." I meant like:

    if (ptr != NULL) { *ptr = 123; }
Which is obviously safe and the compiler cannot remove the check. Not:

    if (ptr != NULL) { [… something else …] } *ptr = 123;
The latter is UB, and UB is allowed to time travel.

Re: Defer: Resource cleanup in C with GCCs magic

#99
post #75
post #70

Earlier quoted context omitted.

> it means one simple thing: «give me the contents of a memory cell (a byte, a word, a long word etc) at the address of 0» Well… sometimes. If you set a pointer to literal 0, you do not actually make that pointer point to address zero, from the C language's point of view. No, you are then setting it to be the null pointer. (c99 6.3.2.3 paragraph 3) Now, what is the bit value of a null pointer? That's undefined. So ho…

You are conflating tha language capability with the hardware capability. C/C++ do not place restrictions on dereferencing the 0th address. Consider the following stub: /* -O2 -std=c23 -Wall -fno-inline-functions */ int *ptr0 = 0; int *ptr0p = (int *)0; int main () { return *ptr0 | *ptr0p; } Head over to godbolt, compile it, and check the code. Zero compilation warnings, and the compiler duly obliges to generate the c…

One last thing. If you think reading and writing to "address zero" is perfectly fine in C, then I'm curious what your explanation is of GCC removing this null pointer check: https://godbolt.org/z/PjYzqKxs4

It's not a GCC bug.

Post reply on HN