什么是 asan
asan 是 address sanitizer 简称,它是是一种基于编译器用于快速检测原生代码中内存错误的工具。
简而言之,asan 就是一个用于快速检测内存错误的工具。这里很多朋友有误解,asan 其实并不能用于内存泄漏检测,android 平台内存泄漏检测推荐 mallocdebug 。
另外需要注意的是 android o(api >= 27)及以上版本才支持 asan ,ndk 需要选用 r20 及以上版本。
asan 可以检测到内存错误类型如下:
stack and heap buffer overflow/underflow 栈和堆缓冲区上溢/下溢;
heap use after free 堆内存被释放之后还在使用其指针;
stack use outside scope 在某个局部变量的作用域之外,使用其指针;
double free/wild free 指针重复释放的情况。
asan 支持 arm 和 x86 平台,使用 asan 时,app 性能会变慢且内存占用会飙升。针对 arm64 平台,android 官方推荐使用 hwaddress sanitizer (hwasan),后面会介绍。
关于 asan 的原理本文不做深入讨论,该文章的主要目的是帮助开发者快速上手 asan 的使用。
这里感性地介绍下 asan 的工作原理:asan 相当于接管了内存的分配,当分配一块内存时,会在这块内存的前后添加标志位,然后再次使用该内存的时候检查标志位是否被修改,当发现标志位被修改时,判断出现内存错误。
怎么使用 asan
之所以写这篇文件,就是因为发现一些文章介绍 asan 使用方法搞得非常复杂,不易上手。
其实 android 官方的使用说明非常简洁,就是复制黏贴,添加两行代码就搞定。
官方文档:https://developer.android.com/ndk/guides/asan
修改编译脚本
cmake 方式
app 下面的 build.gradle 添加:
android { defaultconfig { externalnativebuild { cmake { # can also use system or none as android_stl. arguments -dandroid_arm_mode=arm, -dandroid_stl=c++_shared } } }}
cmakelists.txt 脚本添加:
target_compile_options(${libname} public -fsanitize=address -fno-omit-frame-pointer)set_target_properties(${libname} properties link_flags -fsanitize=address)
ndk-build 方式
application.mk 文件添加:
app_stl := c++_shared # or system, or none.app_cflags := -fsanitize=address -fno-omit-frame-pointerapp_ldflags := -fsanitize=address
android.mk 文件添加:
app_stl := c++_shared # or system, or none.app_cflags := -fsanitize=address -fno-omit-frame-pointerapp_ldflags := -fsanitize=address
拷贝 asan 库到 jnilibs 目录下
asan 库位于下面路径下:
android-ndk-r21toolchainsllvmprebuiltwindows-x86_64lib64clang9.0.8liblinux
64 位 libclang_rt.asan-aarch64-android.so , 32 位 libclang_rt.asan-arm-android.so ,分别拷贝两个库到 jnilibs 相应的目录下。
新建 wrap.sh 文件,拷贝下面内容到文件中:
#!/system/bin/shhere=$(cd $(dirname $0) && pwd)export asan_options=log_to_syslog=false,allow_user_segv_handler=1asan_lib=$(ls $here/libclang_rt.asan-*-android.so)if [ -f $here/libc++_shared.so ]; then # workaround for https://github.com/android-ndk/ndk/issues/988. export ld_preload=$asan_lib $here/libc++_shared.soelse export ld_preload=$asan_libfi$@
在 main 文件夹下新建目录 resourceslib 然后将 wrap.sh 文件拷贝到相应的目录下面,最终的目录结构是这样的:
└── app └── src └── main ├── jnilibs │ ├── arm64-v8a │ │ └── libclang_rt.asan-aarch64-android.so │ ├── armeabi-v7a │ │ └── libclang_rt.asan-arm-android.so │ ├── x86 │ │ └── libclang_rt.asan-i686-android.so │ └── x86_64 │ └── libclang_rt.asan-x86_64-android.so └── resources └── lib ├── arm64-v8a │ └── wrap.sh ├── armeabi-v7a │ └── wrap.sh ├── x86 │ └── wrap.sh └── x86_64 └── wrap.sh
自此 asan 接入完成,是不是很简单?
asan 检测内存错误
这一节我们在代码中故意设置一些常见的内存错误(内存越界等)用来测试 asan 检测出来的结果是否正确。
需要注意的是,当 asan 检测出内存错误,程序就会立即 crash ,不再往下执行,log 中会出现关键字 addresssanitizer 。
堆内存溢出
static void heapbufferoverflow() { int *arr = new int[1024]; arr[0] = 11; arr[1024] = 12; logcate(heapbufferoverflow arr[0]=%d, arr[1024],arr[0], arr[1024]);}
asan 检测结果(crash log)中出现关键字 heap-buffer-overflow :
05-13 19:52:16.247 4194 4194 i com.byteflow.learnffmpeg: =================================================================05-13 19:52:16.247 4194 4194 i com.byteflow.learnffmpeg: ==4194==error: addresssanitizer: heap-buffer-overflow on address 0x004f0d5a8100 at pc 0x0074f822b648 bp 0x007ff0227a00 sp 0x007ff02279f805-13 19:52:16.247 4194 4194 i com.byteflow.learnffmpeg: write of size 4 at 0x004f0d5a8100 thread t005-13 19:52:16.257 23334 23334 d launcher_unlockanimationstatemachine: mresetidlestaterunnable05-13 19:52:16.265 4194 4194 i com.byteflow.learnffmpeg: #0 0x74f822b644 (/data/app/com.byteflow.learnffmpeg-mvg7ccqstxvnjhfo7u0xla==/lib/arm64/liblearn-ffmpeg.so+0x146644)05-13 19:52:16.265 4194 4194 i com.byteflow.learnffmpeg: #1 0x74f8229b20 (/data/app/com.byteflow.learnffmpeg-mvg7ccqstxvnjhfo7u0xla==/lib/arm64/liblearn-ffmpeg.so+0x144b20)05-13 19:52:16.265 4194 4194 i com.byteflow.learnffmpeg: #2 0x74f8229a7c (/data/app/com.byteflow.learnffmpeg-mvg7ccqstxvnjhfo7u0xla==/lib/arm64/liblearn-ffmpeg.so+0x144a7c)05-13 19:52:16.265 4194 4194 i com.byteflow.learnffmpeg: #3 0x74fdaac0a0 (/data/app/com.byteflow.learnffmpeg-mvg7ccqstxvnjhfo7u0xla==/oat/arm64/base.odex+0xb0a0)05-13 19:52:16.265 4194 4194 i com.byteflow.learnffmpeg: ........
栈内存溢出
//stack-buffer-overflow static void stackbufferoverflow() { int arr[1024]; arr[0] = 11; arr[1024] = 12; logcate(stackbufferoverflow arr[0]=%d, arr[1024],arr[0], arr[1024]); }
asan 检测结果(crash log)中出现关键字 stack-buffer-overflow :
05-13 19:54:30.371 5002 5002 i com.byteflow.learnffmpeg: =================================================================05-13 19:54:30.371 5002 5002 i com.byteflow.learnffmpeg: ==5002==error: addresssanitizer: stack-buffer-overflow on address 0x007ff02278c0 at pc 0x0074f8bd6640 bp 0x007ff0226890 sp 0x007ff022688805-13 19:54:30.372 5002 5002 i com.byteflow.learnffmpeg: write of size 4 at 0x007ff02278c0 thread t005-13 19:54:30.389 5002 5002 i com.byteflow.learnffmpeg: #0 0x74f8bd663c (/data/app/com.byteflow.learnffmpeg-aalgh4g_-2c2wc7sx0ibag==/lib/arm64/liblearn-ffmpeg.so+0x14663c)05-13 19:54:30.389 5002 5002 i com.byteflow.learnffmpeg: #1 0x74f8bd4a20 (/data/app/com.byteflow.learnffmpeg-aalgh4g_-2c2wc7sx0ibag==/lib/arm64/liblearn-ffmpeg.so+0x144a20)05-13 19:54:30.389 5002 5002 i com.byteflow.learnffmpeg: #2 0x74f8bd497c (/data/app/com.byteflow.learnffmpeg-aalgh4g_-2c2wc7sx0ibag==/lib/arm64/liblearn-ffmpeg.so+0x14497c)......
使用已释放的指针
//heap-use-after-freestatic void useafterfree() { int *arr = new int[1024]; arr[0] = 11; delete [] arr; logcate(useafterfree arr[0]=%d, arr[1024],arr[0], arr[1024]);}
asan 检测结果(crash log)中出现关键字 heap-use-after-free :
05-13 1944.430 5235 5235 i com.byteflow.learnffmpeg: =================================================================05-13 1944.430 5235 5235 i com.byteflow.learnffmpeg: ==5235==error: addresssanitizer: heap-use-after-free on address 0x004f0d5a7100 at pc 0x0074f7ac945c bp 0x007ff02279d0 sp 0x007ff02279c805-13 19:56:44.430 5235 5235 i com.byteflow.learnffmpeg: read of size 4 at 0x004f0d5a7100 thread t005-13 19:56:44.447 5235 5235 i com.byteflow.learnffmpeg: #0 0x74f7ac9458 (/data/app/com.byteflow.learnffmpeg-w2wnukkplej7i4_8_oj3sw==/lib/arm64/liblearn-ffmpeg.so+0x146458)05-13 19:56:44.448 5235 5235 i com.byteflow.learnffmpeg: #1 0x74f7ac7920 (/data/app/com.byteflow.learnffmpeg-w2wnukkplej7i4_8_oj3sw==/lib/arm64/liblearn-ffmpeg.so+0x144920)05-13 19:56:44.448 5235 5235 i com.byteflow.learnffmpeg: #2 0x74f7ac787c (/data/app/com.byteflow.learnffmpeg-w2wnukkplej7i4_8_oj3sw==/lib/arm64/liblearn-ffmpeg.so+0x14487c)......
局部变量的作用域之外使用其指针
//stack-use-after-scopestatic int *p;static void useafterscope(){ { int a = 0; p = &a; } *p = 1111; logcate(useafterscope *p=%d,*p);}
asan 检测结果(crash log)中出现关键字 stack-use-after-scope :
https://developer.android.com/ndk/guides/asan#cmake05-13 2019.447 5907 5907 i com.byteflow.learnffmpeg: =================================================================05-13 2019.447 5907 5907 i com.byteflow.learnffmpeg: ==5907==error: addresssanitizer: stack-use-after-scope on address 0x007ff02279a0 at pc 0x0074f8236438 bp 0x007ff0227970 sp 0x007ff022796805-13 20:01:19.448 5907 5907 i com.byteflow.learnffmpeg: write of size 4 at 0x007ff02279a0 thread t005-13 20:01:19.462 23334 23334 d launcher_unlockanimationstatemachine: mresetidlestaterunnable05-13 20:01:19.464 5907 5907 i com.byteflow.learnffmpeg: #0 0x74f8236434 (/data/app/com.byteflow.learnffmpeg-jx_xi14rwghag_vz9kwxya==/lib/arm64/liblearn-ffmpeg.so+0x146434)05-13 20:01:19.465 5907 5907 i com.byteflow.learnffmpeg: #1 0x74f8234860 (/data/app/com.byteflow.learnffmpeg-jx_xi14rwghag_vz9kwxya==/lib/arm64/liblearn-ffmpeg.so+0x144860)05-13 20:01:19.465 5907 5907 i com.byteflow.learnffmpeg: #2 0x74f82347bc (/data/app/com.byteflow.learnffmpeg-jx_xi14rwghag_vz9kwxya==/lib/arm64/liblearn-ffmpeg.so+0x1447bc)05-13 20:01:19.465 5907 5907 i com.byteflow.learnffmpeg: #3 0x74fdabe0a0 (/data/app/com.byteflow.learnffmpeg-jx_xi14rwghag_vz9kwxya==/oat/arm64/base.odex+0xb0a0).....
重复释放指针
//double-freestatic void doublefree() { int *arr = new int[1024]; arr[0] = 11; delete [] arr; delete [] arr; logcate(useafterfree arr[0]=%d,arr[0]);}
asan 检测结果(crash log)中出现关键字 double-free :
05-13 20:02:16.474 6102 6102 i com.byteflow.learnffmpeg: =================================================================05-13 20:02:16.475 6102 6102 i com.byteflow.learnffmpeg: ==6102==error: addresssanitizer: attempting double-free on 0x004f0d5a7100 in thread t0:05-13 20:02:16.492 6102 6102 i com.byteflow.learnffmpeg: #0 0x74f9f2b7b0 (/data/app/com.byteflow.learnffmpeg-kjj44nzxl-eya06gf3e2ma==/lib/arm64/libclang_rt.asan-aarch64-android.so+0xd57b0)05-13 20:02:16.492 6102 6102 i com.byteflow.learnffmpeg: #1 0x74f88cd210 (/data/app/com.byteflow.learnffmpeg-kjj44nzxl-eya06gf3e2ma==/lib/arm64/liblearn-ffmpeg.so+0x146210)05-13 20:02:16.492 6102 6102 i com.byteflow.learnffmpeg: #2 0x74f88cb720 (/data/app/com.byteflow.learnffmpeg-kjj44nzxl-eya06gf3e2ma==/lib/arm64/liblearn-ffmpeg.so+0x144720)05-13 20:02:16.492 6102 6102 i com.byteflow.learnffmpeg: #3 0x74f88cb67c (/data/app/com.byteflow.learnffmpeg-kjj44nzxl-eya06gf3e2ma==/lib/arm64/liblearn-ffmpeg.so+0x14467c)......
asan 基本上可以覆盖到常见的内存错误问题,还有其他情况就不一一展示了。
用户的体验感对于区块链来说是怎样的地位
PCBA单板工艺应变测试方案
江苏移动与金鹰集团在南京开展了全省首个5G智慧购物中心建设
数字图像处理的硬件
无限反射镜如何工作?如何制作一个RGB的无限反射镜
基于编译器用于快速检测原生代码中内存错误的ASan
无人机+人工智能技术,英特尔为保护箭扣长城提供精准数据
2019年12月光伏行业有哪些新政策出台
丰唐物联李海东:智能家居应抓住消费者的刚性需求
三星将向谷歌提供定制的5nm Exynos芯片
全球首家全民所有制的数字资产交易平台HiEX介绍
浅谈智能语音楼宇对讲系统
配有Type-C口的新安卓手机或将支持USB-PD快充
海南电网公司推出了个性化智能电网大数据分析应用数字电网平台
雷达液位计的天线型式有哪些
Celgard和Farasis达成和解
OLED电视将迎爆发期 京东携手LGD弥补线上空白
音叉液位开关的常见故障及处理方法详细讲解
关于学习各类仪表的选型要点梳理
智能锁指纹头放置位置的类型,以及各自的优缺点