LLVM 消毒劑

LLVM 是用於構建 Android 的編譯器基礎架構,包含多個執行靜態和動態分析的組件。在這些組件中,清理程序(特別是 AddressSanitizer 和 UndefinedBehaviorSanitizer)可廣泛用於分析 Android。 Sanitizer 是包含在 external/compiler-rt 中的基於編譯器的檢測組件,可在開發和測試期間使用,以消除錯誤並使 Android 變得更好。 Android 當前的清理程序集可以發現和診斷許多內存濫用錯誤和潛在危險的未定義行為。

Android 版本的最佳實踐是在啟用 sanitizer 的情況下啟動和運行,例如 AddressSanitizer 和 UndefinedBehaviorSanitizer。本頁介紹了 AddressSanitizer、UndefinedBehaviorSanitizer 和 KernelAddressSanitizer,展示瞭如何在 Android 構建系統中使用它們,並提供了示例 Android.mk 和 Android.bp 文件,這些文件在啟用這些清理程序的情況下構建本機組件。

AddressSanitizer

AddressSanitizer (ASan) 是一種基於編譯器的檢測功能,可在運行時檢測 C/C++ 代碼中的多種類型的內存錯誤。 ASan 可以檢測多種類型的內存錯誤,包括:

  • 越界內存訪問
  • 雙人免費
  • 免後使用

Android 允許在完整構建級別和應用級別使用 asanwrapper 進行ASan檢測。

AddressSanitizer 結合了所有與內存相關的函數調用(包括 alloca、malloc 和 free)的檢測,並使用在讀取或寫入時觸發 ASan 回調的內存填充所有變量和分配的內存區域。

該儀器讓 ASan 檢測無效的內存使用錯誤,包括雙重釋放和使用後範圍、返回和釋放,而內存區域填充檢測越界讀取或寫入。如果發生對該填充區域的讀取或寫入,ASan 會捕獲它並輸出信息以幫助診斷內存違規,包括調用堆棧、影子內存映射、內存違規的類型、讀取或寫入的內容、導致違規和內存內容。

pixel-xl:/ # sanitizer-status                                                                                            
=================================================================
==14164==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x0032000054b0 at pc 0x005df16ffc3c bp 0x007fc236fdf0 sp 0x007fc236fdd0
WRITE of size 1 at 0x0032000054b0 thread T0
    #0 0x5df16ffc3b in test_crash_malloc sanitizer-status/sanitizer-status.c:36:13
    #1 0x5df17004e3 in main sanitizer-status/sanitizer-status.c:76:7
    #2 0x794cf665f3 in __libc_init (/system/lib64/libc.so+0x1b5f3)
    #3 0x5df16ffa53 in do_arm64_start (/system/bin/sanitizer-status+0xa53)

0x0032000054b0 is located 0 bytes to the right of 32-byte region [0x003200005490,0x0032000054b0)
allocated by thread T0 here:
    #0 0x794d0bdc67 in malloc (/system/lib64/libclang_rt.asan-aarch64-android.so+0x74c67)
    #1 0x5df16ffb47 in test_crash_malloc sanitizer-status/sanitizer-status.c:34:25
    #2 0x5df17004e3 in main sanitizer-status/sanitizer-status.c:76:7
    #3 0x794cf665f3 in __libc_init (/system/lib64/libc.so+0x1b5f3)
    #4 0x5df16ffa53 in do_arm64_start (/system/bin/sanitizer-status+0xa53)
    #5 0x794df78893  (<unknown module>)

SUMMARY: AddressSanitizer: heap-buffer-overflow sanitizer-status/sanitizer-status.c:36:13 in test_crash_malloc

有時,錯誤發現過程似乎是不確定的,特別是對於需要特殊設置或更高級技術的錯誤,例如堆啟動或競爭條件利用。這些錯誤中有許多並不是立即顯現出來的,並且可能會使數千條指令遠離作為實際根本原因的內存違規。 ASan 檢測所有與內存相關的函數,並在不觸發 ASan 回調的情況下使用無法訪問的區域填充數據。這意味著內存違規會在它們發生的那一刻被捕獲,而不是等待導致崩潰的損壞。這在錯誤發現和根本原因診斷中非常有用。

為了驗證 ASAN 在目標設備上是否正常運行,Android 包含了 asan_test 可執行文件。 asan_test 可執行文件測試和驗證目標設備上的 ASAN 功能,提供帶有每個測試狀態的診斷消息。使用 ASAN Android 構建時,它默認位於/data/nativetest/asan_test/asan_test/data/nativetest64/asan_test/asan_test中。

UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) 執行編譯時檢測以檢查各種類型的未定義行為。雖然 UBSan 能夠檢測許多未定義的行為,但 Android 支持對齊、布爾值、邊界、枚舉、浮點轉換溢出、浮點除零、整數除零、非空屬性、空、返回、返回非空屬性、移位基數、移位指數、有符號整數溢出、不可訪問、無符號整數溢出和 vla 綁定。 unsigned-integer-overflow 雖然在技術上不是未定義的行為,但包含在清理程序中並用於許多 Android 模塊,包括媒體服務器組件,以消除任何潛在的整數溢出漏洞。

執行

在 Android 構建系統中,您可以全局或本地啟用 UBSan。要全局啟用 UBSan,請在 Android.mk 中設置 SANITIZE_TARGET。要在每個模塊級別啟用 UBSan,請設置 LOCAL_SANITIZE 並指定要在 Android.mk 中查找的未定義行為。例如:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0

LOCAL_SRC_FILES:= sanitizer-status.c

LOCAL_MODULE:= sanitizer-status

LOCAL_SANITIZE := alignment bounds null unreachable integer
LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer

include $(BUILD_EXECUTABLE)

Android 構建系統尚不支持藍圖文件中的詳細診斷,因為它支持 makefile。這是寫成藍圖 (Android.bp) 的最接近的等價物:

cc_binary {

    cflags: [
        "-std=c11",
        "-Wall",
        "-Werror",
        "-O0",
    ],

    srcs: ["sanitizer-status.c"],

    name: "sanitizer-status",

    sanitize: {
        misc_undefined: [
            "alignment",
            "bounds",
            "null",
            "unreachable",
            "integer",
        ],
        diag: {
            undefined : true
        },
    },

}

UBSan 快捷方式

Android 還有兩個快捷方式, integerdefault-ub ,可以同時啟用一組消毒劑。 integer 啟用integer-divide-by-zerosigned-integer-overflowunsigned-integer-overflowdefault-ub啟用編譯器性能問題最小的檢查:bool、整數除以零、return、returns-nonnull-attribute、shift-exponent、unreachable 和 vla-bound。 integer sanitizer 類可以與 SANITIZE_TARGET 和 LOCAL_SANITIZE 一起使用,而 default-ub 只能與 SANITIZE_TARGET 一起使用。

更好的錯誤報告

當遇到未定義的行為時,Android 的默認 UBSan 實現會調用指定的函數。默認情況下,該函數是中止的。但是,從 2016 年 10 月開始,Android 上的 UBSan 有一個可選的運行時庫,可以提供更詳細的錯誤報告,包括遇到的未定義行為的類型、文件和源代碼行信息。要使用整數檢查啟用此錯誤報告,請將以下內容添加到 Android.mk 文件中:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

LOCAL_SANITIZE 值在構建期間啟用清理程序。 LOCAL_SANITIZE_DIAG 為指定的消毒劑打開診斷模式。可以將 LOCAL_SANITIZE 和 LOCAL_SANITIZE_DIAG 設置為不同的值,但僅啟用 LOCAL_SANITIZE 中的那些檢查。如果在 LOCAL_SANITIZE 中未指定檢查,但在 LOCAL_SANITIZE_DIAG 中指定了檢查,則不會啟用檢查並且不會給出診斷消息。

以下是 UBSan 運行時庫提供的信息示例:

pixel-xl:/ # sanitizer-status ubsan
sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')

內核地址清理器

與基於 LLVM 的用戶空間組件清理程序類似,Android 包括內核地址清理程序 (KASAN)。 KASAN 是內核和編譯時修改的組合,它產生了一個檢測系統,允許更簡單的錯誤發現和根本原因分析。

KASAN 可以檢測內核中許多類型的內存違規。它還可以檢測堆棧、堆和全局變量的越界讀寫,並可以檢測釋放後使用和雙重釋放。

與 ASAN 類似,KASAN 在編譯時使用內存功能檢測和影子內存的組合來跟踪運行時的內存訪問。在 KASAN 中,八分之一的內核內存空間專用於影子內存,它決定了內存訪問是否有效。

x86_64 和 arm64 架構支持 KASAN。自 4.0 以來,它一直是上游內核的一部分,並已向後移植到基於 Android 3.18 的內核。 KASAN 已經在使用 gcc 基於 4.9.2 編譯的 Android 內核上進行了測試。

