ASan和HWAsan在Android中使用

1. ASan和HWAsan比较

ASan

HWASan

全称

Address Sanitizer

Hardware-assisted AddressSanitizer

版本

可以在32位和64位的x86、x86-64上。

从API 27(Android O MR 1)开始,Android NDK 可支持ASAN。

在Android 11 之后的AOSP master中,弃用了arm64 上的平台开发ASan,改为使用HWASan。

只在Android 10 及以上版本有效,且只使用于AArch64硬件平台。

检测bugs

扫描二维码关注公众号,回复: 15507799 查看本文章

Stack and heap buffer overflow/underflow

Heap use after free

Stack use outside scope

Double free/wild free

Stack and heap buffer overflow/underflow

Heap use after free

Stack use outside scope

Double free/wild free

stack use after return

要求

CPU 开销(~ 2倍)

代码大小开销(50% ~ 2倍)

内存开销(~ 2倍)

CPU 开销(~ 2倍)

代码大小开销(40% ~ 50%)

内存开销(10% ~ 35%)

原理

使用shadow memory(内存的一个区域)内存状态进行标记,如free掉的内存在shadow中标记为0xfd,已经申请的内存,前后存在安全区标记为0xfa

AArch64是64位的架构,一个64bit的指针值,其中真正用于寻址的只有低48位。

AArch64拥有地址标记(Address tagging, or top-byte-ignore)的特性,它表示允许软件使用64bit指针值的高8位开发特定功能。HWASAN用这8bit来存储一块内存区域的标签(tag)。

缺点

1)ASAN的运行是需要消耗memory和CPU资源的,此外它也会增加代码大小。它的性能相比于之前的工具确实有了质的提升,但仍然无法适用于某些压力测试场景,尤其是需要全局打开的时候。这一点在Android上尤为明显,每当我们想要全局打开ASAN调试某些奇葩问题时,系统总会因为负载过重而跑不起来;

2)对于 free 的内存标记存在隔离时间,即 free 的区域一段时间后重新分配其他所有者,此时原持有者访问不会报错;

3)对应flow的安全区总归有大小,如果踩踏过了安全区,同样不会报错;

1)可移植性差,只用于64位平台;

2)需要对Linux Kernel做一些改动以支持工具

3)对于所有错误的检测将有一定概率false negative(漏掉一些真实的错误),概率为1/256。原因是tag的生成只能从256(2的8次方)个数中选一个,因此不同地址的tag将有可能相同

优点

比较ASan:

1)不再需要安全区来检测buffer overflow,既极大地降低了工具对于内存的消耗,也不会出现ASAN中某些overflow检测不到的情况;

2)不再需要隔离区来检测UseAfterFree,因此不会出现ASAN中某些UseAfterFree检测不到的情况

2. ASan 编译

2.1 整体编译

m -j16
SANITIZE_TARGET=address m -j16

注意,这里需要两次编译。第一次是为了在 /system/lib 中编译常规库,第二次编译是为了在 /system/lib/asan 中进行 ASan 插桩。 

需要刷新system、vendor、system分区,因为上述第 2 步编译ASAN库在/data/asan目录下。

2.2 单独编译

Android.mk
LOCAL_SANITIZE := address
 
Android.bp
sanitize: { address: true }

2.2.1 编译共享库

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

这样一来,系统会将库放到 /system/lib/asan 中而非 /system/lib 中。但是需要指定库的路径方便链接:

LD_LIBRARY_PATH=/system/lib/asan
或
setenv LD_LIBRARY_PATH /system/lib/asan

通过 /proc/PID/maps 验证使用的库是否来自 /system/lib/asan(如果此库存在),如果不是,可能需要停用 selinux:

adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID
# if it is a system service, or may be adb shell stop; adb shell start.

2.3 无法使用ASan构建

有些目标无法使用ASan 构建:

  • 静态关联的可执行文件
  • LOCA_CLANG:=false 目标
  • 不会针对 SANITIZE_TARGET=address进行ASan 操作的LOCAL_SANITIZE:=false 的目标

在 SANITIZE_TARGET build 中,系统会跳过此类可执行文件,且会将第一个make 调用中构建的版本留在 /system/bin 中。

3. HWASan 编译

3.1 整体编译

export SANITIZE_TARGET=hwaddress
m -j16

与ASan 不同,HWASan 无需构建两次,只需增量构建,没有特殊的刷写指令,不需要擦除,支持静态可执行文件,并且可以跳过除 libc 之外的任何库的排错。

通过环境变量 SANITIZE_TARGET 指定排错(sanitizer)。

3.2 单独编译bin/lib

