1c003555aSWan Jiabing.. SPDX-License-Identifier: GPL-2.0 2c003555aSWan Jiabing 3c003555aSWan Jiabing.. include:: ../disclaimer-zh_CN.rst 4c003555aSWan Jiabing 5c003555aSWan Jiabing:Original: Documentation/dev-tools/kasan.rst 6c003555aSWan Jiabing:Translator: 万家兵 Wan Jiabing <wanjiabing@vivo.com> 7c003555aSWan Jiabing 8c003555aSWan Jiabing内核地址消毒剂(KASAN) 9c003555aSWan Jiabing===================== 10c003555aSWan Jiabing 11c003555aSWan Jiabing概述 12c003555aSWan Jiabing---- 13c003555aSWan Jiabing 14c003555aSWan JiabingKernel Address SANitizer(KASAN)是一种动态内存安全错误检测工具,主要功能是 15507f4879SYanteng Si检查内存越界访问和使用已释放内存的问题。 16c003555aSWan Jiabing 17507f4879SYanteng SiKASAN有三种模式: 18c003555aSWan Jiabing 19507f4879SYanteng Si1. 通用KASAN 20507f4879SYanteng Si2. 基于软件标签的KASAN 21507f4879SYanteng Si3. 基于硬件标签的KASAN 22c003555aSWan Jiabing 23507f4879SYanteng Si用CONFIG_KASAN_GENERIC启用的通用KASAN,是用于调试的模式,类似于用户空 24507f4879SYanteng Si间的ASan。这种模式在许多CPU架构上都被支持,但它有明显的性能和内存开销。 25c003555aSWan Jiabing 26507f4879SYanteng Si基于软件标签的KASAN或SW_TAGS KASAN,通过CONFIG_KASAN_SW_TAGS启用, 27507f4879SYanteng Si可以用于调试和自我测试,类似于用户空间HWASan。这种模式只支持arm64,但其 28507f4879SYanteng Si适度的内存开销允许在内存受限的设备上用真实的工作负载进行测试。 29c003555aSWan Jiabing 30507f4879SYanteng Si基于硬件标签的KASAN或HW_TAGS KASAN,用CONFIG_KASAN_HW_TAGS启用,被 31507f4879SYanteng Si用作现场内存错误检测器或作为安全缓解的模式。这种模式只在支持MTE(内存标签 32507f4879SYanteng Si扩展)的arm64 CPU上工作,但它的内存和性能开销很低,因此可以在生产中使用。 33c003555aSWan Jiabing 34507f4879SYanteng Si关于每种KASAN模式的内存和性能影响的细节,请参见相应的Kconfig选项的描述。 35c003555aSWan Jiabing 36507f4879SYanteng Si通用模式和基于软件标签的模式通常被称为软件模式。基于软件标签的模式和基于 37507f4879SYanteng Si硬件标签的模式被称为基于标签的模式。 38c003555aSWan Jiabing 39507f4879SYanteng Si支持 40507f4879SYanteng Si---- 41507f4879SYanteng Si 42507f4879SYanteng Si体系架构 43507f4879SYanteng Si~~~~~~~~ 44507f4879SYanteng Si 455aa4ac64SQing Zhang在x86_64、arm、arm64、powerpc、riscv、s390、xtensa和loongarch上支持通用KASAN, 46507f4879SYanteng Si而基于标签的KASAN模式只在arm64上支持。 47507f4879SYanteng Si 48507f4879SYanteng Si编译器 49507f4879SYanteng Si~~~~~~ 50507f4879SYanteng Si 51507f4879SYanteng Si软件KASAN模式使用编译时工具在每个内存访问之前插入有效性检查,因此需要一个 52507f4879SYanteng Si提供支持的编译器版本。基于硬件标签的模式依靠硬件来执行这些检查,但仍然需要 53507f4879SYanteng Si一个支持内存标签指令的编译器版本。 54507f4879SYanteng Si 55507f4879SYanteng Si通用KASAN需要GCC 8.3.0版本或更高版本,或者内核支持的任何Clang版本。 56507f4879SYanteng Si 57507f4879SYanteng Si基于软件标签的KASAN需要GCC 11+或者内核支持的任何Clang版本。 58507f4879SYanteng Si 59507f4879SYanteng Si基于硬件标签的KASAN需要GCC 10+或Clang 12+。 60507f4879SYanteng Si 61507f4879SYanteng Si内存类型 62507f4879SYanteng Si~~~~~~~~ 63507f4879SYanteng Si 64507f4879SYanteng Si通用KASAN支持在所有的slab、page_alloc、vmap、vmalloc、堆栈和全局内存 65507f4879SYanteng Si中查找错误。 66507f4879SYanteng Si 67507f4879SYanteng Si基于软件标签的KASAN支持slab、page_alloc、vmalloc和堆栈内存。 68507f4879SYanteng Si 69507f4879SYanteng Si基于硬件标签的KASAN支持slab、page_alloc和不可执行的vmalloc内存。 70507f4879SYanteng Si 71507f4879SYanteng Si对于slab,两种软件KASAN模式都支持SLUB和SLAB分配器,而基于硬件标签的 72507f4879SYanteng SiKASAN只支持SLUB。 73c003555aSWan Jiabing 74c003555aSWan Jiabing用法 75c003555aSWan Jiabing---- 76c003555aSWan Jiabing 77c003555aSWan Jiabing要启用KASAN,请使用以下命令配置内核:: 78c003555aSWan Jiabing 79c003555aSWan Jiabing CONFIG_KASAN=y 80c003555aSWan Jiabing 81c003555aSWan Jiabing同时在 ``CONFIG_KASAN_GENERIC`` (启用通用KASAN模式), ``CONFIG_KASAN_SW_TAGS`` 82c003555aSWan Jiabing(启用基于硬件标签的KASAN模式),和 ``CONFIG_KASAN_HW_TAGS`` (启用基于硬件标签 83c003555aSWan Jiabing的KASAN模式)之间进行选择。 84c003555aSWan Jiabing 85c003555aSWan Jiabing对于软件模式,还可以在 ``CONFIG_KASAN_OUTLINE`` 和 ``CONFIG_KASAN_INLINE`` 86c003555aSWan Jiabing之间进行选择。outline和inline是编译器插桩类型。前者产生较小的二进制文件, 87507f4879SYanteng Si而后者快2倍。 88c003555aSWan Jiabing 89c003555aSWan Jiabing要将受影响的slab对象的alloc和free堆栈跟踪包含到报告中,请启用 90c003555aSWan Jiabing``CONFIG_STACKTRACE`` 。要包括受影响物理页面的分配和释放堆栈跟踪的话, 91c003555aSWan Jiabing请启用 ``CONFIG_PAGE_OWNER`` 并使用 ``page_owner=on`` 进行引导。 92c003555aSWan Jiabing 931003f342SYanteng Si启动参数 941003f342SYanteng Si~~~~~~~~ 951003f342SYanteng Si 961003f342SYanteng SiKASAN受到通用 ``panic_on_warn`` 命令行参数的影响。当它被启用时,KASAN 971003f342SYanteng Si在打印出错误报告后会使内核恐慌。 981003f342SYanteng Si 991003f342SYanteng Si默认情况下,KASAN只对第一个无效的内存访问打印错误报告。使用 1001003f342SYanteng Si``kasan_multi_shot``,KASAN对每一个无效的访问都打印一份报告。这会禁用 1011003f342SYanteng Si了KASAN报告的 ``panic_on_warn``。 1021003f342SYanteng Si 1031003f342SYanteng Si另外,独立于 ``panic_on_warn`` 、 ``kasan.fault=`` boot参数可以用 1041003f342SYanteng Si来控制恐慌和报告行为。 1051003f342SYanteng Si 1061003f342SYanteng Si- ``kasan.fault=report`` 或 ``=panic`` 控制是否只打印KASAN report或 1071003f342SYanteng Si 同时使内核恐慌(默认: ``report`` )。即使 ``kasan_multi_shot`` 被 1081003f342SYanteng Si 启用,恐慌也会发生。 1091003f342SYanteng Si 1101003f342SYanteng Si基于软件和硬件标签的KASAN模式(见下面关于各种模式的部分)支持改变堆栈跟 1111003f342SYanteng Si踪收集行为: 1121003f342SYanteng Si 1131003f342SYanteng Si- ``kasan.stacktrace=off`` 或 ``=on`` 禁用或启用分配和释放堆栈痕 1141003f342SYanteng Si 迹的收集(默认: ``on`` )。 1151003f342SYanteng Si 1161003f342SYanteng Si- ``kasan.stack_ring_size=<number of entries>`` 指定堆栈环的条 1171003f342SYanteng Si 目数(默认: ``32768`` )。 1181003f342SYanteng Si 1191003f342SYanteng Si基于硬件标签的KASAN模式是为了在生产中作为一种安全缓解措施使用。因此,它 1201003f342SYanteng Si支持额外的启动参数,允许完全禁用KASAN或控制其功能。 1211003f342SYanteng Si 1221003f342SYanteng Si- ``kasan=off`` 或 ``=on`` 控制KASAN是否被启用(默认: ``on`` )。 1231003f342SYanteng Si 1241003f342SYanteng Si- ``kasan.mode=sync``, ``=async`` or ``=asymm`` 控制KASAN是否 1251003f342SYanteng Si 被配置为同步、异步或非对称的执行模式(默认: ``同步`` )。 1261003f342SYanteng Si 同步模式:当标签检查异常发生时,会立即检测到不良访问。 1271003f342SYanteng Si 异步模式:不良访问的检测是延迟的。当标签检查异常发生时,信息被存储在硬 1281003f342SYanteng Si 件中(对于arm64来说是在TFSR_EL1寄存器中)。内核周期性地检查硬件,并\ 1291003f342SYanteng Si 且只在这些检查中报告标签异常。 1301003f342SYanteng Si 非对称模式:读取时同步检测不良访问,写入时异步检测。 1311003f342SYanteng Si 1321003f342SYanteng Si- ``kasan.vmalloc=off`` or ``=on`` 禁用或启用vmalloc分配的标记(默认: ``on`` )。 1331003f342SYanteng Si 134c003555aSWan Jiabing错误报告 135c003555aSWan Jiabing~~~~~~~~ 136c003555aSWan Jiabing 137c003555aSWan Jiabing典型的KASAN报告如下所示:: 138c003555aSWan Jiabing 139c003555aSWan Jiabing ================================================================== 140d8310914STiezhu Yang BUG: KASAN: slab-out-of-bounds in kmalloc_oob_right+0xa8/0xbc [kasan_test] 141c003555aSWan Jiabing Write of size 1 at addr ffff8801f44ec37b by task insmod/2760 142c003555aSWan Jiabing 143c003555aSWan Jiabing CPU: 1 PID: 2760 Comm: insmod Not tainted 4.19.0-rc3+ #698 144c003555aSWan Jiabing Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 145c003555aSWan Jiabing Call Trace: 146c003555aSWan Jiabing dump_stack+0x94/0xd8 147c003555aSWan Jiabing print_address_description+0x73/0x280 148c003555aSWan Jiabing kasan_report+0x144/0x187 149c003555aSWan Jiabing __asan_report_store1_noabort+0x17/0x20 150d8310914STiezhu Yang kmalloc_oob_right+0xa8/0xbc [kasan_test] 151d8310914STiezhu Yang kmalloc_tests_init+0x16/0x700 [kasan_test] 152c003555aSWan Jiabing do_one_initcall+0xa5/0x3ae 153c003555aSWan Jiabing do_init_module+0x1b6/0x547 154c003555aSWan Jiabing load_module+0x75df/0x8070 155c003555aSWan Jiabing __do_sys_init_module+0x1c6/0x200 156c003555aSWan Jiabing __x64_sys_init_module+0x6e/0xb0 157c003555aSWan Jiabing do_syscall_64+0x9f/0x2c0 158c003555aSWan Jiabing entry_SYSCALL_64_after_hwframe+0x44/0xa9 159c003555aSWan Jiabing RIP: 0033:0x7f96443109da 160c003555aSWan Jiabing RSP: 002b:00007ffcf0b51b08 EFLAGS: 00000202 ORIG_RAX: 00000000000000af 161c003555aSWan Jiabing RAX: ffffffffffffffda RBX: 000055dc3ee521a0 RCX: 00007f96443109da 162c003555aSWan Jiabing RDX: 00007f96445cff88 RSI: 0000000000057a50 RDI: 00007f9644992000 163c003555aSWan Jiabing RBP: 000055dc3ee510b0 R08: 0000000000000003 R09: 0000000000000000 164c003555aSWan Jiabing R10: 00007f964430cd0a R11: 0000000000000202 R12: 00007f96445cff88 165c003555aSWan Jiabing R13: 000055dc3ee51090 R14: 0000000000000000 R15: 0000000000000000 166c003555aSWan Jiabing 167c003555aSWan Jiabing Allocated by task 2760: 168c003555aSWan Jiabing save_stack+0x43/0xd0 169c003555aSWan Jiabing kasan_kmalloc+0xa7/0xd0 170c003555aSWan Jiabing kmem_cache_alloc_trace+0xe1/0x1b0 171d8310914STiezhu Yang kmalloc_oob_right+0x56/0xbc [kasan_test] 172d8310914STiezhu Yang kmalloc_tests_init+0x16/0x700 [kasan_test] 173c003555aSWan Jiabing do_one_initcall+0xa5/0x3ae 174c003555aSWan Jiabing do_init_module+0x1b6/0x547 175c003555aSWan Jiabing load_module+0x75df/0x8070 176c003555aSWan Jiabing __do_sys_init_module+0x1c6/0x200 177c003555aSWan Jiabing __x64_sys_init_module+0x6e/0xb0 178c003555aSWan Jiabing do_syscall_64+0x9f/0x2c0 179c003555aSWan Jiabing entry_SYSCALL_64_after_hwframe+0x44/0xa9 180c003555aSWan Jiabing 181c003555aSWan Jiabing Freed by task 815: 182c003555aSWan Jiabing save_stack+0x43/0xd0 183c003555aSWan Jiabing __kasan_slab_free+0x135/0x190 184c003555aSWan Jiabing kasan_slab_free+0xe/0x10 185c003555aSWan Jiabing kfree+0x93/0x1a0 186c003555aSWan Jiabing umh_complete+0x6a/0xa0 187c003555aSWan Jiabing call_usermodehelper_exec_async+0x4c3/0x640 188c003555aSWan Jiabing ret_from_fork+0x35/0x40 189c003555aSWan Jiabing 190c003555aSWan Jiabing The buggy address belongs to the object at ffff8801f44ec300 191c003555aSWan Jiabing which belongs to the cache kmalloc-128 of size 128 192c003555aSWan Jiabing The buggy address is located 123 bytes inside of 193c003555aSWan Jiabing 128-byte region [ffff8801f44ec300, ffff8801f44ec380) 194c003555aSWan Jiabing The buggy address belongs to the page: 195c003555aSWan Jiabing page:ffffea0007d13b00 count:1 mapcount:0 mapping:ffff8801f7001640 index:0x0 196c003555aSWan Jiabing flags: 0x200000000000100(slab) 197c003555aSWan Jiabing raw: 0200000000000100 ffffea0007d11dc0 0000001a0000001a ffff8801f7001640 198c003555aSWan Jiabing raw: 0000000000000000 0000000080150015 00000001ffffffff 0000000000000000 199c003555aSWan Jiabing page dumped because: kasan: bad access detected 200c003555aSWan Jiabing 201c003555aSWan Jiabing Memory state around the buggy address: 202c003555aSWan Jiabing ffff8801f44ec200: fc fc fc fc fc fc fc fc fb fb fb fb fb fb fb fb 203c003555aSWan Jiabing ffff8801f44ec280: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc 204c003555aSWan Jiabing >ffff8801f44ec300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 205c003555aSWan Jiabing ^ 206c003555aSWan Jiabing ffff8801f44ec380: fc fc fc fc fc fc fc fc fb fb fb fb fb fb fb fb 207c003555aSWan Jiabing ffff8801f44ec400: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc 208c003555aSWan Jiabing ================================================================== 209c003555aSWan Jiabing 210c003555aSWan Jiabing报告标题总结了发生的错误类型以及导致该错误的访问类型。紧随其后的是错误访问的 211c003555aSWan Jiabing堆栈跟踪、所访问内存分配位置的堆栈跟踪(对于访问了slab对象的情况)以及对象 212c003555aSWan Jiabing被释放的位置的堆栈跟踪(对于访问已释放内存的问题报告)。接下来是对访问的 213c003555aSWan Jiabingslab对象的描述以及关于访问的内存页的信息。 214c003555aSWan Jiabing 215c003555aSWan Jiabing最后,报告展示了访问地址周围的内存状态。在内部,KASAN单独跟踪每个内存颗粒的 216c003555aSWan Jiabing内存状态,根据KASAN模式分为8或16个对齐字节。报告的内存状态部分中的每个数字 217c003555aSWan Jiabing都显示了围绕访问地址的其中一个内存颗粒的状态。 218c003555aSWan Jiabing 219c003555aSWan Jiabing对于通用KASAN,每个内存颗粒的大小为8个字节。每个颗粒的状态被编码在一个影子字节 220c003555aSWan Jiabing中。这8个字节可以是可访问的,部分访问的,已释放的或成为Redzone的一部分。KASAN 221c003555aSWan Jiabing对每个影子字节使用以下编码:00表示对应内存区域的所有8个字节都可以访问;数字N 222c003555aSWan Jiabing(1 <= N <= 7)表示前N个字节可访问,其他(8 - N)个字节不可访问;任何负值都表示 223c003555aSWan Jiabing无法访问整个8字节。KASAN使用不同的负值来区分不同类型的不可访问内存,如redzones 224c003555aSWan Jiabing或已释放的内存(参见 mm/kasan/kasan.h)。 225c003555aSWan Jiabing 226c003555aSWan Jiabing在上面的报告中,箭头指向影子字节 ``03`` ,表示访问的地址是部分可访问的。 227c003555aSWan Jiabing 228c003555aSWan Jiabing对于基于标签的KASAN模式,报告最后的部分显示了访问地址周围的内存标签 229c003555aSWan Jiabing(参考 `实施细则`_ 章节)。 230c003555aSWan Jiabing 231c003555aSWan Jiabing请注意,KASAN错误标题(如 ``slab-out-of-bounds`` 或 ``use-after-free`` ) 232c003555aSWan Jiabing是尽量接近的:KASAN根据其拥有的有限信息打印出最可能的错误类型。错误的实际类型 233c003555aSWan Jiabing可能会有所不同。 234c003555aSWan Jiabing 235c003555aSWan Jiabing通用KASAN还报告两个辅助调用堆栈跟踪。这些堆栈跟踪指向代码中与对象交互但不直接 236c003555aSWan Jiabing出现在错误访问堆栈跟踪中的位置。目前,这包括 call_rcu() 和排队的工作队列。 237c003555aSWan Jiabing 238*2fca11bbSDongliang MuCONFIG_KASAN_EXTRA_INFO 239*2fca11bbSDongliang Mu~~~~~~~~~~~~~~~~~~~~~~~ 240*2fca11bbSDongliang Mu 241*2fca11bbSDongliang Mu启用 CONFIG_KASAN_EXTRA_INFO 选项允许 KASAN 记录和报告更多信息。目前支持的 242*2fca11bbSDongliang Mu额外信息包括分配和释放时的 CPU 编号和时间戳。更多的信息可以帮助找到内核错误的原因, 243*2fca11bbSDongliang Mu并将错误与其他系统事件关联起来,但代价是用额外的内存来记录更多信息(有关更多 244*2fca11bbSDongliang Mu开销的细节,请参见 CONFIG_KASAN_EXTRA_INFO 选项的帮助文本)。 245*2fca11bbSDongliang Mu 246*2fca11bbSDongliang Mu以下为 CONFIG_KASAN_EXTRA_INFO 开启后的报告(仅显示不同部分):: 247*2fca11bbSDongliang Mu 248*2fca11bbSDongliang Mu ================================================================== 249*2fca11bbSDongliang Mu ... 250*2fca11bbSDongliang Mu Allocated by task 134 on cpu 5 at 229.133855s: 251*2fca11bbSDongliang Mu ... 252*2fca11bbSDongliang Mu Freed by task 136 on cpu 3 at 230.199335s: 253*2fca11bbSDongliang Mu ... 254*2fca11bbSDongliang Mu ================================================================== 255*2fca11bbSDongliang Mu 256c003555aSWan Jiabing实施细则 257c003555aSWan Jiabing-------- 258c003555aSWan Jiabing 259c003555aSWan Jiabing通用KASAN 260c003555aSWan Jiabing~~~~~~~~~ 261c003555aSWan Jiabing 262c003555aSWan Jiabing软件KASAN模式使用影子内存来记录每个内存字节是否可以安全访问,并使用编译时工具 263c003555aSWan Jiabing在每次内存访问之前插入影子内存检查。 264c003555aSWan Jiabing 265c003555aSWan Jiabing通用KASAN将1/8的内核内存专用于其影子内存(16TB以覆盖x86_64上的128TB),并使用 266c003555aSWan Jiabing具有比例和偏移量的直接映射将内存地址转换为其相应的影子地址。 267c003555aSWan Jiabing 268c003555aSWan Jiabing这是将地址转换为其相应影子地址的函数:: 269c003555aSWan Jiabing 270c003555aSWan Jiabing static inline void *kasan_mem_to_shadow(const void *addr) 271c003555aSWan Jiabing { 272c003555aSWan Jiabing return (void *)((unsigned long)addr >> KASAN_SHADOW_SCALE_SHIFT) 273c003555aSWan Jiabing + KASAN_SHADOW_OFFSET; 274c003555aSWan Jiabing } 275c003555aSWan Jiabing 276c003555aSWan Jiabing在这里 ``KASAN_SHADOW_SCALE_SHIFT = 3`` 。 277c003555aSWan Jiabing 278c003555aSWan Jiabing编译时工具用于插入内存访问检查。编译器在每次访问大小为1、2、4、8或16的内存之前 279c003555aSWan Jiabing插入函数调用( ``__asan_load*(addr)`` , ``__asan_store*(addr)``)。这些函数通过 280c003555aSWan Jiabing检查相应的影子内存来检查内存访问是否有效。 281c003555aSWan Jiabing 282c003555aSWan Jiabing使用inline插桩,编译器不进行函数调用,而是直接插入代码来检查影子内存。此选项 283c003555aSWan Jiabing显著地增大了内核体积,但与outline插桩内核相比,它提供了x1.1-x2的性能提升。 284c003555aSWan Jiabing 285c003555aSWan Jiabing通用KASAN是唯一一种通过隔离延迟重新使用已释放对象的模式 286c003555aSWan Jiabing(参见 mm/kasan/quarantine.c 以了解实现)。 287c003555aSWan Jiabing 288c003555aSWan Jiabing基于软件标签的KASAN模式 289c003555aSWan Jiabing~~~~~~~~~~~~~~~~~~~~~~~ 290c003555aSWan Jiabing 291c003555aSWan Jiabing基于软件标签的KASAN使用软件内存标签方法来检查访问有效性。目前仅针对arm64架构实现。 292c003555aSWan Jiabing 293c003555aSWan Jiabing基于软件标签的KASAN使用arm64 CPU的顶部字节忽略(TBI)特性在内核指针的顶部字节中 294c003555aSWan Jiabing存储一个指针标签。它使用影子内存来存储与每个16字节内存单元相关的内存标签(因此, 295c003555aSWan Jiabing它将内核内存的1/16专用于影子内存)。 296c003555aSWan Jiabing 297c003555aSWan Jiabing在每次内存分配时,基于软件标签的KASAN都会生成一个随机标签,用这个标签标记分配 298c003555aSWan Jiabing的内存,并将相同的标签嵌入到返回的指针中。 299c003555aSWan Jiabing 300c003555aSWan Jiabing基于软件标签的KASAN使用编译时工具在每次内存访问之前插入检查。这些检查确保正在 301c003555aSWan Jiabing访问的内存的标签等于用于访问该内存的指针的标签。如果标签不匹配,基于软件标签 302c003555aSWan Jiabing的KASAN会打印错误报告。 303c003555aSWan Jiabing 304c003555aSWan Jiabing基于软件标签的KASAN也有两种插桩模式(outline,发出回调来检查内存访问;inline, 305c003555aSWan Jiabing执行内联的影子内存检查)。使用outline插桩模式,会从执行访问检查的函数打印错误 306c003555aSWan Jiabing报告。使用inline插桩,编译器会发出 ``brk`` 指令,并使用专用的 ``brk`` 处理程序 307c003555aSWan Jiabing来打印错误报告。 308c003555aSWan Jiabing 309c003555aSWan Jiabing基于软件标签的KASAN使用0xFF作为匹配所有指针标签(不检查通过带有0xFF指针标签 310c003555aSWan Jiabing的指针进行的访问)。值0xFE当前保留用于标记已释放的内存区域。 311c003555aSWan Jiabing 312c003555aSWan Jiabing 313c003555aSWan Jiabing基于硬件标签的KASAN模式 314c003555aSWan Jiabing~~~~~~~~~~~~~~~~~~~~~~~ 315c003555aSWan Jiabing 316c003555aSWan Jiabing基于硬件标签的KASAN在概念上类似于软件模式,但它是使用硬件内存标签作为支持而 317c003555aSWan Jiabing不是编译器插桩和影子内存。 318c003555aSWan Jiabing 319c003555aSWan Jiabing基于硬件标签的KASAN目前仅针对arm64架构实现,并且基于ARMv8.5指令集架构中引入 320c003555aSWan Jiabing的arm64内存标记扩展(MTE)和最高字节忽略(TBI)。 321c003555aSWan Jiabing 322c003555aSWan Jiabing特殊的arm64指令用于为每次内存分配指定内存标签。相同的标签被指定给指向这些分配 323c003555aSWan Jiabing的指针。在每次内存访问时,硬件确保正在访问的内存的标签等于用于访问该内存的指针 324c003555aSWan Jiabing的标签。如果标签不匹配,则会生成故障并打印报告。 325c003555aSWan Jiabing 326c003555aSWan Jiabing基于硬件标签的KASAN使用0xFF作为匹配所有指针标签(不检查通过带有0xFF指针标签的 327c003555aSWan Jiabing指针进行的访问)。值0xFE当前保留用于标记已释放的内存区域。 328c003555aSWan Jiabing 329c003555aSWan Jiabing如果硬件不支持MTE(ARMv8.5之前),则不会启用基于硬件标签的KASAN。在这种情况下, 330c003555aSWan Jiabing所有KASAN引导参数都将被忽略。 331c003555aSWan Jiabing 332c003555aSWan Jiabing请注意,启用CONFIG_KASAN_HW_TAGS始终会导致启用内核中的TBI。即使提供了 333c003555aSWan Jiabing``kasan.mode=off`` 或硬件不支持MTE(但支持TBI)。 334c003555aSWan Jiabing 335c003555aSWan Jiabing基于硬件标签的KASAN只报告第一个发现的错误。之后,MTE标签检查将被禁用。 336c003555aSWan Jiabing 337c003555aSWan Jiabing影子内存 338c003555aSWan Jiabing-------- 339c003555aSWan Jiabing 340507f4879SYanteng Si本节的内容只适用于软件KASAN模式。 341507f4879SYanteng Si 342c003555aSWan Jiabing内核将内存映射到地址空间的几个不同部分。内核虚拟地址的范围很大:没有足够的真实 343c003555aSWan Jiabing内存来支持内核可以访问的每个地址的真实影子区域。因此,KASAN只为地址空间的某些 344c003555aSWan Jiabing部分映射真实的影子。 345c003555aSWan Jiabing 346c003555aSWan Jiabing默认行为 347c003555aSWan Jiabing~~~~~~~~ 348c003555aSWan Jiabing 349c003555aSWan Jiabing默认情况下,体系结构仅将实际内存映射到用于线性映射的阴影区域(以及可能的其他 350c003555aSWan Jiabing小区域)。对于所有其他区域 —— 例如vmalloc和vmemmap空间 —— 一个只读页面被映射 351c003555aSWan Jiabing到阴影区域上。这个只读的影子页面声明所有内存访问都是允许的。 352c003555aSWan Jiabing 353c003555aSWan Jiabing这给模块带来了一个问题:它们不存在于线性映射中,而是存在于专用的模块空间中。 354c003555aSWan Jiabing通过连接模块分配器,KASAN临时映射真实的影子内存以覆盖它们。例如,这允许检测 355c003555aSWan Jiabing对模块全局变量的无效访问。 356c003555aSWan Jiabing 357c003555aSWan Jiabing这也造成了与 ``VMAP_STACK`` 的不兼容:如果堆栈位于vmalloc空间中,它将被分配 358c003555aSWan Jiabing只读页面的影子内存,并且内核在尝试为堆栈变量设置影子数据时会出错。 359c003555aSWan Jiabing 360c003555aSWan JiabingCONFIG_KASAN_VMALLOC 361c003555aSWan Jiabing~~~~~~~~~~~~~~~~~~~~ 362c003555aSWan Jiabing 363c003555aSWan Jiabing使用 ``CONFIG_KASAN_VMALLOC`` ,KASAN可以以更大的内存使用为代价覆盖vmalloc 364507f4879SYanteng Si空间。目前,这在arm64、x86、riscv、s390和powerpc上受支持。 365c003555aSWan Jiabing 366c003555aSWan Jiabing这通过连接到vmalloc和vmap并动态分配真实的影子内存来支持映射。 367c003555aSWan Jiabing 368c003555aSWan Jiabingvmalloc空间中的大多数映射都很小,需要不到一整页的阴影空间。因此,为每个映射 369c003555aSWan Jiabing分配一个完整的影子页面将是一种浪费。此外,为了确保不同的映射使用不同的影子 370c003555aSWan Jiabing页面,映射必须与 ``KASAN_GRANULE_SIZE * PAGE_SIZE`` 对齐。 371c003555aSWan Jiabing 372c003555aSWan Jiabing相反,KASAN跨多个映射共享后备空间。当vmalloc空间中的映射使用影子区域的特定 373c003555aSWan Jiabing页面时,它会分配一个后备页面。此页面稍后可以由其他vmalloc映射共享。 374c003555aSWan Jiabing 375c003555aSWan JiabingKASAN连接到vmap基础架构以懒清理未使用的影子内存。 376c003555aSWan Jiabing 377c003555aSWan Jiabing为了避免交换映射的困难,KASAN预测覆盖vmalloc空间的阴影区域部分将不会被早期 378c003555aSWan Jiabing的阴影页面覆盖,但是将不会被映射。这将需要更改特定于arch的代码。 379c003555aSWan Jiabing 380c003555aSWan Jiabing这允许在x86上支持 ``VMAP_STACK`` ,并且可以简化对没有固定模块区域的架构的支持。 381c003555aSWan Jiabing 382c003555aSWan Jiabing对于开发者 383c003555aSWan Jiabing---------- 384c003555aSWan Jiabing 385c003555aSWan Jiabing忽略访问 386c003555aSWan Jiabing~~~~~~~~ 387c003555aSWan Jiabing 388c003555aSWan Jiabing软件KASAN模式使用编译器插桩来插入有效性检查。此类检测可能与内核的某些部分 389c003555aSWan Jiabing不兼容,因此需要禁用。 390c003555aSWan Jiabing 391c003555aSWan Jiabing内核的其他部分可能会访问已分配对象的元数据。通常,KASAN会检测并报告此类访问, 392c003555aSWan Jiabing但在某些情况下(例如,在内存分配器中),这些访问是有效的。 393c003555aSWan Jiabing 394c003555aSWan Jiabing对于软件KASAN模式,要禁用特定文件或目录的检测,请将 ``KASAN_SANITIZE`` 添加 395c003555aSWan Jiabing到相应的内核Makefile中: 396c003555aSWan Jiabing 397c003555aSWan Jiabing- 对于单个文件(例如,main.o):: 398c003555aSWan Jiabing 399c003555aSWan Jiabing KASAN_SANITIZE_main.o := n 400c003555aSWan Jiabing 401c003555aSWan Jiabing- 对于一个目录下的所有文件:: 402c003555aSWan Jiabing 403c003555aSWan Jiabing KASAN_SANITIZE := n 404c003555aSWan Jiabing 405c003555aSWan Jiabing对于软件KASAN模式,要在每个函数的基础上禁用检测,请使用KASAN特定的 406c003555aSWan Jiabing``__no_sanitize_address`` 函数属性或通用的 ``noinstr`` 。 407c003555aSWan Jiabing 408c003555aSWan Jiabing请注意,禁用编译器插桩(基于每个文件或每个函数)会使KASAN忽略在软件KASAN模式 409c003555aSWan Jiabing的代码中直接发生的访问。当访问是间接发生的(通过调用检测函数)或使用没有编译器 410c003555aSWan Jiabing插桩的基于硬件标签的模式时,它没有帮助。 411c003555aSWan Jiabing 412c003555aSWan Jiabing对于软件KASAN模式,要在当前任务的一部分内核代码中禁用KASAN报告,请使用 413c003555aSWan Jiabing``kasan_disable_current()``/``kasan_enable_current()`` 部分注释这部分代码。 414c003555aSWan Jiabing这也会禁用通过函数调用发生的间接访问的报告。 415c003555aSWan Jiabing 416507f4879SYanteng Si对于基于标签的KASAN模式,要禁用访问检查,请使用 ``kasan_reset_tag()`` 或 417507f4879SYanteng Si``page_kasan_tag_reset()`` 。请注意,通过 ``page_kasan_tag_reset()`` 418507f4879SYanteng Si临时禁用访问检查需要通过 ``page_kasan_tag`` / ``page_kasan_tag_set`` 保 419507f4879SYanteng Si存和恢复每页KASAN标签。 420c003555aSWan Jiabing 421c003555aSWan Jiabing测试 422c003555aSWan Jiabing~~~~ 423c003555aSWan Jiabing 424c003555aSWan Jiabing有一些KASAN测试可以验证KASAN是否正常工作并可以检测某些类型的内存损坏。 425c003555aSWan Jiabing测试由两部分组成: 426c003555aSWan Jiabing 427c003555aSWan Jiabing1. 与KUnit测试框架集成的测试。使用 ``CONFIG_KASAN_KUNIT_TEST`` 启用。 428c003555aSWan Jiabing这些测试可以通过几种不同的方式自动运行和部分验证;请参阅下面的说明。 429c003555aSWan Jiabing 430c003555aSWan Jiabing2. 与KUnit不兼容的测试。使用 ``CONFIG_KASAN_MODULE_TEST`` 启用并且只能作为模块 431c003555aSWan Jiabing运行。这些测试只能通过加载内核模块并检查内核日志以获取KASAN报告来手动验证。 432c003555aSWan Jiabing 433c003555aSWan Jiabing如果检测到错误,每个KUnit兼容的KASAN测试都会打印多个KASAN报告之一,然后测试打印 434c003555aSWan Jiabing其编号和状态。 435c003555aSWan Jiabing 436c003555aSWan Jiabing当测试通过:: 437c003555aSWan Jiabing 438c003555aSWan Jiabing ok 28 - kmalloc_double_kzfree 439c003555aSWan Jiabing 440c003555aSWan Jiabing当由于 ``kmalloc`` 失败而导致测试失败时:: 441c003555aSWan Jiabing 442d8310914STiezhu Yang # kmalloc_large_oob_right: ASSERTION FAILED at mm/kasan/kasan_test.c:245 443c003555aSWan Jiabing Expected ptr is not null, but is 444d8310914STiezhu Yang not ok 5 - kmalloc_large_oob_right 445c003555aSWan Jiabing 446c003555aSWan Jiabing当由于缺少KASAN报告而导致测试失败时:: 447c003555aSWan Jiabing 448d8310914STiezhu Yang # kmalloc_double_kzfree: EXPECTATION FAILED at mm/kasan/kasan_test.c:709 449507f4879SYanteng Si KASAN failure expected in "kfree_sensitive(ptr)", but none occurred 450d8310914STiezhu Yang not ok 28 - kmalloc_double_kzfree 451507f4879SYanteng Si 452c003555aSWan Jiabing 453c003555aSWan Jiabing最后打印所有KASAN测试的累积状态。成功:: 454c003555aSWan Jiabing 455c003555aSWan Jiabing ok 1 - kasan 456c003555aSWan Jiabing 457c003555aSWan Jiabing或者,如果其中一项测试失败:: 458c003555aSWan Jiabing 459c003555aSWan Jiabing not ok 1 - kasan 460c003555aSWan Jiabing 461c003555aSWan Jiabing有几种方法可以运行与KUnit兼容的KASAN测试。 462c003555aSWan Jiabing 463c003555aSWan Jiabing1. 可加载模块 464c003555aSWan Jiabing 465c003555aSWan Jiabing 启用 ``CONFIG_KUNIT`` 后,KASAN-KUnit测试可以构建为可加载模块,并通过使用 466d8310914STiezhu Yang ``insmod`` 或 ``modprobe`` 加载 ``kasan_test.ko`` 来运行。 467c003555aSWan Jiabing 468c003555aSWan Jiabing2. 内置 469c003555aSWan Jiabing 470c003555aSWan Jiabing 通过内置 ``CONFIG_KUNIT`` ,也可以内置KASAN-KUnit测试。在这种情况下, 471c003555aSWan Jiabing 测试将在启动时作为后期初始化调用运行。 472c003555aSWan Jiabing 473c003555aSWan Jiabing3. 使用kunit_tool 474c003555aSWan Jiabing 475c003555aSWan Jiabing 通过内置 ``CONFIG_KUNIT`` 和 ``CONFIG_KASAN_KUNIT_TEST`` ,还可以使用 476c003555aSWan Jiabing ``kunit_tool`` 以更易读的方式查看KUnit测试结果。这不会打印通过测试 477c003555aSWan Jiabing 的KASAN报告。有关 ``kunit_tool`` 更多最新信息,请参阅 478c003555aSWan Jiabing `KUnit文档 <https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html>`_ 。 479c003555aSWan Jiabing 480c003555aSWan Jiabing.. _KUnit: https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html 481