Kernel address sanitizer (Kasan) 是一款隨 Linux 內核代碼一同發布和維護的內存檢測工具,由內核社區維護和發展。本文簡要介紹 Kasan 的原理及使用方法。
Kasan 是 Kernel Address Sanitizer 的縮寫,它是一個動態檢測內存錯誤的工具,主要功能是檢查內存越界訪問和使用已釋放的內存等問題。Kasan 集成在 Linux 內核中,隨 Linux 內核代碼一起發布,並由內核社區維護和發展。
Kasan 可以追溯到 LLVM 的 sanitizers 項目(https://github.com/google/sanitizers),這個項目包含了 AddressSanitizer,MemorySanitizer,ThreadSanitizer 和 LeakSanitizer 等工具。但這些工具只能檢測用戶空間的內存問題。通過在編譯時加入指定的選項,就可以給用戶程序加入 Address Sanitizer 功能。
// To compile: g++ -O -g -fsanitize=address use-after-free.c int main(int argc, char **argv) { int *array = new int[10]; delete [] array; return array[argc]; // BOOM }
當運行以上有內存使用錯誤的程序時,加入 Address Sanitizer 功能的的版本會報告如下的錯誤信息,而沒有任何選項的版本則會正常結束程序。
tengrui@virtualbox:~/workspace/cc$ g++ -O -g -fsanitize=address use-after-free.cc tengrui@virtualbox:~/workspace/cc$ ./a.out ================================================================= ==4206==ERROR: AddressSanitizer: heap-use-after-free on address 0x60400000dfd4 at pc 0x0000004007d4 bp 0x7ffdfdd414f0 sp 0x7ffdfdd414e0 READ of size 4 at 0x60400000dfd4 thread T0 #0 0x4007d3 in main /home/tengrui/workspace/cc/use-after-free.cc:4 #1 0x7f8aa150882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #2 0x4006b8 in _start (/home/tengrui/workspace/cc/a.out+0x4006b8) 0x60400000dfd4 is located 4 bytes inside of 40-byte region [0x60400000dfd0,0x60400000dff8) freed by thread T0 here: #0 0x7f8aa194abca in operator delete[](void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99bca) #1 0x4007a7 in main /home/tengrui/workspace/cc/use-after-free.cc:3 previously allocated by thread T0 here: #0 0x7f8aa194a5d2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x995d2) #1 0x400797 in main /home/tengrui/workspace/cc/use-after-free.cc:2 SUMMARY: AddressSanitizer: heap-use-after-free /home/tengrui/workspace/cc/use-after-free.cc:4 main Shadow bytes around the buggy address: 0x0c087fff9ba0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9be0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c087fff9bf0: fa fa fa fa fa fa fa fa fa fa[fd]fd fd fd fd fa 0x0c087fff9c00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe ==4206==ABORTING
Andrey Ryabinin 借鑒了 AddressSanitizer 的思想,並在 Linux 內核中實現了 Kernel Address Sanitizer。所以 Kasan 也可以看成是用於內核空間的 Address Sanitizer。
Kasan 的原理是利用“額外”的內存來標記那些可以被使用的內存的狀態。這些做標記的區域被稱為影子區域(shadow region)。了解 Linux 內存管理的讀者知道,內存中的每個物理頁在內存中都會有一個 struct page 這樣的結構體來表示,即每 4KB 的頁需要 40B 的結構體,大約 1% 的內存用來表示內存本身。Kasan 與其類似但“浪費”更為嚴重,影子區域的比例是 1:8,即總內存的九分之一會被“浪費”。用官方文檔中的例子,如果有 128TB 的可用內存,需要有額外 16TB 的內存用來做標記。
做標記的方法比較簡單,將可用內存按照 8 子節的大小分組,如果每組中所有 8 個字節都可以訪問,則影子內存中相應的地方用全零(0x00)表示;如果可用內存的前 N(1 到 7 范圍之間)個字節可用,則影子內存中響應的位置用 N 表示;其它情況影子內存用負數表示該內存不可用。
Kasan 是內核的一部分,使用時需要重新配置、編譯並安裝內核。Kasan 在 Linux 內核 4.0 版本時被引入內核,所以選擇的內核代碼需要高於 4.0 版本。另外,最基本的 Kasan 功能需要 GCC4.9.2 支持,更多的支持則需要 GCC5.0 及以上版本。
首先是配置和編譯內核。
運行如下命令啟動圖形配置界面:
make menuconfig
然後重新編譯並安裝內核即可,除了通用的編譯和安裝命令,在 Fedora 這種發行版本中,還需要更新 grub。
make menuconfig make sudo make modules_install sudo make install
sudo grub2mkconfig – o /boot/grub/grub.cfg
其它發行版本請參考相關文檔。