C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Standardising 0xdeadbeef for pointers

From: Julien Villemure-Fréchette <julien.villemure_at_[hidden]>
Date: Fri, 25 Jul 2025 11:55:59 -0400
Assigning a pointer data member in the destructor of the object containing the pointer data member is pointless and it won't help in any way preventing double delete of the previous pointee. After the destructor of the object "o" returns, any access to the members of "o" (both functions and data) are illegal: you can't name "o.p" or call any function that would try to access "o.p", so the expression "delete o.p" is invalid even before the double delete, it could even happen that some other unrelated object gets stored there and overwrites the "0xdeadbeef" value to something else. Furthermore, assigning to a pointer only modifies this pointer instance, not the pointed to object and certainly not other pointer instances which happen to point to the same location.

In short, writing to an object which is about to go out of scope is pointless: nothing can observe that modification.

Double delete happens when two distinct pointer objects aliases to the same object. If both p1 and p2 point to the same dynamically allocated object, even if you try to write a null pointer value after deleting through p1, the p2 pointer still point to the now deleted object: then "delete p2" yields a double delete.

To prevent double deletes, refrain from using dynamic allocation and create objects directly with value or move semantics, otherwise use unique_ptr for ownership of polymorphic types. Otherwise, use raw pointer to express "access to" an object from an outer scope without owning it (raw pointers should never be deleted if used for this purpose, see CppCoreGuidelines).



On July 25, 2025 7:17:32 a.m. EDT, Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]> wrote:
>I'm writing some code at the moment, and the destructor looks like this:
>
> ~Governor(void) noexcept
> {
> try
> {
> delete this->p;
> }
> catch(...){}
>
> this->p = (void*)0xdeadbeef;
> }
>
>I've set the pointer to 0xdeadbeef because I want to make sure that
>there's nowhere else in the code that this particular object gets
>deleted. If another part of the code goes to delete the object a
>second time, it will segfault on 0xdeadbeef -- which is exactly what I
>want to happen because I want the debugger to catch it.
>
>If alternatively I were to do:
>
> this->p = nullptr;
>
>then a double-delete won't be caught by the compiler because "delete
>nullptr" is harmless.
>
>We already have "nullptr_t", but what if we also had "badptr_t" and
>the constant expression "badptr"?
>
>So my destructor would become:
>
> ~Governor(void) noexcept
> {
> try
> {
> delete this->p;
> }
> catch(...){}
>
> this->p = badptr;
> }
>
>"badptr" coould be a pointer with all bits set to 1 (instead of
>0xdeadbeef). I think it should be more elaborate than this though, and
>I'll explain why. I remember I was assigned a bug at work one time, a
>program was segfaulting because the memory address 0xFFFFFFFE was
>being accessed inside "libc". It turns out that the code did this:
>
> char *p = std::strchr( str, 'Z' ) - 2;
> strcpy( str2, p );
>
>The call to 'strchr' was failing to find the letter 'Z', and so it
>returned a nullptr. But then the nullptr got 2 subtracted from it,
>giving 0xFFFFFFFE. So then the implementation of "std::strcpy" inside
>"libc" was segfaulting on accessing memory address 0xFFFFFFFE.
>
>This is the reason why I think "nullptr" and "badptr" should be far
>away from each other -- it's not good enough to be able to
>increment/decrement one of them to get the other.
>
>And I suppose I might be running the risk in my own program running on
>x86_64 that 0xdeadbeef could be a valid address. Maybe make sure the
>top bit is high: 0xdeadbeefdeadbeef.
>--
>Std-Proposals mailing list
>Std-Proposals_at_[hidden]
>https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2025-07-25 15:56:20