You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用-fsanitize=address编译时调用虚函数触发崩溃的原因咨询

栈上对象过期导致虚函数调用触发AddressSanitizer错误分析
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" << 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;
}

编译运行结果对比

正常编译运行

使用命令 clang++ Demo.cpp 编译后运行,输出如下:

$./a.out
In B::fun1 function

In B::fun1 function

AddressSanitizer编译运行报错

使用命令 clang++ -fsanitize=address Demo.cpp 编译后运行,触发错误:

In B::fun1 function

32155ERROR: 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/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):

错误原因解释

核心问题:栈对象生命周期过期后的非法引用

在E::fun4()函数中,Bobj是栈上分配的局部对象,它的生命周期仅限于fun4()函数执行期间:

  1. 第一次调用fun4()时,D::Initialize()中initialized初始为false,会调用cobj.Reset(&Bobj),让C::pobj指向这个栈上的Bobj。随后调用cobj.fun2(),通过指针调用B::fun1(),此时Bobj仍有效,输出正常。
  2. fun4()执行完毕后,Bobj被销毁,对应的栈内存被系统回收。
  3. 第二次调用fun4()时,D::Initialize()中initialized已经是true,不会重置pobj,此时pobj仍然指向已经销毁的Bobj内存地址。调用cobj.fun2()时,通过这个无效指针调用虚函数fun1(),属于**栈内存使用后返回(stack-use-after-return)**的未定义行为。

虚函数调用的特殊性

虚函数调用依赖对象内部的虚函数表指针(vptr),该指针指向类的虚函数表(vtable)。当栈对象销毁后,其内存空间可能被后续操作覆盖,虚函数表指针会失效。此时通过无效指针访问虚函数表,就会触发内存访问错误,这也是AddressSanitizer能检测到该问题的原因。

正常编译未报错的原因

未定义行为的结果是不确定的:第一次调用后,栈空间可能还没被其他数据覆盖,虚函数表指针暂时有效,所以能正常输出。但这种情况是偶然的,程序随时可能崩溃或出现异常结果。

相关技术参考

  • C++标准明确规定:引用或指向已销毁对象的指针进行操作属于未定义行为,程序的行为不可预测。
  • AddressSanitizer的stack-use-after-return检测机制,专门用于捕获栈对象生命周期结束后,对其内存的非法访问行为,帮助开发者提前发现这类内存安全问题。

内容的提问来源于stack exchange,提问作者Kiran Pawar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 10:18:08