C++ Logo

std-proposals

Advanced search

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

From: Tiago Freire <tmiguelf_at_[hidden]>
Date: Sat, 26 Jul 2025 13:38:19 +0000
This was your stated use case:

~Governor(void) noexcept
        {
            try
            {
                delete this->p;
            }
            catch(...){}

            this->p = (void*)0xdeadbeef;
        }

> If another part of the code goes to delete the object a second time, it will segfault on 0xdeadbeef

No!
That! is a destructor! "p" is a member of this object.

By the time it returns the objects lifetime is over. Whatever expectations you now have for that memory is now UB.
Accessing it in the first place is the problem, the value you will find there is irrelevant.

If the access was valid null would already be a perfect indication that the pointer doesn't point to anything, no other value required.

This is an exercise in pure nonsense, as usual.




________________________________
From: Std-Proposals <std-proposals-bounces_at_[hidden]> on behalf of Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]>
Sent: Saturday, July 26, 2025 12:53:30 PM
To: std-proposals <std-proposals_at_[hidden]>
Cc: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Subject: Re: [std-proposals] Standardising 0xdeadbeef for pointers



On Friday, July 25, 2025, Tiago Freire wrote:

I don't know about you, but intentionally segfaulting is not something I want in my application.


I want the Release build to run perfectly and to get a glowing review, without any segfaults.

However, as a step toward achieving this, I want the Debug build to halt and clearly explain to me what went wrong. I have coined this "The Halting Solution".

I'll give you an example. I wrote a free open-source program called Dynamo and released the first version about 17 years ago. It pretty much ran perfectly, but I noticed sometimes that the network MAC address in the list view on screen would sometimes have garbage characters. This was before C++ 11, and so we used to extend the lifetime of a return value as follows:

    MyClass const &obj = FuncReturnsByValue();

Nowadays with C++ 11, we just write:

    MyClass obj = FuncReturnsByValue();

Well, it turned out that FuncReturnsByValue only returned by value under MS-Windows, and it actually returned a const reference under Linux and Apple -- I remember enlisting the guys on comp.lang.c++ to help me get to the bottom of it, and they pointed me to this excerpt from a 3rd party header file:

    #ifdef BOOST_WINDOWS_API
        const std::string string() const { return string(codecvt()); }
    #else // BOOST_POSIX_API
        // string_type is std::string, so there is no conversion
    const std::string& string() const { return m_pathname; }

So when I took that return value and bound it to an Lvalue const reference, its lifetime got extended on MS-Windows, but not on Linux and Apple. On Linux and Apple, the referred object sometimes got destroyed before the GUI thread displayed it on screen, giving me garbage characters.

I would have preferred if the Debug build halted immediately. I think the GNU Address Sanitizer nowadays would catch the above bug.

I want my Release build to hobble on even when something goes wrong, and I even catch and discard exceptions in some destructors to achieve this in the Release build (i.e. I don't want std::terminate getting called). I've edited Arthur O'Dwyer's Auto macro to catch exceptions in the destructor if NDEBUG is defined, like this:

https://github.com/healytpk/paperkernelcxx/blob/main/main_program/Auto.h

It's very rare that I call std::abort in any program . . . and actually I'll call std::_Exit instead if I think it's time to die as it's the closest thing to:

    mov rax, 60
    mov rdi, 0
    syscall

So 0xdeadbeef probably doesn't have any place in a Release build, but it certainly helps when debugging.

Since all bits zero is already being used for nullptr, maybe all bits one would be good for badptr. I know Oliver linked us to a page that had computers with not-all-bits-zero nullptr's but they were all dinosaurs. The reason why it makes perfect sense for nullptr to be all-bits-zero is that every CPU instruction set has optimised instructions to check if a register's value is zero. The opcode + operand to compare a register value with 0xFFFFFFFFFFFFFFFF is a few more bytes. Plus the 'jump' instructions have optimised forms such as 'jump if zero' and 'jump if non-zero' which are perfect for branching on nullptr.

We've already mandated that the number system must be Two's. We're considering mandating that CHAR_BIT must be 8, so maybe also mandate that nullptr is all bits zero -- which opens the door to make badptr all bits one.

If the compiler and debugger notice the accessing of memory address 0xFFFFFFFFFFFFFFFF, then maybe they can suspend the process and tell the user:

    "Badptr dereferenced in __FUNCTION__, possibly after having been set to badptr in __OTHER_FUNCTION__"




Received on 2025-07-26 13:38:24