HWAddress Sanitizer

從 NDK r21 和 Android 10 (API 級別 29) 開始,Android NDK 支援 HWAddress Sanitizer (又稱為 HWASan)。HWASan 僅適用於 64 位元 Arm 裝置。

HWASan 是一款與 ASan 相似的記憶體錯誤偵測工具。相較於傳統 ASan,HWASan 具備以下特點:

  • 額外占用的 CPU 資源相近 (約 2 倍)
  • 額外占用程式碼的空間相近 (40 - 50%)
  • 額外占用的 RAM 大幅減少 (10% - 35%)

HWASan 能夠偵測 ASan 可偵測到的錯誤:

  • 堆疊和堆積緩衝區溢位或反向溢位
  • 釋放後的堆積使用情況
  • 超出範圍的堆疊使用情況
  • 重複釋放或錯誤釋放

此外,HWASan 也能偵測:

  • 傳回後堆疊的使用情況

範例應用程式

範例應用程式說明如何為 HWASan 設定建構變化版本

建構

如要使用 HWAddress Sanitizer 建構應用程式的原生 (JNI) 程式碼,請執行以下操作:

ndk-build

Application.mk 檔案中:

APP_STL := c++_shared # Or system, or none, but not c++_static.
APP_CFLAGS := -fsanitize=hwaddress -fno-omit-frame-pointer
APP_LDFLAGS := -fsanitize=hwaddress

CMake

在模組的 build.gradle 檔案中:

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                # Can also use system or none as ANDROID_STL, but not c++_static.
                arguments "-DANDROID_STL=c++_shared"
            }
        }
    }
}

針對 CMakeLists.txt 中的每個目標:

target_compile_options(${TARGET} PUBLIC -fsanitize=hwaddress -fno-omit-frame-pointer)
target_link_options(${TARGET} PUBLIC -fsanitize=hwaddress)

如果使用 NDK 27 以上版本,也可以在 build.gradle 中使用以下項目 而且無需變更 CMakeLists.txt:

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                arguments "-DANDROID_SANITIZE=hwaddress"
            }
        }
    }
}

這在使用 ANDROID_USE_LEGACY_TOOLCHAIN_FILE=false 時無法運作。

Android 14 以上版本:新增 wrap.sh

如果執行的是 Android 14 以上版本,可以在任何 Android 裝置上使用 wrap.sh 指令碼執行可偵錯應用程式。如果選擇按照這些步驟操作,請略過這個步驟。 設定操作說明

按照操作說明封裝 wrap.sh 指令碼,為 arm64-v8a 新增下列 wrap.sh 指令碼。

#!/system/bin/sh
LD_HWASAN=1 exec "$@"

執行

如果執行的是 Android 14 以下版本,或並未新增 wrap.sh 指令碼,按照設定操作說明操作,再執行應用程式。

照常執行應用程式。偵測到記憶體錯誤時,應用程式當機且 SIGABRT 並將詳細訊息輸出至 logcat。訊息副本 您可以在 /data/tombstones 下的檔案中找到,如下所示:

ERROR: HWAddressSanitizer: tag-mismatch on address 0x0042a0826510 at pc 0x007b24d90a0c
WRITE of size 1 at 0x0042a0826510 tags: 32/3d (ptr/mem) in thread T0
    #0 0x7b24d90a08  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x2a08)
    #1 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)
    #2 0x7b8f1db364  (/apex/com.android.art/lib64/libart.so+0x18f364)
    #3 0x7b8f2ad8d4  (/apex/com.android.art/lib64/libart.so+0x2618d4)

0x0042a0826510 is located 0 bytes to the right of 16-byte region [0x0042a0826500,0x0042a0826510)
allocated here:
    #0 0x7b92a322bc  (/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so+0x212bc)
    #1 0x7b24d909e0  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x29e0)
    #2 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)

訊息後面可能會附加額外的偵錯資訊,包括應用程式正在使用的執行緒清單、鄰近記憶體配置的標記和 CPU 註冊值。

如要進一步瞭解 HWASan 錯誤訊息,請參閱「瞭解 HWASan 報告」一文。

建構指令列執行檔

您可以在 Android 14 及 更新。您可以按照建構中所述的相同設定,針對 ndk-build 或 CMake。將執行檔推送至裝置 搭載 Android 14 以上版本,並透過殼層照常執行。

如果您使用 libc++,請務必使用共用的 STL,並推送至 然後將 LD_LIBRARY_PATH 設為包含該裝置的目錄。 執行二進位檔

如果您使用的不是 Gradle,請參閱 NDK 說明文件,瞭解如何從 加入 CMake 指令列 ndk-build

Android 13 以下版本:設定

如果裝置搭載的是 Android 14 以上版本,可以略過這個步驟,並按照下方「建構」一節中使用 wrap.sh 的操作說明進行。您也可以選擇按照本節所述操作,略過下方使用 wrap.sh 的操作說明。

在 Android 14 之前,HWASan 應用程式需要 Android HWASan 版本,才能順利執行。 您可以將預先建構的 HWASan 映像檔刷新至支援的 Pixel 裝置。您可在 ci.android.com 中找到這些版本,只要按一下所需確切版本的正方形,就能取得刷新版本連結。為此,您需要知道手機的產品代號

刷新裝置版本

更簡單的做法是直接造訪 flash.android.com,因為在這個作業流程中,系統會從偵測裝置開始,並僅列出您可以使用的建構內容。下圖說明這項工具的設定流程。

在裝置上啟用開發人員模式,然後透過 USB 傳輸線將裝置連接至電腦。按一下「Add new device」,從對話方塊中選取您的裝置,然後按一下「Connect」

偵測要刷新的裝置 選取要連接的裝置

連接裝置後,按一下該裝置即可設定版本。 在「Select a build ID」方塊中,選取 aosp-master-with-phones-throttled 分支版本,讓系統自動為已連接的裝置選擇正確的映像檔。

選取要刷新的裝置 確認刷新選項並刷新裝置

按一下「Install」刷新裝置。

如要進一步瞭解必要的設定,請參閱 Android Flash Tool 說明文件。另外,您也可以參閱 Android 開放原始碼計畫說明文件,瞭解如何從來源建構 HWASan 映像檔。