Google致力於提高黑人社區的種族平等。 怎麼看。
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

地址消毒劑

AddressSanitizer(ASan)是基於快速編譯器的工具,用於檢測本機代碼中的內存錯誤。

ASan檢測到:

  • 堆棧緩衝區溢出/下溢
  • 免費使用後堆
  • 堆棧使用超出範圍
  • 雙人免費/野生免費

ASan可在32位和64位ARM以及x86和x86-64上運行。 ASan的CPU開銷約為2倍,代碼大小開銷在50%到2倍之間,並且內存開銷很大(取決於您的分配模式,但約為2倍)。

Android 10和AArch64上的AOSP主分支支持硬件加速的ASan(HWASan) ,這是一種類似的工具,具有較低的RAM開銷和較大範圍的檢測到的錯誤。除了ASan檢測到的錯誤外,HWASan還可以檢測返回後的堆棧使用情況。

HWASan具有相似的CPU和代碼大小開銷,但RAM開銷要小得多(15%)。 HWASan是不確定的。僅有256個可能的標籤值,因此丟失任何錯誤的機率只有0.4%。 HWASan沒有ASan的大小有限的紅色區域用於檢測溢出,而容量有限的隔離區則用於檢測免費使用後使用,因此HWASan無關緊要的是溢出有多大或內存分配了多長時間。這使得HWASan優於ASan。您可以閱讀有關HWASan設計在Android上使用HWASan的更多信息

除了堆溢出以外,ASan還檢測堆棧/全局溢出,並且以最小的內存開銷實現快速。

本文檔介紹瞭如何使用ASan構建和運行Android的部分/全部。如果您要使用ASan構建SDK / NDK應用,請參閱Address Sanitizer

使用ASan消毒單個可執行文件

LOCAL_SANITIZE:=addresssanitize: { address: true }到可執行文件的生成規則。您可以在代碼中搜索現有示例,或查找其他可用的消毒劑。

當檢測到錯誤時,ASan會將詳細報告打印到標準輸出和logcat ,然後使該過程崩潰。

使用ASan消毒共享庫

由於ASan的工作方式,使用ASan構建的庫只能由使用ASan構建的可執行文件使用。

要清理在多個可執行文件中使用的共享庫,並不是所有的可執行文件都是使用ASan構建的,您需要該庫的兩個副本。推薦的方法是在相關模塊的Android.mk添加以下內容:

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

這會將庫放在/system/lib/asan而不是/system/lib 。然後,使用以下命令運行可執行文件:

LD_LIBRARY_PATH=/system/lib/asan

對於系統守護程序,將以下內容添加到/init.rc/init.$device$.rc的相應部分。

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.

更好的堆棧跟踪

ASan使用快速的,基於幀指針的展開器為程序中的每個內存分配和釋放事件記錄堆棧跟踪。大多數Android是在沒有框架指針的情況下構建的。結果,您通常只會得到一兩個有意義的幀。要解決此問題,請使用ASan(推薦!)或使用以下方法重建庫:

LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm

或在流程環境中設置ASAN_OPTIONS=fast_unwind_on_malloc=0 。後者可能會佔用大量CPU,具體取決於負載。

符號化

最初,ASan報告包含對二進製文件和共享庫中的偏移量的引用。有兩種獲取源文件和行信息的方法:

  • 確保/system/bin存在llvm-symbolizer二進製文件。 llvm-symbolizer是從third_party/llvm/tools/llvm-symbolizer源構建的。
  • 通過external/compiler-rt/lib/asan/scripts/symbolize.py腳本過濾報告。

由於主機上符號庫的可用性,第二種方法可以提供更多數據(即file:line位置)。

應用中的ASan

ASan無法查看Java代碼,但它可以檢測JNI庫中的錯誤。為此,您需要使用ASan構建可執行文件,在本例中為/system/bin/app_process( 32|64 ) 。這可以同時在設備上的所有應用程序中啟用ASan,這是一個沉重的負擔,但是具有2 GB RAM的設備應該可以處理此問題。

frameworks/base/cmds/app_process LOCAL_SANITIZE:=address添加到app_process構建規則。現在,請忽略同一文件中的app_process__asan目標(如果在您閱讀本文時該目標仍然存在)。

編輯service zygote相應的部分system/core/rootdir/init.zygote( 32|64 ).rc文件到下面的行添加到含有縮進的行塊class main ,也以相同的量縮進:

    setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
    setenv ASAN_OPTIONS allow_user_segv_handler=true

構建,adb同步,fastboot閃存引導和重新引導。

使用wrap屬性

上一節中的方法將ASan放入系統中的每個應用程序(實際上是Zygote進程的每個後代)。使用ASan可以僅運行一個(或幾個)應用程序,以犧牲一些內存開銷來降低應用程序的啟動速度。

可以通過自動啟動應用來完成wrap.屬性。以下示例在ASan下運行Gmail應用程序:

adb root
adb shell setenforce 0  # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"

在這種情況下, asanwrapper /system/bin/app_process asanwrapper重寫為/system/bin/app_process /system/bin/asan/app_process ,它是使用ASan構建的。它還在動態庫搜索路徑的開頭添加了/system/lib/asan 。這樣,在與asanwrapper運行時, /system/lib/asan asan中由ASan插入的庫將優先於/system/lib普通庫。

如果發現錯誤,則應用程序將崩潰,並且報告將被打印到日誌中。

SANITIZE_TARGET

Android 7.0及更高版本支持立即使用ASan構建整個Android平台。 (如果要構建高於Android 9的發行版,則HWASan是更好的選擇。)

在同一構建樹中運行以下命令。

make -j42
SANITIZE_TARGET=address make -j42

在這種模式下, userdata.img包含額外的庫,並且也必須閃存到設備中。使用以下命令行:

fastboot flash userdata && fastboot flashall

這將構建兩套共享庫: /system/lib normal(第一個make調用)和ASan-instrumented在/data/asan/lib (第二個make調用)。來自第二個版本的可執行文件將覆蓋來自第一個版本的可執行文件。牙山儀表可執行文件得到了包括不同的庫搜索路徑/data/asan/lib/system/lib通過使用/system/bin/linker_asanPT_INTERP

$SANITIZE_TARGET值更改時,構建系統將替換中間對象目錄。這將強制所有目標的重建,同時將已安裝的二進製文件保留在/system/lib

某些目標無法使用ASan構建:

  • 靜態鏈接的可執行文件
  • LOCAL_CLANG:=false目標
  • LOCAL_SANITIZE:=false不能用於SANITIZE_TARGET=address

這樣的可執行文件在SANITIZE_TARGET構建中被跳過,並且第一次make調用的版本保留在/system/bin

這樣的庫是在沒有ASan的情況下構建的。它們可以包含它們依賴的靜態庫中的一些ASan代碼。

支持文檔