你是否碰到过 Access Violation?
当在调试模式(/MTd or /MDd)下使用 Microsoft C++ 标准库时,标准库内部会尽其所能保证使开发者远离内存访问违规(Access Violation)的问题。标准库中的每个容器都有一个自定义的“包装”迭代器,在每次访问时,它会检查它是否仍然有效,不是结束迭代器,并且在执行算术运算时,检查它是否还未可用。
但是,一旦你离开迭代器的世界进入指针,这些检查就无法再执行任何操作,请看下面的代码:
地址清理器(ASan)隆重登场。
使用 cl 或 clang 编译器将 -fsanitize=address 选项添加到构建中,编译器将添加对访问地址的检查,以确保访问的内存在范围内并且未被提前释放。
自我们最初实现地址清理器功能以来,检查堆栈和原始堆内存一直有效。当使用 std::vector 或 std::string 等容器时,它还将确保你不会访问底层分配之外的内存。但是,由于容器是库代码,因此默认情况下,ASan 不会阻止你访问超出容器容量范围但仍在分配范围内的内存。
展开全文
在 microsoft/STL#2071 中,标准库团队在 std::vector 中添加了对 ASan 容器溢出注释的支持,这意味着我们的标准库使用 ASan 的
__sanitizer_annotate_contiguous_container API 手动保持 std::vector 缓冲区的注释为最新。这意味着check_vector上面的容器溢出错误正确无误,我们在代码中发现了更多的错误!
然而,std::string与std::vector非常不同,因为小字符串优化(SSO)。在 microsoft/STL#2196 中,我们尝试进行初始实现,也支持 SSO 缓冲区的注释。但是,这有很多有趣和不幸的错误,因此我们需要在Microsoft / STL#2990中再次禁用它。
但是,从 Visual Studio 2022 17.6 预览版 1(在 microsoft/STL#3164 中)开始,我们修复了错误并重新启用了 std::string 中的跟踪机制,以使 ASan 的知识保持最新和正确——这意味着上面的代码示例突然从无声的未定义行为变成了非常响亮的未定义行为!它适用于任何分配器,包括自定义分配器,但会检查边界处的错误略少 – 如果你希望自定义分配器完全支持 ASan 检查,您可以查看 ASan 容器溢出注释。
为了避免困扰原始错误,此检查的一个限制是我们不注释 std::string 的 SSO 缓冲区。这意味着仍然可以访问 SSO 缓冲区内的越界。我们已经为将来解决此问题敞开了大门,但我们希望确保至少检查堆分配的缓冲区有效。
如果在 std::string 上进行容器溢出检查时确实遇到问题,可以通过将
-D_DISABLE_STRING_ANNOTATION 传递给编译来禁用它。
总结
如果你的代码执行过程中,出现了内存访问违规,这通常是一个标志:你的程序在某个地方有问题,或许是访问了早已释放的内存,或者是释放了已经释放过的内存。
有了 ASan 这个检查利器,至少我们找错误的时间可以缩短一些。
但是,重要的是:我们需要加倍努力,争取在一开始就不要犯这个错误。
最后
Microsoft Visual C++团队的博客是我非常喜欢的博客之一,里面有很多关于Visual C++的知识和最新开发进展。大浪淘沙,如果你对Visual C++这门古老的技术还是那么感兴趣,则可以经常去他们那(或者我这)逛逛。
本文来自:《std::string now supports Address Sanitizer》
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权【久伴学 9banxue.com】发表,未经许可,不得转载。