AddressSanitizer (ASan) 是一款以編譯器為基礎的快速工具,可用來偵測 原生程式碼的記憶體錯誤
ASan 可偵測到:
- 堆疊和堆積緩衝區溢位/反向溢位
- 釋放後的堆積使用情況
- 超出範圍的堆疊使用情況
- 重複釋放/錯誤釋放
ASan 可以在 32 位元和 64 位元 ARM 以及 x86 和 x86-64 上執行。ASan 的 CPU 負擔 約為 2 倍,程式碼大小負擔介於 50% 到 2 倍之間,會產生大量記憶體負擔 (取決於分配模式,但以 2 倍為單位)。
Android 10 和 AArch64 上的 Android 開放原始碼計畫主要分支版本 支援硬體輔助 AddressSanitizer (HWASan), 是一種類似工具,RAM 負擔較小,且在記憶體容量較大 各種錯誤HWASan 可以偵測返回後的堆疊使用行為、找出錯誤 與 ASan 偵測。
HWASan 的 CPU 和程式碼大小大致相同,但 RAM 負擔更低 (15%)。 HWASan 無法確定。標記值可能只有 256 個,因此固定為 0.4% 遺漏任何錯誤的機率HWASan 沒有 ASan 的限量紅色區域 偵測溢位和有限容量的隔離區 藉此偵測釋放後的使用情況 所以無論 HWASan 要得知溢位有多大 被取消配置。因此 HWASan 優於 ASan。如要進一步瞭解 設計 HWASan,或是有關 Android 上的 HWASan 使用方式。
ASan 可偵測堆疊/全域溢位 除了堆積溢位以外,速度飛快且記憶體負擔最低。
本文件將說明如何使用 ASan。如果您使用 ASan 建構 SDK/NDK 應用程式,請參閱 Address Sanitizer 。
使用 ASan 清理個別執行檔
新增LOCAL_SANITIZE:=address
或sanitize: { 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
若是系統 Daemon,請將下列程式碼新增到
/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/bin
中。 「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 的裝置應該就能處理這個負載
將 LOCAL_SANITIZE:=address
新增至
frameworks/base/cmds/app_process
中的 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 快閃開機和重新啟動。
使用包裝屬性
上一節的方法會將 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
,這個模型是利用
ASan。並在以下位置的開頭加上 /system/lib/asan
動態資料庫搜尋路徑以這樣 ASan 檢測
較推薦使用 /system/lib/asan
的程式庫至一般程式庫
使用 asanwrapper
執行時,在 /system/lib
中執行。
如果發現錯誤,應用程式就會當機,且報告會顯示於 。
目標
Android 7.0 以上版本支援使用 一次(如果您要建構的版本高於 Android 9,建議您選擇 HWASan)。
在同一個建構樹狀結構中執行下列指令。
make -j42
SANITIZE_TARGET=address make -j42
在這個模式下,userdata.img
包含額外的程式庫,必須
刷新到裝置上使用以下指令列:
fastboot flash userdata && fastboot flashall
這樣會建立兩組共用程式庫:一般的
/system/lib
(第一次發出叫用) 以及 ASan 檢測
/data/asan/lib
(第二次發出叫用)。從 Container Registry 提取的執行檔
第二個版本會覆寫第一個版本ASan 檢測
執行檔會取得不同的程式庫搜尋路徑
/system/lib
前 /data/asan/lib
透過使用
PT_INTERP
中有 /system/bin/linker_asan
。
當建構系統擷取到
$SANITIZE_TARGET
的值已變更。這會強制使
並保留 /system/lib
底下已安裝的二進位檔。
部分目標無法使用 ASan 建構:
- 靜態連結的執行檔
LOCAL_CLANG:=false
個目標LOCAL_SANITIZE:=false
不是「SANITIZE_TARGET=address
」的 ASan
SANITIZE_TARGET
建構作業會略過這類執行檔,而且
第一次叫用的版本會保留在 /system/bin
中。
這類程式庫是在沒有 ASan 的情況下建構。可能包含一些 ASan 所需的靜態資料庫編寫程式碼