记一次内存访问越界的问题查找
开发结束后,测试代码。出现段错误,堆栈全部变成 " ??"了,被冲没了。因为没有堆栈,出问题的代码很难找。心态有点崩了。
后来同事给了一个编译选项 -fsanitize=address-fno-omit-frame-pointer ,在编译的时候加上,堆栈一下子就出现了。
下面这个就是有问题的代码,还是老代码,差点背锅了。代码一眼看过去没啥问题,其实问题出在snprintf的返回值上。
截图来自百度。
当最后一次往pbuf里写数据时,剩余空间不够了,源字符串被截断,pbuf此时是正常的。但是snprintf这次返回的是源字符串欲写入的长度,不是真实写入的长度。所以下次再去写pbuf的时候,pbuf + uoffset已经越界了,然后ubuflen - uoffset 也成了一个很大的值,内存越界产生了。
AddressSanitizer是google开发一个应用内存检查工具,性能据说比valgrind要好不少,可以配合clang或者GCC编译器使用,GCC需要4.8及以上版本。4.8版本GCC对AddressSanitizer支持有限,功能不太完善,输出的错误信息也不够友好,使用不太方便,建议使用4.9及以上版本。但是我这次使用的是8.3 版本的GCC。详细了解AddressSanitizer信息可以访问其github项目地址:
https://github.com/google/sanitizers/wiki/AddressSanitizer
使用方法很简单,只要在编译程序时加上-fsanitize=address -fno-omit-frame-pointer两个编译选项即可,需要说明的是要使用系统自带的内存管理库,不能使用第三方的内存管理库,因为这个功能要拦截malloc,free等标准函数。gcc几个常用编译选项如下:
-fsanitize=address #开启地址越界检查功能
-fno-omit-frame-pointer #开启后,可以出界更详细的错误信息