在编译脚本中增加如下参数即可

Android.mk
LOCAL_SANITIZE:= hwaddress
 
Android.bp
sanitize: { hwaddress: true }

如果需要在整体构建中,去除某模块HWASAN的编译

Android.mk
LOCAL_NOSANITIZE := hwaddress
 
Android.bp
sanitize: { hwaddress: false}

4. 解析

由于版本默认库或者bin是stripped过的,因此无法解析,如

==4415==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x003a861bb057 at pc 0x00775f3c664c bp 0x007fd0f434b0 sp 0x007fd0f42c90
READ of size 8 at 0x003a861bb057 thread T0
    #0 0x775f3c6648  (/system/lib64/libclang_rt.asan-aarch64-android.so+0x72648)
    #1 0x775f3c6ff8  (/system/lib64/libclang_rt.asan-aarch64-android.so+0x72ff8)
    #2 0x59861bf0a8  (/vendor/bin/qrtr-lookup+0x20a8)
    #3 0x775f72488c  (/apex/com.android.runtime/lib64/bionic/libc.so+0x4988c)
 
0x003a861bb057 is located 0 bytes to the right of 7-byte region [0x003a861bb050,0x003a861bb057)
allocated by thread T0 here:
    #0 0x775f3f6088  (/system/lib64/libclang_rt.asan-aarch64-android.so+0xa2088)
    #1 0x59861bf094  (/vendor/bin/qrtr-lookup+0x2094)
    #2 0x775f72488c  (/apex/com.android.runtime/lib64/bionic/libc.so+0x4988c)
    #3 0x59861bf044  (/vendor/bin/qrtr-lookup+0x2044)
    #4 0x7760b9fbb4  (/vendor/bin/qrtr-lookup+0x4cbb4)
 
SUMMARY: AddressSanitizer: heap-buffer-overflow (/system/lib64/libclang_rt.asan-aarch64-android.so+0x72648)
Shadow bytes around the buggy address:
  0x001750c375b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x001750c375c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x001750c375d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x001750c375e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x001750c375f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x001750c37600: fa fa 00 fa fa fa 00 fa fa fa[07]fa fa fa fa fa
  0x001750c37610: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x001750c37620: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x001750c37630: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x001750c37640: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x001750c37650: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==4415==ABORTING

4.1 直接解析

  • push llvm-symbolizer到 system/bin下
    • 如 push到其他目录,需要保证该目录在PATH下或者设置环境变量export ASAN_SYMBOLIZER_PATH=/system/bin/llvm-symbolizer
    •  llvm-symbolizer路径:android\vendor\qcom\proprietary\llvm-arm-toolchain-ship\10.0\aarch64-linux-android\bin\llvm-symbolizer
  • push 对应模块带有symbols的库或者bin到对应目录,源文件位于android\out\target\product\shift\symbols

上述工作完成后,当ASAN / HWASan 检查到错误后,相关结果会自动解析到logcat 或screen 上,包括具体的函数,行号等等

==================================================================
==6646==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x003c452d8057 at pc 0x00749d4d364c bp 0x007fd09ac530 sp 0x007fd09abd10
READ of size 8 at 0x003c452d8057 thread T0
    #0 0x749d4d3648 in printf_common(void*, char const*, std::__va_list) /out/llvm-project/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors_format.inc:547:9
    #1 0x749d4d3ff8 in __interceptor_vprintf /out/llvm-project/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:1645:1
    #2 0x749d4d3ff8 in printf /out/llvm-project/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:1703:1
    #3 0x5b452dc1a8 in main vendor/qcom/proprietary/qmi-framework/qrtr/src/lookup.c:143:5
    #4 0x749d3a888c in __libc_init (/apex/com.android.runtime/lib64/bionic/libc.so+0x4988c)
 
0x003c452d8057 is located 0 bytes to the right of 7-byte region [0x003c452d8050,0x003c452d8057)
allocated by thread T0 here:
    #0 0x749d503088 in malloc /out/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:145:3
    #1 0x5b452dc194 in main vendor/qcom/proprietary/qmi-framework/qrtr/src/lookup.c:142:21
    #2 0x749d3a888c in __libc_init (/apex/com.android.runtime/lib64/bionic/libc.so+0x4988c)
    #3 0x5b452dc044 in _start_main bionic/libc/arch-common/bionic/crtbegin.c:45:3
    #4 0x749eb2dbb4  (/vendor/bin/qrtr-lookup+0x4cbb4)
 
