局部变量与std::map

最近遇到了一个问题,一个平时运行没有毛病的模块,被ASAN报heap use after free,问题出在堆内存,但是看上去全局变量没有哪里会被释放,唯一有风险的地方是在栈内存,这就比较有意思了。

局部变量的生命周期

局部变量一般分配在栈内存上,随着函数执行结束而释放:

int foo()
{
    int a = 1;    //入栈
    return a;
}

int main()
{
    foo();    //出栈
    return 0;
}

如果在main中有变量接收了返回值,那么它就会在堆内存中开辟一块空间:

int foo()
{
    int a = 1;    //入栈
    cout << &a << endl;
    return a;
}

int main()
{
    int a = foo();    //出栈,main的a复制foo的返回值
    cout << &a << endl;
    return 0;
}

可以看到两次地址是不一样的。
如果局部变量是个指针,就会导致危险行为:

const char* foo()
{
    string s = "hello";
    const char * s2 = s.c_str();
    cout << s2 << endl;
    cout << (void*)s2 << endl;
    return s2;
}

int main()
{
    const char* s = foo();
    cout << s << endl;
    cout << (void*)s << endl;
    return 0;
}

main的s只是复制了foo返回的地址,而地址指向的内存是已经被释放的,也就是s变成了野指针,这在运行时会造成严重的错误,很容易被发现。

如果是STL容器呢?

typedef map<int,string> SMap;
const char* foo()
{
    SMap testMap;
    SMap::iterator iter;
    string s = "hello";
    testMap.insert(make_pair(0,s));
    iter = testMap.find(0);
    return iter->second.c_str();
}

int main()
{
    const char* s = foo();
    cout << s << endl;
    return 0;
}

这里的main函数可以正常输出"hello",即使那一块内存已经被释放了,但是对系统来说,那一块内存还是在内存池里泡着,所以并没有被系统释放,通过以下代码可以验证:

typedef map<int,string> SMap;

const char* foo()
{
    SMap testMap;
    SMap::iterator iter;
    string s = "hello";
    testMap.insert(make_pair(0,s));
    iter = testMap.find(0);
    return iter->second.c_str();
}

int main()
{
    const char* s = foo();
    cout << s << endl;
    SMap testMap;
    testMap.insert(make_pair(1,"world"));
    cout << s << endl;
    return 0;
}

它的输出是:

hello
world

可以看到,在main中新建的map覆盖了foo函数中临时map的值,这是由于内存池的机制导致的,对STL来说,这一块内存是已经被释放的,它被标记为空闲,只是内容还暂时保留,所以虽然一开始的s可以输出正确的"hello",但一旦在调用它之前又创建了新的map并insert了差不多长度的内容的话(长度不等内存池可能会调用别的内存块,可以自行验证),这块内存就会被覆盖,这是非常危险的行为,但是又极其隐蔽,因为只要你调用及时,s的值是完全正确的。

总结

STL容器实现了内存池,内存是预先开辟好,而不是临时申请的,所以即使是一个局部的map,调用insert时,它创建的内容也是在堆上而非栈上,只有容器本身是在栈上,这就导致了局部变量引起heap UAF。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容