C++ Logo

std-proposals

Advanced search

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

From: J Decker <d3ck0r_at_[hidden]>
Date: Mon, 4 Aug 2025 22:29:35 -0700
On Fri, Jul 25, 2025 at 4:17 AM 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.
>

(just giving the dead horse one final kick :) probably not )

Generally this seems like it should be controlled by a compile flag like
_DEBUG, and/or implemented by an external tool like valgrind.

For C, my allocator (
https://github.com/d3x0r/SACK/blob/master/src/memlib/sharemem.c all C is
also C++ in this project), in debug mode, with a soft debug-memory flag set
(which can be turned off at runtime, but not back-on but anyway), when
allocating a slab of memory it fills it with a certain constant which is
register sized (64/32 as appropriate), then blocks that are Allocate()d are
filled to be not zero, with a different constant; giving uninitialized
members of structs a certain value, and blocks that are Deallocate()d are
filled with a value; but the macros that implement the calls to Ex versions
of the functions pass the current file and line as members, which is
available to C because of how it has to be done. The file and line of the
last allocator/deallocator is stored, then blocks that get deallocated too
soon can be checked for who deallocated it. Since blocks that are free
have a certain structure, when blocks are allocated/released, a function to
check the chain of memory blocks is run 1) to check headers of the blocks
to make sure the chain is still good - the blocks are also over-allocated
and an end-of-block signature is used, which can be checked for block
overruns, 2) check data in deallocated blocks to see that it is the
signature - use after delete/free/Deallocate... the chain can also be
dumped to a string/file for debugging, and checking for memory leaks like
if you have a lot of a block of a certain size, you can check who allocated
it... There are a whole set of DBG_FILELINE sort of macros that could be
DBG_RELAY (pass a pFile,nLine passed to this function) which can be used to
forward such information like who is actually responsible - instead of
blaming the factory, blame who is using the factory (
https://github.com/d3x0r/SACK/blob/master/include/sack_types.h#L689-L764).

But; I found when providing overrides for new,new[], delete, delete[] etc
there's no way to get file and line information, so a lot of functionality
wasn't very useful, and then different compilers had different requirements
for how new and delete had to be overridden, and it was sort of hard to
maintain; so I ended up just using the C interface, or ended up just
relying on the default allocators, since other objects I'd use from other
libraries would already be expecting some other allocator.

Having a method to provide a better abstract allocator for C++ to use would
be a nice thing to have.

Visual Studio debugger has the ability to breakpoint on reading
uninitialized variables - which are initialized in debug mode with
0xCCCCCCCC; so certainly there's hardware support already for reading a
certain value into a register; so there's already some hardware support for
such things, unlike what others would lead you to believe (which I find it
hard to imagine they never ran into that feature themselves).

J



> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2025-08-05 05:29:55