KASAN 是 Kernel Address Sanitizer 的縮寫,它是一個動態檢測內存錯誤的工具,主要功能是檢查內存越界訪問和使用已釋放的內存等問題。KASAN 集成在 Linux 內核中,隨 Linux 內核代碼一起發布,並由內核社區維護和發展。本文簡要介紹 KASAN 的原理及使用方法。
一、KASAN的原理和使用方法
1. 如何打開KASAN功能
Kernel defconfig增加如下配置:
由於1/8的內存用於shadow memory,可用內存會減少1/8,例如8GB的內存,打開KASAN后,MemTotal約為6.72GB。
C:\Users>adb shell "cat /proc/meminfo | grep MemTotal"
MemTotal: 6723572 kB
2. KASAN原理概述
KASAN利用額外的內存標記可用內存的狀態,這部分額外的內存被稱作shadow memory(影子區),KASAN將1/8的內存用作shadow memory。使用特殊的magic num填充shadow memory,在每一次load/store內存的時候檢測對應的shadow memory確定操作是否valid。連續8 bytes內存(8 bytes align)使用1 byte shadow memory標記。
如果8 bytes內存都可以訪問,則shadow memory的值為0;如果連續N(1 =< N <= 7) bytes可以訪問,則shadow memory的值為N;如果8 bytes內存訪問都是invalid,則shadow memory的值為負數。
圖片摘自 http://www.wowotech.net/memory_management/424.html
例如:
這段匯編指令是往0xffffffc08821e810地址寫5,當打開Kasan時,編譯器會自動插入紅色的bl __asan_store1指令,__asan_store1函數就是檢測一個地址對應的shadow memory的值是否允許寫1 byte,藍色匯編指令是真正的內存訪問。
3. 如何根據shadow memory的值判斷內存訪問操作是否合法?
shadow memory檢測原理的實現主要就是__asan_load##size()和__asan_store##size()函數。KASAN如何根據訪問的address以及對應的shadow memory的狀態值來判斷訪問是否合法呢?
1)當訪問8 bytes時,*shadow_memory == 0,訪問是valid,否則是invalid;
2)當訪問N bytes (N = 1,2,4)時,
if (*shadow && *shadow > ((unsigned long)addr & 7) + N),訪問valid;否則,是invalid;
4. 伙伴系統分配的內存shadow memory值如何填充
(1) 從buddy system分配內存
圖片摘自 http://www.wowotech.net/memory_management/424.html
Step1:假如從buddy system分配4 pages,系統首先從order=2的鏈表中摘下一塊內存;
Step2:然后根據shadow memory address和memory address的對應關系找到對應的shadow memory;對應關系為:
Shadow_addr = (addr >> 3) + KASAN_SHADOW_OFFSET,右移3bit的原因是8 byte memory對應1 byte shadow memory;
Step3:填充shadow memory的內容,分配的4 pages均可訪問,填充為0;
(2) 從buddy system釋放內存
圖片摘自 http://www.wowotech.net/memory_management/424.html
Step1:從buddy system order = 2的鏈表中釋放4 pages;
Step2:根據shadow memory addr和memory addr的對應關系,找到shadow memory;
Step3:將shadow memory對應的內存區域2KB(16KB/8)填充為0xFF(KASAN_FREE_PAGE);
5. slub分配的內存shadow memory值如何填充
(1) 從slub cache分配內存
圖片摘自 http://www.wowotech.net/memory_management/424.html
Step1:kmalloc(20)會匹配到kmalloc-32的kmem_cache,實際分配的object大小是32 bytes。剩下的12 bytes KASAN會標記為不可訪問狀態;
Step2:根據shadow memory addr和memory addr的對應關系找到shadow meory;
Step3:填充shadow memory的內容為00 00 04 FC,具體含義為:
1)前面2個00表示第0~15 byte均可訪問
2)04表示第16~23 byte只有前面4 bytes可以訪問
3)FC表示第24~31 byte為 KASAN_KMALLOC_REDZONE,不可訪問
Step4:保存內存分配的call-stack;
Step5:如果訪問了REDZONE區域,KASAN會report out-of-bounds bug;
(2) 從slub cache釋放內存
圖片摘自 http://www.wowotech.net/memory_management/424.html
Step1:從slub cache(kmalloc-32) free 20 bytes內存;
Step2:根據memory addr找到shadow memory addr;
Step3:將shadow memory的4 bytes填充為FB(KASAN_KMALLOC_FREE);
Step4:保存slub free的call-stack;
Step5:如果訪問了 FB對應的內存,KASAN會報use-after-free bug;
6. 其他形式分配的內存shadow memory如何填充?
全局變量/棧分配的內存填充原理和前面類似,實現有些差異,這里不一一贅述。
7. KASAN report bug舉例
二、總結
KASAN通過建立影子內存來管理內存訪問的合法性,可以有效檢測內存越界等問題,但無法發現因邏輯問題導致的合法內存的內容改寫問題。
參考文獻:
1. http://www.wowotech.net/memory_management/424.html
2. https://www.kernel.org/doc/html/latest/dev-tools/kasan.html
3. KernelAddressSanitizer (KASan) a fast memory error detector for the Linux kernel.pdf

“內核工匠”微信公眾號
Linux 內核黑科技 | 技術文章 | 精選教程