微信开放平台可以做网站么,去西安旅游最佳路线,平面设计技术培训机构,美工培训班线上目录
1、C程序中的内存问题
2、AddressSanitizer是什么#xff1f;
3、AddressSanitizer内存检测原理简述
3.1、内存映射
3.2、插桩
4、为什么选择AddressSanitizer#xff1f;
4.1、Valgrind介绍
4.2、AddressSanitizer在速度和内存方面为什么明显优于Valgrind
4.3…目录
1、C程序中的内存问题
2、AddressSanitizer是什么
3、AddressSanitizer内存检测原理简述
3.1、内存映射
3.2、插桩
4、为什么选择AddressSanitizer
4.1、Valgrind介绍
4.2、AddressSanitizer在速度和内存方面为什么明显优于Valgrind
4.3、在很多实际项目中我们需要使用AddressSanitizer
5、无法使用Valgrind的具体项目实例
5.1、使用Valgrind检测导致CPU占满无法进行检测
5.2、使用Valgrind检测导致程序运行过慢无法进行检测
6、AddressSanitizer与其他内存工具的比较
7、如何使用AddressSanitizer
7.1、升级gcc版本
7.2、如何配置使用AddressSanitizer进行内存检测
7.3、使用AddressSanitizer进行内存检测的实例
7.4、使用AddressSanitizer的注意事项
8、Windows平台高版本的Visual Studio也支持AddressSanitizer工具 VC常用功能开发汇总专栏文章列表欢迎订阅持续更新...https://blog.csdn.net/chenlycly/article/details/124272585C软件异常排查从入门到精通系列教程专栏文章列表欢迎订阅持续更新...https://blog.csdn.net/chenlycly/article/details/125529931C软件分析工具从入门到精通案例集锦专栏文章正在更新中...https://blog.csdn.net/chenlycly/article/details/131405795C/C基础与进阶专栏文章持续更新中...https://blog.csdn.net/chenlycly/category_11931267.html C程序中大部分问题都是内存问题有些可以快速定位有些则很难排查通过日志打印及代码走读很难定位并且有些难缠的问题只在客户的环境中才会出现在公司内部测试环境中无法复现处理起来非常头疼把人搞的精疲力竭后可能还查不出来。今天给大家介绍来自Google的强大C/C内存检测工具AddressSanitizer它可以很好地解决实际应用环境中很多无法快速定位的内存问题。
1、C程序中的内存问题 在C程序中大部分程序运行异常都是内存问题引起的内存问题也是最让C程序员头疼的事情。常见的内存异常有空指针、野指针、线程栈溢出、内存越界栈内存越界、堆内存越界和全局内存越界、堆内存被破坏、内存泄漏、虚拟内存不足等如下所示 其中某些场景下的内存越界问题以及堆内存被破坏问题最为难查特别是堆内存被破坏问题。一般堆内存被破坏会表现为程序到处胡乱崩溃一会崩在这里一会崩在那里因为堆内存被破坏一会崩溃在new的地方一会崩溃在delete的地方 关于C软件异常及内存错误的详细说明我就不在此赘述了感兴趣的可以去看看我之前写的文章
C软件异常分析概述https://blog.csdn.net/chenlycly/article/details/123991269引发C程序内存错误的常见原因分析与总结https://blog.csdn.net/chenlycly/article/details/128599525
2、AddressSanitizer是什么 AddressSanitizer简称ASan是google提供的一款面向C/C语言的内存错误问题检查工具它可以检测出堆溢出Heap buffer overflow、栈溢出Stack buffer overflow、全局变量越界Global buffer overflow、已释放内存使用Use after free 、初始化顺序Initialization order bugs、内存泄漏Use after free 等多个内存问题。 AddressSanitizer项目地址AddressSanitizer · google/sanitizers Wiki · GitHub 参考文档页面AddressSanitizerAlgorithm · google/sanitizers Wiki · GitHub AddressSanitizer相对于Valgrind要快很多只拖慢程序两倍左右。它包括一个编译器instrumentation插桩模块和一个提供malloc/free替代项的运行时库。从gcc 4.8开始AddressSanitizer成为gcc的一部分使用时非常方便只需要在编译时指定编译选项就可以了。gcc 4.8自带的AddressSanitizer还不完善有明显的缺陷比如当监测到任何一个error它就会强制退出主程序导致程序无法继续运行再比如没有符号信息最好使用gcc 4.9及以上版本。 AddressSanitizer原先只支持Linux现在也可以在Windows上使用了。微软在Visual Studio 2019的16.9版本中引入了强大的内存分析工具AddressSanitizer。 3、AddressSanitizer内存检测原理简述 AddressSanitizer主要由两部分组成一个是静态插桩(Instrumentation)模块将内存访问判断的逻辑直接插入在了二进制中保证了检测逻辑的执行速度另一部分则是运行时库(Run-time library)提供部分功能的开启、报错函数和 malloc/free/memcpy 等函数的ASan检测版本。 instrument静态插桩模块对栈上对象、全局对象、动态分配的对象分配redzone以及针对这些内存做访问检测。 runtime 运行时库提供了一些运行时的复杂的功能(比如poison/unpoison shadow memory)替换 malloc/free/memcpy/memset等实现提供报错函数针对每一次内存读写编译器都会插入判断逻辑判断地址是否被投毒(poisoned)。 该算法的思路是如果要防住Buffer Overflow漏洞只需要在每块内存区域右端或两端能防overflow和underflow加一块区域RedZone使RedZone的区域的影子内存Shadow Memory)设置为不可写即可。 3.1、内存映射 AddressSanitizer保护的主要原理是对程序中的虚拟内存提供粗粒度的影子内存(每8个字节的内存对应一个字节的影子内存)为了减少overhead采用了直接内存映射策略所采用的具体策略如下Shadow (Mem 3) offset。每8个字节的内存对应一个字节的影子内存 影子内存中每个字节存取一个数字k如果k0则表示该影子内存对应的8个字节的内存都能访问如果0k7表示前k个字节可以访问如果k为负数不同的数字表示不同的错误e.g. Stack buffer overflow, Heap buffer overflow)。 3.2、插桩 为了防止buffer overflow需要将原来分配的内存两边分配额外的内存Redzone并将这两边的内存加锁设为不能访问状态这样可以有效的防止buffer overflow但不能杜绝buffer overflow。插桩的简化示意图如下 以下是在栈中插桩的一个例子 1未插桩的代码 void foo()
{char a[8];// ...return;
} 2插桩后的代码 char redzone1[32]; // 32-byte aligned
char a[8]; // 32-byte aligned
char redzone2[24];
char redzone3[32]; // 32-byte aligned
int*shadow_base MemToShadow(redzone1);
shadow_base[e] oxffffffff;// poison redzone1
shadow_base[1] oxffffffe0;// poison redzone2unpoison a
shadow_base[2] oxffffffff;// poison redzone3
// ...
return; 在动态运行库中将malloc/free函数进行了替换。在malloc函数中额外的分配了Redzone区域的内存将与Redzone区域对应的影子内存加锁主要的内存区域对应的影子内存不加锁。free函数将所有分配的内存区域加锁并放到了隔离区域的队列中保证在一定的时间内不会再被malloc函数分配可检测Use after free类的问题。
4、为什么选择AddressSanitizer Linux平台上常用的内存分析工具主要有Valgrind和AddressSanitizer这两个工具在使用方式上有一定的区别。Valgrind不需要重新编译代码可以直接附加到程序上对内存进行监测AddressSanitizer则需要重新编译代码。所以很多时候大家为了图方便会优先使用Valgrind。 但Valgrind会占用大量内存并明显拖慢程序运行的速度这使得在部分场景下无法正常使用Valgrind。而AddressSanitizer在运行速度和效率上要比Valgrind好很多所以在Valgrind无法完成检测时可以选择AddressSanitizer。
4.1、Valgrind介绍 Valgrind是一套Linux下开放源代码GPL V2的仿真调试工具的集合是运行在Linux 上的多用途代码分析和内存调试常用工具。Valgrind由内核core以及基于内核的其他调试工具组成。内核类似于一个框架framework它模拟了一个CPU环境并提供服务给其他工具而其他工具则类似于插件 (plug-in)利用内核提供的服务完成各种特定的内存调试任务。 Valgrind是基于仿真的方式对程序进行调试它先于应用程序获取实际处理器的控制权并在实际处理器的基础上仿真一个虚拟处理器并使应用程序运行于这个虚拟处理器之上从而对应用程序的运行进行监视。 应用程序并不知道该处理器是虚拟的还是实际的已经编译成二进制代码的应用程序并不用重新进行编译Valgrind 直接解释二进制代码使得应用程序基于它运行从而能够检查内存操作时可能出现的错误。所以在Valgrind下运行的程序运行速度要慢的多而且使用的内存比目标程序要多的多这也是Valgrind的一大劣势这也导致部分场合下没法使用Valgrind去分析。
4.2、AddressSanitizer在速度和内存方面为什么明显优于Valgrind Valgrind采用的是二进制完全映射的影子内存技术会占用更多内存才能去有效地监测内存变化。并且开启Valgrind监测之后会严重降速比如使用memcheck工具去监测内存基本上是10到30倍的降速明显的降速会导致我们的软件在业务上出现不可用的情况。关于降速Valgrind官网上有着详细的说明 The main one is that programs run significantly more slowly under Valgrind. Depending on which tool you use, the slowdown factor can range from 5-100. Memcheck runs programs about 10-30x slower than normal. 而Google提供的内存检测工具AddressSanitizer在内存占用和运行速度方面有着卓越的表现相比于ValgrindAddressSanitizer的优势相当明显。AddressSanitizer采用了一种取巧的影子内存玩法将虚拟地址空间的1/8分配给它的影子内存并使用一个带有比例和偏移量的直接映射将一个应用程序地址转换为它相应的影子地址确保了少量内存就能完成一个程序的监测。并且AddressSanitizer降速也比较少。AddressSanitizer在内存占用和降速方面通过USENIX高等计算机系统协会某篇论文中的一段描述可以佐证 We present AddressSanitizer, a new tool that combines performance and coverage. AddressSanitizer finds out-of-bounds accesses (for heap, stack, and global objects) and uses of freed heap memory at the relatively low cost of 73% slowdown,1.5x-4x memory overheadmaking it a good choice for testing a wide range of C/C applications. 4.3、在很多实际项目中我们需要使用AddressSanitizer Valgrind采用的是二进制完全映射的影子内存技术会占用更多内存才能去有效地监测内存变化还会明显地拖慢程序的运行速度可能会导致程序在收到请求后不能及时的响应没法模拟出真实运行时的场景可能就不一定能复现问题甚至还会因为运行速度过慢导致程序根本无法正常的运转。所以我们有时需要使用占用内存少、运行速度更快的AddressSanitizer。
5、无法使用Valgrind的具体项目实例 在实际项目中我们遇到过不少无法使用Valgrind的场景。如果没有内存检测工具排查起来效率非常低仅仅通过打印日志和走读代码很难定位问题。如果有内存检测工具可能很快就能定位出来。所以后来转向使用AddressSanitizer很多Valgrind无法工作的场景AddressSanitizer都可以胜任。与Valgrind相比AddressSanitizer的运行速度是真的快同时内存错误的检测能力也非常强。 5.1、使用Valgrind检测导致CPU占满无法进行检测 某客户现场我们的程序出现了内存异常问题最先使用Valgrind进行检测发现使用Valgrind检测时机器的CPU一直是100%直接导致程序业务无法正常工作由于业务无法运转导致我们没有办法让程序跑到存在问题的流程所以检测也就无法实施了。
5.2、使用Valgrind检测导致程序运行过慢无法进行检测 某客户现场出现了多线程死锁使用gdb附加到目标程序上调试运行发现某个公用模块每次都会检测到“它管理的堆的魔数被破坏”于是强制退出了。由于该公用模块中内存管理器使用的地方比较多包括上层业务代码和底层库通过走读代码去分析哪些地方分配了堆内存很难实施。于是我们使用Valgrind分析但因为速度过慢程序没法运转起来内存检测任务无法实施。
6、AddressSanitizer与其他内存工具的比较 AddressSanitizer与其他内存检测工具的比较如下所示 Items \ Tools AddressSanitizer Valgrind/Memcheck Dr. Memory Mudflap Guard Page gperftools technology CTI DBI DBI CTI Library Library ARCH x86,ARM,PPC,... x86,ARM,PPC x86 all(?) all(?) all(?) OS Linux, Mac, Windows, FreeBSD, Android Linux, Mac Windows, Linux Linux, Mac(?) All (1) Linux, Windows Slowdown 2x 20x 10x 2x-40x ? ? Detects: Heap OOB yes yes yes yes some some Stack OOB yes no no some no no Global OOB yes no no ? no no UAF yes yes yes yes yes yes UAR yes (see UseAfterReturn) no no no no no UMR no (see MemorySanitizer) yes yes ? no no Leaks yes (see LeakSanitizer) yes yes ? no yes
上表中的相关名词说明如下 DBI: dynamic binary instrumentation CTI: compile-time instrumentation UMR: uninitialized memory reads UAF: use-after-free (aka dangling pointer) UAR: use-after-return OOB: out-of-bounds x86: includes 32- and 64-bit. Guard Page: a family of memory error detectors (Electric fence or DUMA on Linux, Page Heap on Windows, Guard Malloc in Mac) gperftools: various performance tools/error detectors bundled with TCMalloc. Heap checker (leak detector) is only available on Linux. Debug allocator provides both guard pages and canary values for more precise detection of OOB writes, so its better than guard page-only detectors. 7、如何使用AddressSanitizer 从gcc 4.8开始gcc才集成AddressSanitizer工具所以要使用AddressSanitizer必须将gcc升级到4.8或以上版本。然后使用高版本gcc对代码进行重新编译在编译时指定编译选项就可以了。 7.1、升级gcc版本 可以到ftp://gcc.gnu.org/pub/gcc上下载高版本的gcc然后到执行源码树中的contrib/download_prerequisites文件它会下载和设置GCC编译依赖的组件。然后在GCC源码树同级的目录建立一个编译目录比如叫build_dir然后在该编译目录中执行如下命令进行编译和安装 ../src_dir/configure make make install 7.2、如何配置使用AddressSanitizer进行内存检测 AddressSanitizer是内置在gcc中的主要设置编译参数去设定是否启用AddressSanitizer的内存检测。
1如果没使用makefile直接gcc命令去编译则在命令中添加-fsanitizeaddress选项如下 gcc -fsanitizeaddress -fno-omit-frame-pointer -O1 -g use-after-free.c -o use-after-free 其中 1用-fsanitizeaddress选项编译和链接你的程序。 2用-fno-omit-frame-pointer编译以得到更容易理解stack trace。 3可选择-O1或者更高的优化级别编译 2如果使用makefile则在编译选项CFLAGS和链接选项LDFLAGS中都要添加-fsanitizeaddress选项如下 #都要追加-fsanitizeaddress开关CFLAGS-fsanitizeaddress LDFLAGS-fsanitizeaddress 7.3、使用AddressSanitizer进行内存检测的实例 比如下面的代码中分配array数组并释放然后返回它的一个元素返回了一个已经释放了的内存地址 int main (int argc, char** argv)
{int* array new int[100];delete []array;return array[1];
} 上述代码放置在use-after-free.c中直接使用gcc编译该文件即可命令如下 gcc -fsanitizeaddress -fno-omit-frame-pointer -O1 -g use-after-free.c -o use-after-free 然后运行use-after-feeAddressSanitizer检测了错误就会打印出下面的信息 3189ERROR: AddressSanitizer: heap-use-after-free on address 0x61400000fe44
at pc 0x0000004008f1 bp 0x7ffc9b6e2630 sp 0x7ffc9b6e2620
READ of size 4 at 0x61400000fe44 thread T0#0 0x4008f0 in main /home/ron/dev/as/use_after_free.cpp:9#1 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.60x2082f)#2 0x4007b8 in _start (/home/ron/dev/as/build/use_after_free0x4007b8)0x61400000fe44 is located 4 bytes inside of 400-byte region [0x61400000fe40,0x61400000ffd0)
freed by thread T0 here:#0 0x7f3763ef1caa in operator delete[](void*) (/usr/lib/x86_64-linux-gnu/libasan.so.20x99caa)#1 0x4008b5 in main /home/ron/dev/as/use_after_free.cpp:8#2 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.60x2082f)previously allocated by thread T0 here:#0 0x7f3763ef16b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.20x996b2)#1 0x40089e in main /home/ron/dev/as/use_after_free.cpp:7#2 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.60x2082f)SUMMARY: AddressSanitizer: heap-use-after-free /home/ron/dev/as/use_after_free.cpp:9 main 如上图打印出的信息主要分三部分 1ERROR部分指出错误类型是heap-use-after-free 2READ部分指出线程名thread T0操作为READ发生的位置是use-after-free.c:9行号。 该heapk块之前已经在use-after-free.c:8行号被释放了 该heap块是在use-fater-free.c:7行号分配的。 3SUMMARY部分前面输出的概要说明。 7.4、使用AddressSanitizer的注意事项 使用AddressSanitizer过程中可能会遇到一些问题此处给大家讲几个注意事项。
1 如果存在第三方内存管理器可能需要取消对第三方管理器的依赖 如果存在第三方内存管理器比如tcmalloc)需要去掉第三方内存管理器的编译选项或连接选项因为内存管理器分配的内存自身会预留一些管理空间越界不多只写到这部分空间时AddressSanitizer越界检测是不会认为它是异常的因为它们仍然是用户分配的范围之内的第三方内存管理器与应用程序对于ASan没有差异的。同时内存管理器通常会延迟释放内存这也会影响检测的及时性。此外如果链接时提示ASan中的符号找不到时给程序显示添加对libasan库的连接(默认在/usr/local/lib目录找不时使用find命令找下)。
2内存不足问题 内存检测工具会增加程序的内存消耗32位程序地址空间只有4G用户态的通常只有3G如果程序跑起来之后提示无法分配内存可以通过设置如下两个选项缓解一下 export ASAN_OPTIONSquarantine_size_mb256:start_deactivated1。 其中 quarantine_size_mb设置小点牺牲使用已经释放了的内存问题的检测能力。 start_deactivated设置为1启动时不会加载asan的全部功能用于节省内存。 上面的选项只是缓解根本的解决之道还是要开发64位版本的程序。
3错误忽略 有些错误我们改动不了或直接认为绝对安全也可以在函数上面添加属性进行错误忽略。比如 #if defined(__clang__) || defined (__GNUC__)
# define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
#else
# define ATTRIBUTE_NO_SANITIZE_ADDRESS
#endif
...
ATTRIBUTE_NO_SANITIZE_ADDRESS
void ThisFunctionWillNotBeInstrumented() {...} 8、Windows平台高版本的Visual Studio也支持AddressSanitizer工具 AddressSanitizer工具原先只支持Linux现在也可以在Windows上使用了。微软在Visual Studio 2019的16.9版本们引入了AddressSanitizer在安装Visual Studio 2019的16.9版本及以后的版本时会默认安装AddressSanitizer工具默认勾选“C AddressSanitizer” 对于如何在VS中如何使用AddressSanitizer内存分析工具可以看一下微软官方文章的详细说明在Visual Studio中使用AddressSanitizerhttps://docs.microsoft.com/zh-cn/cpp/sanitizers/asan?viewmsvc-170此处我就不详细展开了大家需要使用的话可以去详细研究一下。