C/C++等底层语言在提供强大功能及性能的同时,其灵活的内存访问也带来了各种纠结的问题。如果crash的地方正是内存使用错误的地方,说明你人品好。如果crash的地方内存明显不是consistent的,或者内存管理信息都已被破坏,并且还是随机出现的,那就比较麻烦了。当然,祼看code打log是一个办法,但其效率不是太高,尤其是在运行成本高或重现概率低的情况下。另外,静态检查也是一类方法,有很多工具(lint, cppcheck, klockwork, splint, o, etc.)。但缺点是误报很多,不适合针对性问题。另外好点的一般还要钱。最后,就是动态检查工具。下面介绍几个Linux平台下主要的运行时内存检查工具。绝大多数都是开源免费且支持x86和ARM平台的。
首先,比较常见的内存问题有下面几种:
- • memory overrun:写内存越界
- • double free:同一块内存释放两次
- • use after free:内存释放后使用
- • wild free:释放内存的参数为非法值
- • access uninitialized memory:访问未初始化内存
- • read invalid memory:读取非法内存,本质上也属于内存越界
- • memory leak:内存泄露
- • use after return:caller访问一个指针,该指针指向callee的栈内内存
- • stack overflow:栈溢出
针对上面的问题,主要有以下几种方法:
- 1. 为了检测内存非法使用,需要hook内存分配和操作函数。hook的方法可以是用C-preprocessor,也可以是在链接库中直接定义(因为Glibc中的malloc/free等函数都是weak symbol),或是用LD_PRELOAD。另外,通过hook strcpy(),memmove()等函数可以检测它们是否引起buffer overflow。
- 2. 为了检查内存的非法访问,需要对程序的内存进行bookkeeping,然后截获每次访存操作并检测是否合法。bookkeeping的方法大同小异,主要思想是用shadow memory来验证某块内存的合法性。至于instrumentation的方法各种各样。有run-time的,比如通过把程序运行在虚拟机中或是通过binary translator来运行;或是compile-time的,在编译时就在访存指令时就加入检查操作。另外也可以通过在分配内存前后加设为不可访问的guard page,这样可以利用硬件(MMU)来触发SIGSEGV,从而提高速度。
- 3. 为了检测栈的问题,一般在stack上设置canary,即在函数调用时在栈上写magic number或是随机值,然后在函数返回时检查是否被改写。另外可以通过mprotect()在stack的顶端设置guard page,这样栈溢出会导致SIGSEGV而不至于破坏数据。
以上方法有些强于功能,有些胜在性能,有些则十分方便易用,总之各有千秋。以下是几种常用工具在Linux x86_64平台的实验结果,注意其它平台可能结果有差异。另外也可能由于版本过老,编译环境差异,姿势不对,总之各种原因造成遗漏,如有请谅解~
Tool\Problem | memory overrun | double free | use after free | wild free | access uninited | read invalid memory | memory leak | use after return | stack overflow |
---|---|---|---|---|---|---|---|---|---|
Memory checking tools in Glibc | Yes | Yes | Yes | Yes(if use memcpy, strcpy, etc) | |||||
TCMalloc(Gperftools) | Yes | ||||||||
Valgrind | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
Address Sanitizer(ASan) | Yes | Yes | Yes | Yes | (Memory Sanitizer) | Yes | Yes | Yes | Yes |
Memwatch | Yes | Yes | Yes | ||||||
Dr.Memory | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | |
Electric Fence | Yes | Yes | Yes | Yes | |||||
Dmalloc | Yes | Yes | Yes | Yes | Yes | ||||
mtrace | Yes |