为何ASAN未检测到stack-use-after-scope问题?附C++代码疑问
关于Address Sanitizer的stack-use-after-scope问题疑问
第一段代码及理解疑问
我无法理解Address Sanitizer的stack-use-after-scope概念,以下是我的代码:
#include <iostream> using namespace std; class User { public: User(){ _name = "No Name"; } User(string name){ _name = name; _salary = 0; } string getName(){ return _name; } int* getRoles(){ return _roles; } private: string _name; int _salary; int _roles[5]; }; class Employee { public: Employee(User user){ _user = user; } User getUser(){ return _user; } private: User _user; }; int main() { // your code goes here User user("User1"); Employee employee(user); auto roles = employee.getUser().getRoles(); roles[0] = 1; }
我的理解是:getUser()返回的临时对象会被销毁,roles变量可能指向已被回收的内存地址,这个理解是否正确?
代码对比疑问
类似地,以下代码会触发错误:
string name = "name123"; auto arr = name.substr(0,4).c_str(); cout<<arr[0];
但为何这段代码却不会报错?
string name = "name123"; string sub(name.substr(0,4).c_str()); cout<<sub;
同时,为何第一段代码未被ASAN检测为stack-use-after-scope问题?
问题解答
1. 第一段代码的理解是否正确?
你的理解完全正确。employee.getUser()返回的是User类型的临时对象,这个对象的生命周期只持续到当前表达式结束(也就是auto roles = ...;这一行执行完)。getRoles()返回的是临时对象中_roles数组的地址,当临时对象被销毁后,roles就指向了栈上已被回收的内存区域,后续对roles[0]的赋值操作本质就是stack-use-after-scope问题。
2. 为什么第二段代码触发错误,第三段却不会?
- 第二段代码中,
name.substr(0,4)返回的临时string对象会在表达式结束后立即销毁,c_str()得到的是这个临时对象内部字符数组的地址,临时对象销毁后该内存被回收,访问arr[0]就是操作已释放的栈内存,ASAN会检测到这个问题。 - 第三段代码中,临时
string对象销毁前,我们已经用它的c_str()构造了新的string sub。string的构造函数会把c_str()指向的字符内容完整复制到自己管理的内存空间中,后续使用的是sub自身的内存,和原临时对象无关,因此不会出现悬空引用。
3. 为什么第一段代码没被ASAN检测出来?
ASAN对栈内存的检测存在一定局限性:
- 临时对象销毁后,栈上对应的内存区域可能还没被ASAN标记为“不可访问”,或者未被其他数据覆盖,此时的内存操作可能刚好避开了检测逻辑。
- 栈内存的标记和复用机制存在延迟,当你对
roles[0]赋值时,该区域可能还处于ASAN未监控的状态。 - 可以尝试关闭编译优化(添加
-O0参数)、使用更高版本的编译器,或者增加对该内存的后续访问操作,ASAN大概率能检测到这个问题。
内容的提问来源于stack exchange,提问作者Apurv
相关产品推荐
相关产品推荐

