Search code examples
c++clangclang++virtual-functionsaddress-sanitizer

Crashing because of calling virtual function when i compile with -fsanitize=address flag


using namespace std;
class A {
    public:
        virtual void fun1()=0;
};
class B : public A {
    public:
        virtual void fun1();
};
void B::fun1() {
    cout << " In B::fun1 function\n" << endl; 
}
class C {
    public:
        A *pobj;
        void Reset(A *pobj);
        void fun2();
};
void C::fun2() {
    pobj->fun1();
}
void C::Reset(A* p_obj) {
     pobj = p_obj;
}
class D {
    public:
        C cobj;
        bool initialized;
        void Initialize(A *pAobj);
};
void D::Initialize(A *pAobj) {      // casting
    if(initialized == false) {
        initialized = true;
        cobj.Reset(pAobj);
    }
    cobj.fun2();
}

class E {
    public:
        E();
        ~E();
        void fun4();
        void fun5();
        D *p_Dobj;
};
E::E()
    : p_Dobj(new D){
}
E::~E() {
    if(p_Dobj != NULL) {
        delete p_Dobj;
    }
}
void E::fun4() {
    B Bobj;    // Created Local Object which may causing issue.
    p_Dobj->Initialize(&Bobj);
}
void E::fun5() {
    fun4();
    fun4();
}
int main() {
    E Eobj;
    Eobj.fun5();
    return 0;
}

When i am compiling the above code like,

$clang++ Demo.cpp

then is is properly working,

$./a.out

Output:

In B::fun1 function

In B::fun1 function

but when i compile with -fsanitize=address flag like,

$clang++ -fsanitize=address Demo.cpp

Output:

> In B::fun1 function
> 
> 
>     ==32155==ERROR: AddressSanitizer: stack-use-after-return on address 0x7f49b5900060 at pc    0x565479ba8963 bp 0x7fff9ca67730 sp 0x7fff9ca67728
> READ of size 8 at 0x7f49b5900060 thread T0
>     #0 0x565479ba8962 (/home/excellarate/Desktop/All Tasks/Wasm/a.out+0xf4962)
>     #1 0x565479ba8aae (/home/excellarate/Desktop/All Tasks/Wasm/a.out+0xf4aae)
>     #2 0x565479ba8cab (/home/excellarate/Desktop/All Tasks/Wasm/a.out+0xf4cab)
>     #3 0x565479ba8d21 (/home/excellarate/Desktop/All Tasks/Wasm/a.out+0xf4d21)
>     #4 0x565479ba8e04 (/home/excellarate/Desktop/All Tasks/Wasm/a.out+0xf4e04)
>     #5 0x7f49b7f8f082 (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)
>     #6 0x565479ad337d (/home/excellarate/Desktop/All Tasks/Wasm/a.out+0x1f37d)
> 
> Address 0x7f49b5900060 is located in stack of thread T0 at offset 32 in frame
>     #0 0x565479ba8bbf (/home/excellarate/Desktop/All Tasks/Wasm/a.out+0xf4bbf)
> 
> This frame has 1 object(s):
> [32, 40) 'Bobj' (line 68) <== Memory access at offset 32 is inside this variable
> HINT: this may be a false positive if your program uses some custom stack unwind mechanism,   swapcontext or vfork
>     (longjmp and C++ exceptions are supported)
> SUMMARY: AddressSanitizer: stack-use-after-return (/home/excellarate/Desktop/All   Tasks/Wasm/a.out+0xf4962)
> Shadow bytes around the buggy address:
> 0x7f49b58ffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x7f49b58ffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x7f49b58ffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x7f49b58fff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x7f49b58fff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>     =>0x7f49b5900000: f1 f1 f1 f1 00 f3 f3 f3 f5 f5 f5 f5[f5]f5 f5 f5
> 0x7f49b5900080: f1 f1 f1 f1 00 f3 f3 f3 00 00 00 00 00 00 00 00
> 0x7f49b5900100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x7f49b5900180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x7f49b5900200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 0x7f49b5900280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> Shadow byte legend (one shadow byte represents 8 application bytes):

when it is calling virtual function (fun1) then there is crash occur can you explain why it is happening? Share documentation related to this issue if possible


Solution

  • I don't think this is really anything to do with virtual functions. It's a simple lifetime issue and your own comment points it out. In the first call to fun4(), you call p_Dobj->Initialize with a pointer to the local object Bobj, which results in p_Dobj->cobj.pobj pointing to this object, and p_Dobj->initialized set to true. The lifetime of this object ends when fun4() returns. When you call fun4() for the second time, p_Dobj->initialized is true so p_Dobj->cobj.pobj is left alone, and it is dereferenced in the call to p_Dobj->cobj.fun2(). So you are dereferencing a pointer to an object whose lifetime has ended, and the behavior is undefined.

    Bonus bug: in D::Initialize, the member initialized is, ironically, read without being initialized.