1. 程式人生 > >記憶體管理三 核心記憶體檢測KASAN

記憶體管理三 核心記憶體檢測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 
您的支援是對博主最大的鼓勵,感謝您的認真閱讀。 
本文無所謂版權,歡迎轉載。