常见内存问题
内存问题有两种:内存损坏 memory corruption(crash) 和 内存泄漏 memory leak
memory crash:发生在修改了未知内存后,程序访问了这部分受损的内存,可能会导致程序crash掉或者发生不可预知的结果。
发生在:
- 使用未初始化的内存
- 使用不存在的内存,空指针
- 使用了超出分配的内存,数组越界
- 堆内存管理错误,UAF,double free
memory leak:动态分配的内存没有释放,或者分配太多内存跑不到free的地方
可能会引起:
- 程序功能正常,但是引起系统性能卡顿,apk越来越慢
- 内存泄漏过多导致申请内存不足malloc mmap失败,造成OOM等其他问题
内存调试技术
Malloc Debug
Malloc debug 是一种native层内存问题的方法。他可以帮助我们定位memory crash, memory leak, UAF的问题。如果检测到任何的问题,会通过日志的方式呈现出来。
Malloc Debug 开启后会在替换掉原来的内存分配函数(加Hook层),在分配的内存数据前加Header,并且可以根据option添加前后的front_guard和rear_guard。
Header |
---|
front_guard |
allocation data |
rear_guard |
Header组成如下:
struct Header {
uint32_t tag; //标记内存是否释放
void* orig_pointer; //指向Header的起始地址
size_t size: //allocation data的大小
size_t usable_size; //已使用大小
}__attribute__((packed)); //取消内存对齐
在Header中有个tag标记内存是否释放:0x1ee7d00d为可以使用, 0x1cc7dccd为已经释放
- front_guard 由 0xaa 填充的8个byte组成
- rear_guard 由 0xbb 填充的8个byte组成
根据这些前后的固定填充,如果这些固定的字节发生了变化可以判断这部分内存被踩了。
相关的option:
- guard 可以开启front_guard和rear_guard,做越界检查
- 不会在写超的时候第一现场拦下来,只有free的时候才可以检查
- free_track 可以检查UAF
- 当free后不会归还系统,而是保存到List,填充内容为0xef。如果后面发现这部分内容不是0xef的话就表示这部分内存UAF了
- 但是这样会有额外内存开销,系统压力会比较大。
- backtrace 追踪每次分配信息
- 会大大减慢内存分配的速度。如果因为开启这个选项导致系统运行太慢,需要减少采集的帧数。
通过 dump + gdb 查看内存信息。
malloc debug 对内存调试有帮助,但是不方便,你只能确定发生了这些问题,但是不能在第一现场拦下,也不能确定问题发生的时间。
ASAN
ASAN:Address Sanitizer Mechanism
把进程虚拟地址分成两部分,一部分是正常使用的main application memory,一部分是Shadow memory影子内存,两者占比8:1。
shadow memory用来记录main allocation memory的状态。
这个debug方法可以在第一现场拦下
但是ASAN只能标记内存能否访问,没有标记内存的所有者。
log里面会把出错的report打印出来
开启方式:make结束以后再执行一次相应的命令
HWASAN
HWASAN:Hardware ASAN
需要在64位机器上,并且要内核4.14以上才支持
可以标记内存的所有者,在指针的最高2byte做了tag
HWASAN可以检测的bug与ASAN相同:
- 堆栈buffer溢出
- UAF,double free
- 栈溢出
与ASAN的改动:main application memory 与 shadow memory 的占比为16:1
然后在可用内存地址的最高2个byte(16个bit)加了个tag标记内存所有者
在shadow memory如果对应的main memory的16个byte都可以访问,则shadow memory存放tag的值,如果只有部分byte可以访问,则记录0~15可访问的byte,然后将tag的值记录到main memory最后一个不可访问的byte上。
开启方式:可以用一次make指令然后加上编译选项即可: make SANITIZE_TARGET=hwaddress
如何确认已开启?通过ps | grep process找到进程号,然后cat /proc/pid/maps|grep hwasan
缺点:因为tag只有8bit,最多只有256个不同的tag,有概率会分配2个相同的tag
Slub Debug
用于内核层的debug工具,与Malloc Debug类似采用前后插guard的方式
KASAN
对应与用户空间的ASAN工具。也是采用shadow memory来检测内存,占可用内存的1/8,会带来内核内存空间的开销。
开启方式:在.config配置文件中指定 CONFIG_KASAN_GENERIC=y
KASAN有三种,GENERIC KASAN, Software Tag-based KASAN, Hardware Tag-based KASAN
memory leak
cat /proc/meminfo 查看内存使用情况
cat /proc/pid/maps 查看进程的内存映射情况(比如使用malloc后会出现[heap]区