字节Android Native Crash治理之Memory Corruption工具原理与实践

本文介绍了字节跳动开发的MemCorruption工具,用于检测和定位Android Native层的UseAfterFree、DoubleFree、HeapBufferOverflow问题。文章详细阐述了工具的原理、字节跳动的解决方案、线上效果及案例分析,展示了如何通过栈回溯和双采样内存配置策略实现在用户无感知的情况下检测Memory Corruption问题。


作者:字节跳动终端技术——庞翔宇

内容摘要

​ MemCorruption工具是字节跳动AppHealth (Client Infrastructure - AppHealth) 团队开发的一款用于定位野指针(UseAfterFree)、内存越界(HeapBufferOverflow)、重复释放(DoubleFree)类问题检测工具。广泛用于字节跳动旗下各大 App 线上问题检测。本文将通过方案原理和实践案例来介绍此工具。

背景

​ 随着 Android App 开发的技术栈不断向Native层扩展,带来的线上Native稳定性问题日趋严重。Android中有超过半数的漏洞都来源于Memory Corruption问题。分析定位线上此类问题的难点在于,首先线下难复现,其次问题发生时已经不是第一案发现场,且此类问题调用栈表现类型多样化。这就导致了此类问题短期内难分析、难定位、难解决的现状。

什么是Memory Corruption问题

UseAfterFree

UseAfterFree下面简称UAF,野指针类问题;

void HeapUseAfterFree() {
  int *ptr1 = (int*)malloc(4);
  if(ptr1 != NULL){
    *ptr1 = 0xcccc;
    free(ptr1);           //free ptr1           
    *ptr1 = 0xabcd;       //free后write ptr1 mem这里不会崩溃
  }
}

