記憶體管理三 核心記憶體檢測KASAN
一、簡介:
Kasan 是 Kernel Address Sanitizer 的縮寫,它是一個動態檢測記憶體錯誤的工具,主要功能是檢查記憶體越界訪問和使用已釋放的記憶體等問題。KASAN可以檢測的記憶體異常包括:slab-out-of-bounds/user-after-free/stack-out-of-bounds/global-out-of-bounds等。
(1)環境要求:
GCC 5.0或者以上的編譯器;
(2)開啟方法:
CONFIG_KASAN=y
CONFIG_KASAN_OUTLINE=y(效能會變差)或CONFIG_KASAN_INLINE=y
如某些檔案或者目錄不想加入KASAN的檢測,可以做如下處理:
在對應的Makefile裡新增(檔案:xxx.c):
KASAN_SANITIZE_xxx.o := n
在對應的Makefile裡新增:
KASAN_SANITIZE := n
(3)相關概念
如下圖是8個位元組記憶體對映1個位元組shadow memory,(部分核心可能是16:1的關係,後面的例程就是16:1),這樣8位元組的記憶體就會犧牲1位元組的用於標記readzone,這樣會導致效能的部分下降,造成系統卡頓。下圖中,當8個位元組全部可用時,這塊shadow memory會標記位0x00,7個位元組可用時會標記位0x07 ......;
部分其他的值代表如下:
在記憶體相關的操作時,當開啟KASAN時,編譯器自動插入檢查程式碼:
*address = ...; // or: ... = *address;// 操作某塊地址
shadow_address = MemToShadow(address);
if (ShadowIsPoisoned(shadow_address)) { //對該塊地址進行檢查
ReportError(address, kAccessSize, kIsWrite); //越界或者其他異常報錯
}
*address = ...; // or: ... = *address;//無異常繼續進行操作
二、例項:
(1)slab-out-of-bounds越界檢測:
在前文建立sys節點的基礎上,對cat呼叫的的show函式修改如下:
static ssize_t hello_test_show(struct device *dev, struct device_attribute *attr, char *buf)
{
size_t size = 21;
char *ptr = NULL;
printk("hello_test %s,%d\n",__FUNCTION__,__LINE__);
ptr = kmalloc(size, GFP_KERNEL); //申請21bit位元組可用的記憶體
if(!ptr){
pr_err("Allocation failed! \n");
return -1;
}
ptr[size + 1] = 'X';//給30 + 1大小的記憶體寫X,實際越界2位元組
kfree(ptr);
return 0;
}
當cat /sys/bus/platform/drivers/hello_test/odm:hello_test/hello_test,會產生越界異常,這是KASAN的機制會捕捉到,併產生KE異常,解析KE的DB檔案,如下看到如下堆疊異常:
[ 101.291091] (3)[5074:cat]hello_test hello_test_show,16
[ 101.291131] -(3)[5074:cat]==================================================================
[ 101.291163] -(3)[5074:cat]BUG: KASAN: slab-out-of-bounds in hello_test_show+0x58/0x88 at addr ffffffdabc0a2a96
[ 101.291175] -(3)[5074:cat]Write of size 1 by task cat/5074
[ 101.291195] -(3)[5074:cat]CPU: 3 PID: 5074 Comm: cat Tainted: G W O 4.9.77+ #16
[ 101.291206] -(3)[5074:cat]Hardware name: MT6761V/WBB (DT)
[ 101.291218] -(3)[5074:cat]Call trace:
[ 101.291239] -(3)[5074:cat][<ffffff8fa848b4e8>] dump_backtrace+0x0/0x358
[ 101.291254] -(3)[5074:cat][<ffffff8fa848b8dc>] show_stack+0x14/0x20
[ 101.291273] -(3)[5074:cat][<ffffff8fa88de118>] dump_stack+0xa8/0xd0
[ 101.291293] -(3)[5074:cat][<ffffff8fa86933c4>] kasan_object_err+0x24/0x80
[ 101.291308] -(3)[5074:cat][<ffffff8fa8693654>] kasan_report.part.1+0x1dc/0x498
[ 101.291323] -(3)[5074:cat][<ffffff8fa8693b98>] qlist_move_cache+0x0/0xc0
[ 101.291338] -(3)[5074:cat][<ffffff8fa8691fe4>] __asan_store1+0x4c/0x58
[ 101.291354] -(3)[5074:cat][<ffffff8fa8ec13a8>] hello_test_show+0x58/0x88
[ 101.291371] -(3)[5074:cat][<ffffff8fa8a12678>] dev_attr_show+0x40/0x88
[ 101.291389] -(3)[5074:cat][<ffffff8fa87549b8>] sysfs_kf_seq_show+0x128/0x1c8
[ 101.291404] -(3)[5074:cat][<ffffff8fa8752720>] kernfs_seq_show+0x80/0x98
[ 101.291421] -(3)[5074:cat][<ffffff8fa86d6fd4>] seq_read+0x14c/0x720
[ 101.291436] -(3)[5074:cat][<ffffff8fa8753774>] kernfs_fop_read+0x1e4/0x280
[ 101.291452] -(3)[5074:cat][<ffffff8fa86a074c>] __vfs_read+0x74/0x1d0
[ 101.291466] -(3)[5074:cat][<ffffff8fa86a1ac0>] vfs_read+0x98/0x188
[ 101.291480] -(3)[5074:cat][<ffffff8fa86a35b0>] SyS_read+0x68/0xe0
[ 101.291496] -(3)[5074:cat][<ffffff8fa8482f70>] el0_svc_naked+0x24/0x28
[ 101.291508] -(3)[5074:cat]Object at ffffffdabc0a2a80, in cache kmalloc-64 size: 64
[ 101.291517] -(3)[5074:cat]Allocated:
......
[ 101.291989] -(3)[5074:cat] ffffffdabc0a2800: fb fb fb fb fc fc fc fc fb fb fb fb fc fc fc fc
[ 101.292003] -(3)[5074:cat] ffffffdabc0a2900: 00 00 00 fc fc fc fc fc 00 00 00 00 fc fc fc fc
[ 101.292016] -(3)[5074:cat]>ffffffdabc0a2a00: fb fb fb fb fc fc fc fc 00 05 fc fc fc fc fc fc
[ 101.292026] -(3)[5074:cat] ^
其中:
0xFC是Redzone標記,如果訪問了Redzone區域KASAN就會檢測out-of-bounds的發生;
0xFB標記記憶體是釋放的狀態。但是不是可用狀態;
0x00是可用記憶體;
此處的shadow memory和可用記憶體是16:1的 關係,故0x00代表16位元組可用,0x05程式碼5位元組可用,故和申請的21位元組匹配上。
(2)user-after-free釋放後使用:
將程式碼修改如下,然後cat對應的節點,同樣會產生KE的DB檔案,
static ssize_t hello_test_show(struct device *dev, struct device_attribute *attr, char *buf)
{
size_t size = 21;
char *ptr = NULL;
printk("hello_test %s,%d\n",__FUNCTION__,__LINE__);
ptr = kmalloc(size, GFP_KERNEL);
if(!ptr){
pr_err("Allocation failed! \n");
return -1;
}
//ptr[size + 1] = 'X';
kfree(ptr); //提前釋放
ptr[size - 1] = 'X'; //釋放後對此記憶體程序賦值寫入資料
return 0;
}
log顯示如下:
[ 98.979214] (1)[4982:cat]hello_test hello_test_show,16
[ 98.979256] -(1)[4982:cat]==================================================================
[ 98.979284] -(1)[4982:cat]BUG: KASAN: use-after-free in hello_test_show+0x5c/0x88 at addr ffffffe777c16f94
[ 98.979296] -(1)[4982:cat]Write of size 1 by task cat/4982
[ 98.979313] -(1)[4982:cat]CPU: 1 PID: 4982 Comm: cat Tainted: G W O 4.9.77+ #18
[ 98.979324] -(1)[4982:cat]Hardware name: MT6761V/WBB (DT)
[ 98.979334] -(1)[4982:cat]Call trace:
[ 98.979354] -(1)[4982:cat][<ffffffa7baa8b4e8>] dump_backtrace+0x0/0x358
[ 98.979368] -(1)[4982:cat][<ffffffa7baa8b8dc>] show_stack+0x14/0x20
[ 98.979385] -(1)[4982:cat][<ffffffa7baede118>] dump_stack+0xa8/0xd0
[ 98.979402] -(1)[4982:cat][<ffffffa7bac933c4>] kasan_object_err+0x24/0x80
[ 98.979417] -(1)[4982:cat][<ffffffa7bac93654>] kasan_report.part.1+0x1dc/0x498
[ 98.979431] -(1)[4982:cat][<ffffffa7bac93b98>] qlist_move_cache+0x0/0xc0
[ 98.979444] -(1)[4982:cat][<ffffffa7bac91fe4>] __asan_store1+0x4c/0x58
[ 98.979459] -(1)[4982:cat][<ffffffa7bb4c13ac>] hello_test_show+0x5c/0x88
[ 98.979475] -(1)[4982:cat][<ffffffa7bb012678>] dev_attr_show+0x40/0x88
[ 98.979491] -(1)[4982:cat][<ffffffa7bad549b8>] sysfs_kf_seq_show+0x128/0x1c8
[ 98.979506] -(1)[4982:cat][<ffffffa7bad52720>] kernfs_seq_show+0x80/0x98
[ 98.979521] -(1)[4982:cat][<ffffffa7bacd6fd4>] seq_read+0x14c/0x720
[ 98.979534] -(1)[4982:cat][<ffffffa7bad53774>] kernfs_fop_read+0x1e4/0x280
[ 98.979549] -(1)[4982:cat][<ffffffa7baca074c>] __vfs_read+0x74/0x1d0
[ 98.979562] -(1)[4982:cat][<ffffffa7baca1ac0>] vfs_read+0x98/0x188
[ 98.979575] -(1)[4982:cat][<ffffffa7baca35b0>] SyS_read+0x68/0xe0
[ 98.979999] -(1)[4982:cat] ffffffe777c16d00: 00 00 00 00 fc fc fc fc fb fb fb fb fc fc fc fc
[ 98.980012] -(1)[4982:cat] ffffffe777c16e00: fb fb fb fb fc fc fc fc 00 00 00 00 fc fc fc fc
[ 98.980025] -(1)[4982:cat]>ffffffe777c16f00: fb fb fb fb fc fc fc fc fb fb fb fb fc fc fc fc
[ 98.980034] -(1)[4982:cat] ^
如上0xFB標記記憶體是釋放的狀態。但對其進行寫入,故出現報錯。
作者:frank_zyp
您的支援是對博主最大的鼓勵,感謝您的認真閱讀。
本文無所謂版權,歡迎轉載。