Search code examples
c++castingvirtual-destructor

c++ type casting to gain access


I was writing a class (destructor is virtual) which objects would only be deleted using a recycler class's object. But I discovered that any one can delete that class's object without having knowledge of my recycler class.

Please consider the following example:

class A
{
public:
    A() {qDebug() << "cTor: A";}

protected:
    virtual ~A() {qDebug() << "dTor: A";}
};

class B
{
public:
    virtual ~B() {cout << "dTor: B";}

protected:
    B() {cout << "cTor: B";}
};

A *a = new A;
delete (B*)a; // How is this possible !!??

output:

cTor: A
dTor: A

If possible what can I do to prevent this without making final subclass of A's destructor non-virtual?


Solution

  • For the case you mentioned above you just got lucky that the address of B's destructor is the same of A's destructor in terms of offset from the pointer.

    The virtual destructor is just another entry in the vftable of the object. You can call any destructor (no matter how private he is) if you just access the memory address .

    Here's a code sample that show how these things work out :

    class A
    {
    public:
        virtual void foo() {printf("foo !");};
    };
    class B
    {
    public:
        virtual void bar() {printf("bar !");};
    }; 
    ...
    A* a = new A();
    ((B*)a)->bar();
    

    Apparently I can call A foo by calling B->bar and B doesn't even have a A !!!! Magic ? no. It just happened that B->bar has the same address as A->foo . Considering your example , the destructor of A has the same address as the destructor of B.

    Regarding your comment if this is safe from a library development PoV :

    If a developer doesn't know the correct way to cast it's not your problem, or if a developer really wants to break your library, he will. Your job is to provide working code when things are used properly. You cannot make anything if the users of the library want to reinterpret cast objects from your library or use other non-standard, undefined behaviors (it's not your fault that some random dude wants to allocate objects using malloc for example... have fun dealing with that dude !).