​ 这里以UAF问题说明Native崩溃后不是第一现场的场景。假设上面代码运行在线程A,第2行申请4byte大小的一块堆内存,第5行释放这块堆内存,执行第6行前线程A时间片执行完,切换到线程B执行,线程B此时申请4byte大小的内存块,内存管理器会概率性的分配之前已经释放的ptr1指向的内存块分配给线程B使用,线程B给ptr2指向内存赋值0xff,之后线程B时间片执行完让出CPU,切换线程A执行,ptr1被赋值0xabcd,之后切换回线程B进行条件判断,ptr2内存值不为0xff触发异常逻辑。不是线程B预期的值。这样的场景在大型的App程序运行过程中时有发生。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-os3EN3eo-1635736391205)( http://lf3-client-infra.bytetos.com/obj/client-infra-images/memCorruption/Android_mem_1.png)]

DoubleFree

DoubleFree下面简称DF,堆内存二次释放类问题;

void DoubleFree() {
   
   
  int *ptr = (int*)malloc(4);
  free(ptr);
  free(ptr)
<think>我们正在讨论如何检查glibc内存损坏问题。用户之前遇到了`free(): corrupted unsorted chunks`错误,并询问了检查glibc内存损坏的方法。根据之前的对话,我们已经讨论了一些内存调试工具(如Valgrind和AddressSanitizer),现在需要更详细地展开关于glibc内存损坏的检查方法。 注意:用户要求参考站内引用,但之前的引用标记(如[^1])是用于示例的,现在我们需要根据实际内容合理引用。 我们将介绍以下内容: 1. 使用glibc内置的调试功能(如MALLOC_CHECK_) 2. 使用Valgrind 3. 使用AddressSanitizer (ASan) 4. 使用GDB调试器结合glibc的堆检查函数 5. 其他工具(如Electric Fence、ltrace) 注意:按照要求,数学表达式用$...$,独立公式用$$...$$,并且使用中文回答。同时,在回答的末尾生成相关问题。 由于用户的问题是关于检查glibc内存损坏,我们将重点放在如何利用工具来检测堆损坏。 </think> ## 🔍 如何检查glibc内存损坏问题 glibc内存损坏问题可通过以下方法进行系统化检测,按推荐优先级排序: ### ✅ 1. AddressSanitizer (ASan) - 首选工具 ```bash # 编译时添加ASan检测 gcc -fsanitize=address -g your_program.c -o output ./output ``` - **检测能力**:实时捕捉堆溢出、栈溢出、UAF(释放后使用)等错误 - **优势**:执行效率高(仅2-3倍减速),错误定位精确到代码行 - **输出示例**: ``` ==ERROR: AddressSanitizer: heap-buffer-overflow WRITE of size 4 at 0x60200000effc #0 in function foo() at example.c:15 # 直接定位到问题代码行[^1] ``` ### ✅ 2. Valgrind - 深度内存分析 ```bash valgrind --tool=memcheck --leak-check=full ./your_program ``` - **核心功能**: - `Invalid read/write`:检测越界访问 - `Use after free`:野指针使用 - `Double free`:重复释放内存 - **优势**:无需重新编译,支持多线程检测 - **注意**:运行时减速约10-20倍,适合非性能敏感场景 ### ✅ 3. glibc内置调试功能 ```bash export MALLOC_CHECK_=3 # 启用严格检查模式 ./your_program ``` - **级别说明**: - `0`:关闭检查 - `1`:打印错误但不终止 - `2`:立即终止并报错 - `3`:打印详细错误并终止(最严格) - **适用场景**:生产环境快速启用,但信息不如ASan详细 ### ✅ 4. GDB调试 + glibc堆分析 ```gdb (gdb) break __libc_message # 在glibc错误处断点 (gdb) run (gdb) p *(mchunkptr)0x12345 # 查看堆块元数据 (gdb) malloc_info(0, stdout) # 导出堆结构(需glibc>=2.10) ``` - **关键命令**: - `vis`(pwndbg插件):可视化堆布局 - `heap`(gef插件):分析堆块状态 - **适用场景**:ASan/Valgrind无法定位时的深度分析 ### ✅ 5. mtrace 内存跟踪 ```c #include <mcheck.h> int main() { mtrace(); // 开启跟踪 // ... 业务代码 ... muntrace(); } ``` ```bash export MALLOC_TRACE=mtrace.log ./program mtrace ./program mtrace.log # 生成分析报告 ``` - **输出内容**:未释放内存位置及分配点 - **局限**:仅检测内存泄漏,不检查越界 --- ### 📊 工具对比总结 | 工具 | 检测类型 | 速度影响 | 定位精度 | 使用复杂度 | |--------------------|------------------|----------|----------|------------| | **AddressSanitizer** | 所有内存错误 | 2-3x | 代码行 | ★★☆☆☆ | | **Valgrind** | 所有内存错误 | 10-20x | 函数级 | ★★★☆☆ | | **MALLOC_CHECK_** | 堆一致性 | 极小 | 进程级 | ★☆☆☆☆ | | **GDB+堆分析** | 堆结构损坏 | 无 | 内存地址 | ★★★★☆ | > 📌 **最佳实践**:开发阶段使用ASan实时检测,线上环境用`MALLOC_CHECK_=2`捕获严重错误,疑难问题结合Valgrind和GDB分析[^2]。 --- ### ❓ 相关问题 1. AddressSanitizer检测到`heap-buffer-overflow`时应如何快速定位问题根源? 2. 如何在生产环境中安全启用glibc的`MALLOC_CHECK_`参数? 3. Valgrind报告"Conditional jump depends on uninitialised value"错误如何解决? 4. 如何通过GDB分析glibc的堆块元数据结构`struct malloc_chunk`? 5. 多线程程序中的内存损坏问题有哪些特殊检测方法? [^1]: ASan在$O(1)$时间内完成影子内存映射,实时拦截非法访问 [^2]: `MALLOC_CHECK_`通过覆写`_libc_message`实现错误拦截,级别3会触发`abort()`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值