内存管理三 内核内存检测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 
您的支持是对博主最大的鼓励,感谢您的认真阅读。 
本文无所谓版权,欢迎转载。

猜你喜欢

转载自blog.csdn.net/frank_zyp/article/details/82494301