Bug 95321 - Run-time crash on valid destructor code
Summary: Run-time crash on valid destructor code
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 9.1.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-05-25 17:15 UTC by Francois-Xavier Coudert
Modified: 2020-05-25 18:11 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-05-25 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Francois-Xavier Coudert 2020-05-25 17:15:01 UTC
// Run-time crash with g++ >= 9 and -Ox, x > 1
// Works perfectly with g++ <= 8.3
class IDestroyable
{
protected:
    virtual void  Destroy() = 0;

public:
    void operator delete (void * ptr)
    {
        if (ptr != nullptr) static_cast<IDestroyable *>(ptr)->Destroy();
    }
};

class IToto
{
public:
    virtual void  Toto() = 0;
};

class ITotoDestroyable : public IToto, public IDestroyable
{
public:
    void operator delete (void * ptr)
    {
        if (ptr != nullptr) delete static_cast<IDestroyable *>(static_cast<ITotoDestroyable *>(ptr));
    }
};

template <typename INTERFACE>
class DestroyableBase : public INTERFACE
{
protected:
    virtual void  Destroy()
    {
        ::delete this;
    }

public:
    virtual ~DestroyableBase()
    {
    }

    void operator delete (void * ptr)
    {
        ::operator delete (ptr);
    }
};

#include <iostream>

class TotoDestroyable : public DestroyableBase<ITotoDestroyable>
{
public:
    ~TotoDestroyable()
    {
        std::cout << "OK Destroyed !\n";
    }

    void  Toto()
    {
        std::cout << "Toto !\n";
    }
};

int main()
{
    ITotoDestroyable * foo = new TotoDestroyable();
    // Uncomment to workaround the crash
    // foo->Toto();
    delete foo;
}



Segfaults at runtime when compiled with G++ >= 9 and -O1 or higher.

Process 52619 launched: '/tmp/a.out' (x86_64)
Process 52619 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x0000000100000d44 a.out`main [inlined] IDestroyable::operator delete(ptr=0x0000000100604858) at a.cpp:11:70
   8   	public:
   9   	    void operator delete (void * ptr)
   10  	    {
-> 11  	        if (ptr != nullptr) static_cast<IDestroyable *>(ptr)->Destroy();
   12  	    }
   13  	};
   14
Comment 1 Andrew Pinski 2020-05-25 17:26:00 UTC
I don't think this is valid:
BY the time operator delete gets called the deconstructor is called and therefor the object no longer exists therefor:
    void operator delete (void * ptr)
    {
        if (ptr != nullptr) static_cast<IDestroyable *>(ptr)->Destroy();
    }

Is invalid code as the vtable in ptr is no longer valid.

Where is this code from?
Comment 2 Andrew Pinski 2020-05-25 17:31:50 UTC
(In reply to Andrew Pinski from comment #1)
> I don't think this is valid:
> BY the time operator delete gets called the deconstructor is called and
> therefor the object no longer exists therefor:
>     void operator delete (void * ptr)
>     {
>         if (ptr != nullptr) static_cast<IDestroyable *>(ptr)->Destroy();
>     }
> 
> Is invalid code as the vtable in ptr is no longer valid.
s/invalid/undefined/
Comment 3 Francois-Xavier Coudert 2020-05-25 18:11:51 UTC
I don't have certainty it's not undefined behaviour, sorry. Closing.