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