SUMMARY: AddressSanitizer: heap-buffer-overflow /out/llvm-project/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors_format.inc:547:9 in printf_common(void*, char const*, std::__va_list)
Shadow bytes around the buggy address:
  0x001788a5afb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x001788a5afc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x001788a5afd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x001788a5afe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x001788a5aff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x001788a5b000: fa fa 00 fa fa fa 00 fa fa fa[07]fa fa fa fa fa
  0x001788a5b010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x001788a5b020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x001788a5b030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x001788a5b040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x001788a5b050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==6646==ABORTING
Aborted

4.2 编译带symbols 的lib/bin(单独模块进行排错)

  • ASAN/HWASan,push 额外libc++_shared.so
    • 命令 adb push android\prebuilts\ndk\r21\sources\cxx-stl\llvm-libc++\libs\arm64-v8a\libc++_shared.so /system/lib64
  • HWASan 需要额外push  libclang_rt.hwasan-aarch64-android.so库
  • push llvm-symbolizer到system/bin下
  • 模块的编译脚本携带如下参数
Android.mk
LOCAL_STRIP_MODULE :=false
Android.bp
strip :{keep_symbols: true,},

可得到如下信息,得到具体的函数

==10804==ERROR: HWAddressSanitizer: invalid-free on address 0x0038f7647040 at pc 0x0072ed942bb8
tags: 1a/96 (ptr/mem)
    #0 0x72ed942bb4 in __sanitizer_free /out/llvm-project/compiler-rt/lib/hwasan/hwasan_interceptors.cpp:108:3
    #1 0x57f764b0b8 in main (/vendor/bin/qrtr-lookup+0x20b8)
    #2 0x72ed831174 in __libc_init (/apex/com.android.runtime/lib64/bionic/libc.so+0x4e174)
    #3 0x57f764b044 in _start_main (/vendor/bin/qrtr-lookup+0x2044)
    #4 0x72eefd3bb4  (/vendor/bin/qrtr-lookup+0x4cbb4)
 
[0x0038f7647040,0x0038f7647060) is a small unallocated heap chunk; size: 32 offset: 0
0x0038f7647040 is located 0 bytes inside of 7-byte region [0x0038f7647040,0x0038f7647047)
freed by thread T0 here:
    #0 0x72ed942bb4 in __sanitizer_free /out/llvm-project/compiler-rt/lib/hwasan/hwasan_interceptors.cpp:108:3
    #1 0x57f764b0b0 in main (/vendor/bin/qrtr-lookup+0x20b0)
    #2 0x72ed831174 in __libc_init (/apex/com.android.runtime/lib64/bionic/libc.so+0x4e174)
    #3 0x57f764b044 in _start_main (/vendor/bin/qrtr-lookup+0x2044)
    #4 0x72eefd3bb4  (/vendor/bin/qrtr-lookup+0x4cbb4)
 
previously allocated here:
    #0 0x72ed943084 in __sanitizer_malloc /out/llvm-project/compiler-rt/lib/hwasan/hwasan_interceptors.cpp:169:3
    #1 0x72ed826bdc in malloc (/apex/com.android.runtime/lib64/bionic/libc.so+0x43bdc)
    #2 0x57f764b094 in main (/vendor/bin/qrtr-lookup+0x2094)
    #3 0x72ed831174 in __libc_init (/apex/com.android.runtime/lib64/bionic/libc.so+0x4e174)
    #4 0x57f764b044 in _start_main (/vendor/bin/qrtr-lookup+0x2044)
    #5 0x72eefd3bb4  (/vendor/bin/qrtr-lookup+0x4cbb4)
 
hwasan_dev_note_heap_rb_distance: 1 1023
Thread: T0 0x006900002000 stack: [0x007fd2fc0000,0x007fd37c0000) sz: 8388608 tls: [0x000000000000,0x000000000000)
Memory tags around the buggy address (one tag corresponds to 16 bytes):
  0x006d8f764680: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f764690: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f7646a0: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f7646b0: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f7646c0: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f7646d0: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f7646e0: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f7646f0: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
=>0x006d8f764700: 08  00  08  00 [96] 00  00  00  00  00  00  00  00  00  00  00
  0x006d8f764710: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f764720: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f764730: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f764740: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f764750: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f764760: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f764770: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
  0x006d8f764780: 00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
  0x006d8f7646f0: ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
=>0x006d8f764700: e2  ..  7a  .. [..] ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
  0x006d8f764710: ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags
SUMMARY: HWAddressSanitizer: invalid-free /out/llvm-project/compiler-rt/lib/hwasan/hwasan_interceptors.cpp:108:3 in __sanitizer_free

4.3 host 解析

将dump信息copy进入文件dumpinfo,按照如下格式(===开头)