除了 KASAN,kcov 是另一個對測試有用的內核修改。開發 kcov 是為了允許在內核中進行覆蓋引導的模糊測試。它根據系統調用輸入來測量覆蓋率,並且對模糊系統(例如syzkaller )很有用。

執行

要在啟用 KASAN 和 kcov 的情況下編譯內核,請將以下構建標誌添加到內核構建配置中:

CONFIG_KASAN 
CONFIG_KASAN_INLINE 
CONFIG_TEST_KASAN 
CONFIG_KCOV 
CONFIG_SLUB 
CONFIG_SLUB_DEBUG 
CONFIG_CC_OPTIMIZE_FOR_SIZE

並刪除以下內容:

CONFIG_SLUB_DEBUG_ON 
CONFIG_SLUB_DEBUG_PANIC_ON 
CONFIG_KASAN_OUTLINE 
CONFIG_KERNEL_LZ4

然後像往常一樣構建和刷新你的內核。 KASAN 內核比原始內核大得多。如果適用,修改任何引導參數和引導加載程序設置以考慮到這一點。

刷新內核後,檢查內核啟動日誌以查看 KASAN 是否已啟用並正在運行。內核將使用 KASAN 的內存映射信息啟動,例如:

...
[    0.000000] c0      0 Virtual kernel memory layout:
[    0.000000] c0      0     kasan   : 0xffffff8000000000 - 0xffffff9000000000   (    64 GB)
[    0.000000] c0      0     vmalloc : 0xffffff9000010000 - 0xffffffbdbfff0000   (   182 GB)
[    0.000000] c0      0     vmemmap : 0xffffffbdc0000000 - 0xffffffbfc0000000   (     8 GB maximum)
[    0.000000] c0      0               0xffffffbdc0000000 - 0xffffffbdc3f95400   (    63 MB actual)
[    0.000000] c0      0     PCI I/O : 0xffffffbffa000000 - 0xffffffbffb000000   (    16 MB)
[    0.000000] c0      0     fixed   : 0xffffffbffbdfd000 - 0xffffffbffbdff000   (     8 KB)
[    0.000000] c0      0     modules : 0xffffffbffc000000 - 0xffffffc000000000   (    64 MB)
[    0.000000] c0      0     memory  : 0xffffffc000000000 - 0xffffffc0fe550000   (  4069 MB)
[    0.000000] c0      0       .init : 0xffffffc001d33000 - 0xffffffc001dce000   (   620 KB)
[    0.000000] c0      0       .text : 0xffffffc000080000 - 0xffffffc001d32284   ( 29385 KB)
...

這就是錯誤的外觀:

[   18.539668] c3      1 ==================================================================
[   18.547662] c3      1 BUG: KASAN: null-ptr-deref on address 0000000000000008
[   18.554689] c3      1 Read of size 8 by task swapper/0/1
[   18.559988] c3      1 CPU: 3 PID: 1 Comm: swapper/0 Tainted: G        W      3.18.24-xxx #1
[   18.569275] c3      1 Hardware name: Android Device
[   18.577433] c3      1 Call trace:
[   18.580739] c3      1 [<ffffffc00008b32c>] dump_backtrace+0x0/0x2c4
[   18.586985] c3      1 [<ffffffc00008b600>] show_stack+0x10/0x1c
[   18.592889] c3      1 [<ffffffc001481194>] dump_stack+0x74/0xc8
[   18.598792] c3      1 [<ffffffc000202ee0>] kasan_report+0x11c/0x4d0
[   18.605038] c3      1 [<ffffffc00020286c>] __asan_load8+0x20/0x80
[   18.611115] c3      1 [<ffffffc000bdefe8>] android_verity_ctr+0x8cc/0x1024
[   18.617976] c3      1 [<ffffffc000bcaa2c>] dm_table_add_target+0x3dc/0x50c
[   18.624832] c3      1 [<ffffffc001bdbe60>] dm_run_setup+0x50c/0x678
[   18.631082] c3      1 [<ffffffc001bda8c0>] prepare_namespace+0x44/0x1ac
[   18.637676] c3      1 [<ffffffc001bda170>] kernel_init_freeable+0x328/0x364
[   18.644625] c3      1 [<ffffffc001478e20>] kernel_init+0x10/0xd8
[   18.650613] c3      1 ==================================================================

此外,如果您的內核中啟用了模塊,您可以加載 test_kasan 內核模塊以進行進一步測試。該模塊嘗試越界內存訪問和釋放後使用,可用於在目標設備上測試 KASAN。