地址消毒劑

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

ASan 檢測:

  • 堆棧和堆緩衝區上溢/下溢
  • 免費後堆使用
  • 堆棧使用範圍外
  • 雙免/野生免

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

機器人10和上AArch64支持AOSP主分支硬件加速牙山(花山) ,具有較低的開銷RAM類似的工具和更大範圍內檢測的錯誤。除了 ASan 檢測到的錯誤之外,HWASan 還會檢測返回後的堆棧使用情況。

HWASan 具有類似的 CPU 和代碼大小開銷,但 RAM 開銷要小得多 (15%)。 HWASan 是不確定的。只有 256 個可能的標籤值,因此遺漏任何錯誤的概率為 0.4%。 HWASan 沒有 ASan 用於檢測溢出的大小有限的紅色區域和用於檢測釋放後使用的容量有限的隔離區,因此 HWASan 與溢出的大小或內存在多長時間前被釋放無關。這使得 HWASan 比 ASan 更好。你可以閱讀更多關於花山的設計或對使用Android上花山

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

本文檔描述瞭如何使用 ASan 構建和運行部分/全部 Android。如果你正在構建一個SDK / NDK應用與阿三,看地址消毒劑來代替。

使用 ASan 清理單個可執行文件

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

當檢測到一個錯誤,阿三打印冗長兩者報告給標準輸出並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

驗證進程正在使用的庫從/system/lib/asan當存在於由讀取/proc/$PID/maps 。如果不是,您可能需要禁用 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 報告包含對二進製文件和共享庫中偏移量的引用。有兩種方式獲取源文件和行信息:

  • 確保llvm-symbolizer二進制的存在/system/binllvm-symbolizer是從來源內置third_party/llvm/tools/llvm-symbolizer
  • 過濾通過的報告external/compiler-rt/lib/asan/scripts/symbolize.py腳本。

第二種方法可以提供更多的數據(即, file:line位置)的,因為在主機上象徵庫的可用性。

應用程序中的 ASan

ASan 無法查看 Java 代碼,但它可以檢測 JNI 庫中的錯誤。對於這一點,你需要建立與阿三,在這種情況下是可執行文件/system/bin/app_process( 32|64 )這會同時在設備上的所有應用程序中啟用 ASan,這是一個沉重的負載,但具有 2 GB RAM 的設備應該能夠處理此問題。

添加LOCAL_SANITIZE:=addressapp_process構建規則frameworks/base/cmds/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/system/bin/asan/app_process ,這是建立與阿三。它還添加/system/lib/asan在動態庫搜索路徑的開始。從這種方式牙山儀表庫/system/lib/asan是優選的,以正常的庫/system/lib與運行時asanwrapper

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

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 (第一補充調用),和在牙山儀表/data/asan/lib (所述第二補充調用)。第二個構建中的可執行文件會覆蓋第一個構建中的可執行文件。牙山儀表可執行文件得到了包括不同的庫搜索路徑/data/asan/lib/system/lib通過使用/system/bin/linker_asanPT_INTERP

編譯系統則會覆蓋中間目標目錄時的$SANITIZE_TARGET值發生了變化。這力量,同時也保留下安裝的二進制文件重建所有目標/system/lib

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

  • 靜態鏈接的可執行文件
  • LOCAL_CLANG:=false目標
  • LOCAL_SANITIZE:=false不ASan'd為SANITIZE_TARGET=address

像這些可執行文件被跳過在SANITIZE_TARGET構建,並從第一化妝調用的版本留在/system/bin

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

支持文檔