文章来源
引言
Kasan是Kernel Address Sanitizer的缩写,是一种动态检测内存错误的工具,主要用于检查内存过量访问和已释放内存使用等问题。Kasan集成到Linux内核中,随Linux内核代码一起发布,并在内核社区中进行维护和发展。
背景
Kasan可以追溯到LLVM的sanitizers项目(),该项目包含AddressSanitizer、MemorySanitizer、ThreadSanitizer和LeakSanitizer等工具。但是,这些工具只能检测用户空间的内存问题。通过添加编译时指定的选项,可以将Address Sanitizer功能添加到用户程序中。
清单 1. 用户空间内存错误代码实例
运行上述内存使用错误的程序时,添加到Address Sanitizer功能中的版本将报告以下错误消息,没有选项的版本将正常退出程序。
清单 2. Address Sanitizer 运行结果
Andrey Ryabinin借用AddressSanitizer的想法,在Linux内核上实现了kerner,因此Sanitizer
原理
Kasan的原则是利用“附加”内存来显示可用内存的状态。这样显示的区域称为“阴影区域”。了解Linux内存管理的读者知道,内存中的每个物理页面都有内存中的结构,如结构页。这意味着每4KB页需要40B结构,大约1%的内存用于表示内存本身。Kasan相似,但“浪费”更严重。阴影区域的比例为1:8。也就是说,总内存的九分之一被“浪费”。例如,如果官方文档的可用内存为128TB,则需要额外的16TB内存来显示。
标记的方法很简单。将可用内存分组为8个子部分的大小,如果每个组的所有8个字节均可访问,则阴影内存的相应部分将显示为全部0 (0x00)。如果可用内存的前N个字节(范围从1到7)可用,则阴影内存中的响应位置将显示为N。否则,阴影内存表示内存不可用,为负数。
图 1. Kasan 内存布局原理
使用
Kasan是内核的一部分,需要重建、编译和安装内核。Kasan必须在Linux内核4.0版中引入内核,选择高于4.0的内核代码。此外,最基本的Kasan功能需要GCC4.9.2支持,更多支持需要GCC5.0或更高版本。
首先是配置和编译内核。
运行以下命令以启动图形配置界面:
清单 3. Linux 图形配置命令
make menuconfig
图 2. Kasan 内核选项配置界面
图 3. Kasan 模式选项
ps://p6.toutiaoimg.com/large/pgc-image/RnJkMzGESCR1Ha?from=article.detail&_iz=31825&index=6" width="580" height="448"/>
然后重新编译并安装内核即可,除了通用的编译和安装命令,在 Fedora 这种发行版本中,还需要更新 grub。
清单 4. Linux 内核编译、安装命令
清单 5. Grub 配置命令
测试
学习 Linux 的同学一定对 Linus 的名言“Talk is cheap, show me the code.”耳熟能详。由于 Kasan 这部分代码一直在变化之中,更重要的是代码也比较难懂,本文暂时不去讨论具体的实现细节,而是从测试的角度研究其原理。
幸运的是 Linux 内核的源码中已经包含了针对 Kasan 的测试代码,其位置在 linux/lib。编译内核或者单独编译 lib 模块的时候,会生成 模块。当向内核插入该模块的时候,就会执行测试代码。
例如,下面的代码模拟了内存越界的情况:申请了 124 字节的空间,却写访问第 125 个字节的内容,则会造成越界访问的问题。
清单 6. Kasan 内存右侧越界测试代码
当运行以上测试代码的时候,在内核日志中会详细打印以下内容:
清单 7. 内核日志
[18319.272272] kasan test: kmalloc_oob_right out-of-bounds to right
[18319.272288] kasan test: kmalloc_oob_right ptr address:
0xffff8800d40b9798
[18319.272292] =====================================================
[18319.272996] BUG: KASAN: slab-out-of-bounds in
kmalloc_oob_right+0xb4/0xdb [test_kasan] at addr ffff8800d40b9814
[18319.274250] Write of size 1 by task insmod/4852
[18319.274992] ===========================================
==================================
[18319.275982] BUG kmalloc-128 (Tainted: G B
OE ): kasan: bad access detected
[18319.276103] ----------------------------------------------------
[18319.276103] INFO: Allocated in 0xffff8800d40b9d08
age=18446723238827703540 cpu=0 pid=0
[18319.276103] kmalloc_oob_right+0x53/0xdb [test_kasan]
[18319.276103] ___slab_alloc+0x4da/0x540
[18319.276103] __slab_alloc+0x20/0x40
[18319.276103] kmem_cache_alloc_trace+0x1f8/0x270
[18319.276103] kmalloc_oob_right+0x53/0xdb [test_kasan]
[18319.276103] kmalloc_tests_init+0x9/0xf25 [test_kasan]
[18319.276103] do_one_initcall+0xa9/0x230
[18319.276103] do_init_module+0x1d0/0x4de
[18319.276103] load_module+0x74d0/0x9ca0
[18319.276103] SYSC_finit_module+0x190/0x1d0
[18319.276103] SyS_finit_module+0xe/0x10
[18319.276103] entry_SYSCALL_64_fastpath+0x1e/0xa8
[18319.276103] INFO: Freed in 0x10044bcf2 age=18446723238827703542 cpu=0 pid=0
[18319.276103] load_elf_binary+0x219/0x4400
[18319.276103] __slab_free+0x17f/0x2d0
[18319.276103] kfree+0x18a/0x1d0
[18319.276103] load_elf_binary+0x219/0x4400
[18319.276103] search_binary_handler+0x151/0x420
[18319.276103] do_execvea
[18319.276103] SyS_execve+0x3a/0x50
[18319.276103] do_syscall_64+0x19c/0x3b0
[18319.276103] return_from_SYSCALL_64+0x0/0x6a
[18319.276103] INFO: Slab 0xffffea0003502e00 objects=17
used=14 fp=0xffff8800d40b8748 flags=0x1ffff0000004080
[18319.276103] INFO: Object 0xffff8800d40b9790
@offset=6032 fp=0xcccccccccccccccc
[18319.276103] Redzone ffff8800d40b9788: 5a 5a 5a 5a 5a 5a 5a 5a
ZZZZZZZZ
[18319.276103] Object ffff8800d40b9790: cc cc cc cc cc cc
cc cc 6b 6b 6b 6b 6b 6b 6b 6b ........kkkkkkkk
[18319.276103] Object ffff8800d40b97a0: 6b 6b 6b 6b 6b
6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[18319.276103] Object ffff8800d40b97b0: 6b 6b 6b 6b 6b
6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[18319.276103] Object ffff8800d40b97c0: 6b 6b 6b 6b 6b
6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[18319.276103] Object ffff8800d40b97d0: 6b 6b 6b 6b 6b
6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[18319.276103] Object ffff8800d40b97e0: 6b 6b 6b 6b 6b
6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[18319.276103] Object ffff8800d40b97f0: 6b 6b 6b 6b 6b
6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[18319.276103] Object ffff8800d40b9800: 6b 6b 6b 6b 6b
6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[18319.276103] Redzone ffff8800d40b9810: 6b 6b 6b 6b 6b 6b 6b a5
kkkkkkk.
[18319.276103] Padding ffff8800d40b9950: f0 bc 44 00 01 00 00 00
..D.....
[18319.276103] CPU: 0 PID: 4852 Comm: insmod Tainted: G B
OE 4.7.0-rc4+ #25
[18319.276103] Hardware name: innotek GmbH
VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[18319.276103] 0000000000000000 00000000bfaee01f
ffff8800d40a7810 ffffffff81b6fd61
[18319.276103] ffff88011f603440 ffff8800d40b9790
ffff8800d40a7840 ffffffff8157b472
[18319.276103] ffff88011f603440 ffffea0003502e00
ffff8800d40b9790 ffffffffc03780db
[18319.276103] Call Trace:
[18319.276103] [<ffffffff81b6fd61>] dump_stack+0x63/0x82
[18319.276103] [<ffffffff8157b472>] print_trailer+0x112/0x1a0
[18319.276103] [<ffffffffc03780db>] ? kmalloc_oob_right+0xdb/0xdb [test_kasan]
[18319.276103] [<ffffffff815815f4>] object_err+0x34/0x40
[18319.276103] [<ffffffff81583a82>] kasan_report_error+0x222/0x540
[18319.276103] [<ffffffff8146af5c>] ? power_down+0xc4/0xc4
[18319.276103] [<ffffffffc03780db>] ? kmalloc_oob_right+0xdb/0xdb [test_kasan]
[18319.276103] [<ffffffff81584031>] __asan_report_store1_noabort+0x61/0x70
[18319.276103] [<ffffffffc03780b4>] ? kmalloc_oob_right+0xb4/0xdb [test_kasan]
[18319.276103] [<ffffffffc03780b4>] kmalloc_oob_right+0xb4/0xdb [test_kasan]
[18319.276103] [<ffffffffc03780e4>] kmalloc_tests_init+0x9/0xf25 [test_kasan]
[18319.276103] [<ffffffff81002299>] do_one_initcall+0xa9/0x230
[18319.276103] [<ffffffff810021f0>] ? initcall_blacklisted+0x180/0x180
[18319.276103] [<ffffffff815830c6>] ? kasan_unpoison_shadow+0x36/0x50
[18319.276103] [<ffffffff815830c6>] ? kasan_unpoison_shadow+0x36/0x50
[18319.276103] [<ffffffff8158313e>] ? kasan_kmalloc+0x5e/0x70
[18319.276103] [<ffffffff815830c6>] ? kasan_unpoison_shadow+0x36/0x50
[18319.276103] [<ffffffff815831d7>] ? __asan_register_globals+0x87/0xa0
[18319.276103] [<ffffffff8146b8a3>] do_init_module+0x1d0/0x4de
[18319.276103] [<ffffffff812eea50>] load_module+0x74d0/0x9ca0
[18319.276103] [<ffffffff812e3d00>] ? m_show+0x4a0/0x4a0
[18319.276103] [<ffffffff812e7580>] ? module_frob_arch_sections+0x20/0x20
[18319.276103] [<ffffffff815c421d>] ? rw_verify_area+0xbd/0x2b0
[18319.276103] [<ffffffff81534d05>] ? __vmalloc_node_range+0x485/0x630
[18319.276103] [<ffffffff815d4a89>] ? kernel_read_file_from_fd+0x49/0x80
[18319.276103] [<ffffffff812f15d0>] SYSC_finit_module+0x190/0x1d0
[18319.276103] [<ffffffff812f1440>] ? SYSC_init_module+0x220/0x220
[18319.276103] [<ffffffff814d1790>] ? vma_is_stack_for_task+0x90/0x90
[18319.276103] [<ffffffff815d1036>] ? vfs_getattr+0x26/0x30
[18319.276103] [<ffffffff812f162e>] SyS_finit_module+0xe/0x10
[18319.276103] [<ffffffff828bb8b6>] entry_SYSCALL_64_fastpath+0x1e/0xa8
[18319.276103] Memory state around the buggy address:
[18319.276103] ffff8800d40b9700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[18319.276103] ffff8800d40b9780: fc fc fc 00 00 00 00 00 00 00 00 00 00 00 00 00
[18319.276103] >ffff8800d40b9800: 00 00 04 fc fc fc fc fc fc fc fc fc fc fc fc fc
[18319.276103] ^
[18319.276103] ffff8800d40b9880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[18319.276103] ffff8800d40b9900: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 00
[18319.276103] ==================================================================
[18319.333357] kasan test: kmalloc_oob_right ptr[size] address: 0xffff8800d40b9814
其中能直观看出 Kasan 原理的是 Memory state around the buggy address,在一堆 fc 字节中有一串 00 以及一个 04。如前所述,每个 00 代表 8 个可用字节,04 代表该对应的地址中前四个字节可用。这里共有 15 个 00,一个 04。15 x 8 + 4 = 124,正是代码中申请的 124 字节。当测试代码往第 125 个字节中写入数据的时候,Kasan 就会检测到该行为并报告一系列的相关信息。其中 fc 表示的是 slub 对象中的红色区域,其它填充值的意义可以参考以下定义。
清单 8. 填充值的定义
#define KASAN_FREE_PAGE 0xFF /* page was freed */
#define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocations */
#define KASAN_KMALLOC_REDZONE 0xFC /* redzone inside slub object */
#define KASAN_KMALLOC_FREE 0xFB /* object was freed (kmem_cache_free/kfree) */
#define KASAN_GLOBAL_REDZONE 0xFA /* redzone for global variable */
该测试代码包含了许多其它的测试用例,有兴趣的读者可以参考如下代码清单有选择地编译并运行。
清单 9. Kasan 测试用例集
static int __init kmalloc_tests_init(void)
{
kmalloc_oob_right;
kmalloc_oob_left;
kmalloc_node_oob_right;
#ifdef CONFIG_SLUB
kmalloc_pagealloc_oob_right;
#endif
kmalloc_large_oob_right;
kmalloc_oob_krealloc_more;
kmalloc_oob_krealloc_less;
kmalloc_oob_16;
kmalloc_oob_in_memset;
kmalloc_oob_memset_2;
kmalloc_oob_memset_4;
kmalloc_oob_memset_8;
kmalloc_oob_memset_16;
kmalloc_uaf;
kmalloc_uaf_memset;
kmalloc_uaf2;
kmem_cache_oob;
kasan_stack_oob;
kasan_global_oob;
ksize_unpoisons_memory;
copy_user_test;
return -EAGAIN;
}
对比
和 Kasan 功能类似的工具还有 kmemcheck,它比 Kasan 更早加入内核,但是运行速度没有 Kasan 快,这是因为 Kasan 利用了编译器的特性,可以将代码编译为內联模式。但 Kasan 也有自己的不足,目前 Kasan 不能检测出读取未初始化内存的错误,而这一点 kmemcheck 是支持的。
此外,内核还包含了一些配置选项可以打开其它的内存检测功能,如 SLAB_DEBUG 和 DEBUG_SLAB 选项可以激活 redzones 和 poisoning 功能,用来检测申请和释放内存的错误。当打开 DEBUG_PAGEALLOC 选项后,可以检测部分释放后使用内存的情况。
这些都是内核代码质量的保证工具,当提交代码的时候,综合使用以上工具可以预防自己的补丁引入一些低级的错误。
结束语
本文介绍了 Kasan 的配置及使用方法,并通过运行 Kasan 的测试用例说明了 Kasan 的原理。对于内核开发者来说,该工具不仅可以用来检测自己代码。对该工具有兴趣的读者,也可以给该工具增加新功能或发现并修复其中的 BUG。
相关主题
参考 Clang 文档 中关于 AddressSanitizer 的内容。
参考 LWN 文档:The kernel address sanitizer:Linux 每周新闻关于 Kasan 的介绍文章。
参考 LWN文档:Kernel address sainitzer (KASan) - dynamic memory error deetector:Linux 每周新闻中关于 Kasan 的建议及补丁的收录。
参考 Linux 内核文档,了解关于 memcheck 的介绍。
参考 LinuxCon North America 2015 KernelAddre:Andrey Konovalov 和 Dmitry Vyukov 在 LinuxCon North America 2015 会议上的演讲稿。
IBM developerWorks 中国 linux 专区:为使用 linux 的开发人员准备的技术信息和资料。这里提供产品下载、how-to 信息、支持资源以及免费技术库,包含 2000 多份技术文章、教程、最佳实践、IBM Redbook 和在线产品手册。
(完)
1.《【宝马x63540】Kasan-Linux内核的内存检测工具》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《【宝马x63540】Kasan-Linux内核的内存检测工具》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/auto/2768425.html