=================================================================
==24786==ERROR: AddressSanitizer: SEGV on unknown address 0x180001a46bc1c34 (pc 0x00761175f308 bp 0x007fc5f519b0 sp 0x007fc5f51970 T0)
==24786==The signal is caused by a READ memory access.
    #0 0x761175f308  (/system/system_ext/lib64/libimsmedia_jni.so+0x3308)
    #1 0x761175f1b8 in JNI_OnLoad (/system/system_ext/lib64/libimsmedia_jni.so+0x31b8)
    #2 0x7681c104d8 in art::JavaVMExt::LoadNativeLibrary(_JNIEnv*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, _jobject*, _jclass*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) (/apex/com.android.art/lib64/libart.so+0x5be4d8)
    #3 0x7678bf2128 in JVM_NativeLoad (/apex/com.android.art/lib64/libopenjdkjvm.so+0x8128)
    #4 0x6fba7a24  (/apex/com.android.art/javalib/arm64/boot.oat+0x80a24)

然后执行(asan_symbolize路径,android\external\compiler-rt\lib\asan\scripts)

asan_symbolize -s "$OUT/symbols"/ < ./external/compiler-rt/lib/asan/scripts/dumpinfo
#0 0x7332a21308 in _Z18load_ims_media_libPKc vendor/qcom/proprietary/commonsys/telephony-apps/ims/jni/media/ims_media_jni.cpp:477:56
   #1 0x7332a211b8 in _Z18load_ims_media_libPKc vendor/qcom/proprietary/commonsys/telephony-apps/ims/jni/media/ims_media_jni.cpp:0:0
   #2 0x73a24dc168 in _ZN3art9JavaVMExt17LoadNativeLibraryEP7_JNIEnvRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEP8_jobjectP7_jclassPS9_ art/runtime/jni/java_vm_ext.cc:1080:19
   #3 0x7399a1b16c in JVM_NativeLoad art/openjdkjvm/OpenjdkJvm.cc:333:24

5. FAQ

5.1 system 编译失败

由于当前为非动态分区,且system分区大小为1.5G,开启ASAN/HWASAN后,编译的system.img超过了1.5G,因此需要按照如下修改(后续改成动态分区则不需要修改了)

partion

vi device/jxx/shift/BoardConfig.mk
ifneq ($(strip $(BOARD_DYNAMIC_PARTITION_ENABLE)),true)
#BOARD_BUILD_SYSTEM_ROOT_IMAGE := true
BOARD_VENDORIMAGE_PARTITION_SIZE := 1043333120 #1073741824
ifeq ($(strip $(WITH_GMS)),true)
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 3093299200 #3221225472
else
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 3093299200 #1610612736
endif
BOARD_PRODUCTIMAGE_PARTITION_SIZE := 838860800
ifeq ($(ENABLE_AB), true)
AB_OTA_PARTITIONS ?= system
endif
else

5.2 Not enough space to resize partition

烧录system时,提示无足够空间,因为当前ROM是4G,而system.img超过了1.5G,烧录system时,除了system.img,还剩余空间少于1.5G,因此失败,此时需要将system和vendor打包为super.img烧录

  • super打包命令
lpmake --metadata-size 65536 --super-name super --metadata-slots 3 --virtual-ab --device super:4294967296 \
       --group qti_dynamic_partitions_a:4290772992  --group qti_dynamic_partitions_b:4290772992 \
       --partition system_a:readonly:$(get_build_var BOARD_SYSTEMIMAGE_PARTITION_SIZE):qti_dynamic_partitions_a --image system_a=$OUT/system.img \
       --partition system_b:readonly:0:qti_dynamic_partitions_b \
       --partition system_ext_a:readonly:0:qti_dynamic_partitions_a \
       --partition system_ext_b:readonly:0:qti_dynamic_partitions_b \
       --partition product_a:readonly:0:qti_dynamic_partitions_a \
       --partition product_b:readonly:0:qti_dynamic_partitions_b \
       --partition vendor_a:readonly:$(get_build_var BOARD_VENDORIMAGE_PARTITION_SIZE):qti_dynamic_partitions_a --image vendor_a=$OUT/vendor.img \
       --partition vendor_b:readonly:0:qti_dynamic_partitions_b \
       --sparse --output $OUT/super.img
  • super烧录命令
fastboot flash super super.img

5.3 userdata 烧录

由于文件系统的不同,烧录userdata会导致系统无法启动,因此在烧录特殊版本的ASAN时,不烧录userdata.img,待system、vendor烧录启动后,push $OUT/data/asan到板卡的data目录下即可

猜你喜欢

转载自blog.csdn.net/jingerppp/article/details/131322020