1 .. SPDX-License-Identifier: GPL-2.0 1 .. SPDX-License-Identifier: GPL-2.0 2 2 3 .. include:: ../disclaimer-zh_TW.rst 3 .. include:: ../disclaimer-zh_TW.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 Kernel Address SANitizer(KASAN)是一種動態內存安全錯誤檢測工具,主要功能是 15 檢查內存越界訪問和使用已釋放內 15 檢查內存越界訪問和使用已釋放內存的問題。 16 16 17 KASAN有三種模式: 17 KASAN有三種模式: 18 18 19 1. 通用KASAN 19 1. 通用KASAN 20 2. 基於軟件標籤的KASAN 20 2. 基於軟件標籤的KASAN 21 3. 基於硬件標籤的KASAN 21 3. 基於硬件標籤的KASAN 22 22 23 用CONFIG_KASAN_GENERIC啓用的通用KASAN, 23 用CONFIG_KASAN_GENERIC啓用的通用KASAN,是用於調試的模式,類似於用戶空 24 間的ASan。這種模式在許多CPU架構上 24 間的ASan。這種模式在許多CPU架構上都被支持,但它有明顯的性能和內存開銷。 25 25 26 基於軟件標籤的KASAN或SW_TAGS KASAN, 26 基於軟件標籤的KASAN或SW_TAGS KASAN,通過CONFIG_KASAN_SW_TAGS啓用, 27 可以用於調試和自我測試,類似於 27 可以用於調試和自我測試,類似於用戶空間HWASan。這種模式只支持arm64,但其 28 適度的內存開銷允許在內存受限的 28 適度的內存開銷允許在內存受限的設備上用真實的工作負載進行測試。 29 29 30 基於硬件標籤的KASAN或HW_TAGS KASAN, 30 基於硬件標籤的KASAN或HW_TAGS KASAN,用CONFIG_KASAN_HW_TAGS啓用,被 31 用作現場內存錯誤檢測器或作爲安 31 用作現場內存錯誤檢測器或作爲安全緩解的模式。這種模式只在支持MTE(內存標籤 32 擴展)的arm64 CPU上工作,但它的內 32 擴展)的arm64 CPU上工作,但它的內存和性能開銷很低,因此可以在生產中使用。 33 33 34 關於每種KASAN模式的內存和性能影 34 關於每種KASAN模式的內存和性能影響的細節,請參見相應的Kconfig選項的描述。 35 35 36 通用模式和基於軟件標籤的模式通 36 通用模式和基於軟件標籤的模式通常被稱爲軟件模式。基於軟件標籤的模式和基於 37 硬件標籤的模式被稱爲基於標籤的 37 硬件標籤的模式被稱爲基於標籤的模式。 38 38 39 支持 39 支持 40 ---- 40 ---- 41 41 42 體系架構 42 體系架構 43 ~~~~~~~~ 43 ~~~~~~~~ 44 44 45 在x86_64、arm、arm64、powerpc、riscv、s3 45 在x86_64、arm、arm64、powerpc、riscv、s390、xtensa和loongarch上支持通用KASAN, 46 而基於標籤的KASAN模式只在arm64上支 46 而基於標籤的KASAN模式只在arm64上支持。 47 47 48 編譯器 48 編譯器 49 ~~~~~~ 49 ~~~~~~ 50 50 51 軟件KASAN模式使用編譯時工具在每 51 軟件KASAN模式使用編譯時工具在每個內存訪問之前插入有效性檢查,因此需要一個 52 提供支持的編譯器版本。基於硬件 52 提供支持的編譯器版本。基於硬件標籤的模式依靠硬件來執行這些檢查,但仍然需要 53 一個支持內存標籤指令的編譯器版 53 一個支持內存標籤指令的編譯器版本。 54 54 55 通用KASAN需要GCC 8.3.0版本或更高版 55 通用KASAN需要GCC 8.3.0版本或更高版本,或者內核支持的任何Clang版本。 56 56 57 基於軟件標籤的KASAN需要GCC 11+或者 57 基於軟件標籤的KASAN需要GCC 11+或者內核支持的任何Clang版本。 58 58 59 基於硬件標籤的KASAN需要GCC 10+或Clan 59 基於硬件標籤的KASAN需要GCC 10+或Clang 12+。 60 60 61 內存類型 61 內存類型 62 ~~~~~~~~ 62 ~~~~~~~~ 63 63 64 通用KASAN支持在所有的slab、page_alloc 64 通用KASAN支持在所有的slab、page_alloc、vmap、vmalloc、堆棧和全局內存 65 中查找錯誤。 65 中查找錯誤。 66 66 67 基於軟件標籤的KASAN支持slab、page_al 67 基於軟件標籤的KASAN支持slab、page_alloc、vmalloc和堆棧內存。 68 68 69 基於硬件標籤的KASAN支持slab、page_al 69 基於硬件標籤的KASAN支持slab、page_alloc和不可執行的vmalloc內存。 70 70 71 對於slab,兩種軟件KASAN模式都支持S 71 對於slab,兩種軟件KASAN模式都支持SLUB和SLAB分配器,而基於硬件標籤的 72 KASAN只支持SLUB。 72 KASAN只支持SLUB。 73 73 74 用法 74 用法 75 ---- 75 ---- 76 76 77 要啓用KASAN,請使用以下命令配置 77 要啓用KASAN,請使用以下命令配置內核:: 78 78 79 CONFIG_KASAN=y 79 CONFIG_KASAN=y 80 80 81 同時在 ``CONFIG_KASAN_GENERIC`` (啓用通 81 同時在 ``CONFIG_KASAN_GENERIC`` (啓用通用KASAN模式), ``CONFIG_KASAN_SW_TAGS`` 82 (啓用基於硬件標籤的KASAN模式),和 82 (啓用基於硬件標籤的KASAN模式),和 ``CONFIG_KASAN_HW_TAGS`` (啓用基於硬件標籤 83 的KASAN模式)之間進行選擇。 83 的KASAN模式)之間進行選擇。 84 84 85 對於軟件模式,還可以在 ``CONFIG_KAS 85 對於軟件模式,還可以在 ``CONFIG_KASAN_OUTLINE`` 和 ``CONFIG_KASAN_INLINE`` 86 之間進行選擇。outline和inline是編譯 86 之間進行選擇。outline和inline是編譯器插樁類型。前者產生較小的二進制文件, 87 而後者快2倍。 87 而後者快2倍。 88 88 89 要將受影響的slab對象的alloc和free堆 89 要將受影響的slab對象的alloc和free堆棧跟蹤包含到報告中,請啓用 90 ``CONFIG_STACKTRACE`` 。要包括受影響物 90 ``CONFIG_STACKTRACE`` 。要包括受影響物理頁面的分配和釋放堆棧跟蹤的話, 91 請啓用 ``CONFIG_PAGE_OWNER`` 並使用 ``pa 91 請啓用 ``CONFIG_PAGE_OWNER`` 並使用 ``page_owner=on`` 進行引導。 92 92 93 啓動參數 93 啓動參數 94 ~~~~~~~~ 94 ~~~~~~~~ 95 95 96 KASAN受到通用 ``panic_on_warn`` 命令行 96 KASAN受到通用 ``panic_on_warn`` 命令行參數的影響。當它被啓用時,KASAN 97 在打印出錯誤報告後會使內核恐慌 97 在打印出錯誤報告後會使內核恐慌。 98 98 99 默認情況下,KASAN只對第一個無效 99 默認情況下,KASAN只對第一個無效的內存訪問打印錯誤報告。使用 100 ``kasan_multi_shot``,KASAN對每一個無效 100 ``kasan_multi_shot``,KASAN對每一個無效的訪問都打印一份報告。這會禁用 101 了KASAN報告的 ``panic_on_warn``。 101 了KASAN報告的 ``panic_on_warn``。 102 102 103 另外,獨立於 ``panic_on_warn`` 、 ``kas 103 另外,獨立於 ``panic_on_warn`` 、 ``kasan.fault=`` boot參數可以用 104 來控制恐慌和報告行爲。 104 來控制恐慌和報告行爲。 105 105 106 - ``kasan.fault=report`` 或 ``=panic`` 控制 106 - ``kasan.fault=report`` 或 ``=panic`` 控制是否只打印KASAN report或 107 同時使內核恐慌(默認: ``report`` 107 同時使內核恐慌(默認: ``report`` )。即使 ``kasan_multi_shot`` 被 108 啓用,恐慌也會發生。 108 啓用,恐慌也會發生。 109 109 110 基於軟件和硬件標籤的KASAN模式( 110 基於軟件和硬件標籤的KASAN模式(見下面關於各種模式的部分)支持改變堆棧跟 111 蹤收集行爲: 111 蹤收集行爲: 112 112 113 - ``kasan.stacktrace=off`` 或 ``=on`` 禁用 113 - ``kasan.stacktrace=off`` 或 ``=on`` 禁用或啓用分配和釋放堆棧痕 114 跡的收集(默認: ``on`` )。 114 跡的收集(默認: ``on`` )。 115 115 116 - ``kasan.stack_ring_size=<number of entries>` 116 - ``kasan.stack_ring_size=<number of entries>`` 指定堆棧環的條 117 目數(默認: ``32768`` )。 117 目數(默認: ``32768`` )。 118 118 119 基於硬件標籤的KASAN模式是爲了在 119 基於硬件標籤的KASAN模式是爲了在生產中作爲一種安全緩解措施使用。因此,它 120 支持額外的啓動參數,允許完全禁 120 支持額外的啓動參數,允許完全禁用KASAN或控制其功能。 121 121 122 - ``kasan=off`` 或 ``=on`` 控制KASAN是否 122 - ``kasan=off`` 或 ``=on`` 控制KASAN是否被啓用(默認: ``on`` )。 123 123 124 - ``kasan.mode=sync``, ``=async`` or ``=asymm` 124 - ``kasan.mode=sync``, ``=async`` or ``=asymm`` 控制KASAN是否 125 被配置爲同步、異步或非對稱的 125 被配置爲同步、異步或非對稱的執行模式(默認: ``同步`` )。 126 同步模式:當標籤檢查異常發生 126 同步模式:當標籤檢查異常發生時,會立即檢測到不良訪問。 127 異步模式:不良訪問的檢測是延 127 異步模式:不良訪問的檢測是延遲的。當標籤檢查異常發生時,信息被存儲在硬 128 件中(對於arm64來說是在TFSR_EL1寄 128 件中(對於arm64來說是在TFSR_EL1寄存器中)。內核週期性地檢查硬件,並\ 129 且只在這些檢查中報告標籤異常 129 且只在這些檢查中報告標籤異常。 130 非對稱模式:讀取時同步檢測不 130 非對稱模式:讀取時同步檢測不良訪問,寫入時異步檢測。 131 131 132 - ``kasan.vmalloc=off`` or ``=on`` 禁用或 132 - ``kasan.vmalloc=off`` or ``=on`` 禁用或啓用vmalloc分配的標記(默認: ``on`` )。 133 133 134 錯誤報告 134 錯誤報告 135 ~~~~~~~~ 135 ~~~~~~~~ 136 136 137 典型的KASAN報告如下所示:: 137 典型的KASAN報告如下所示:: 138 138 139 ========================================== 139 ================================================================== 140 BUG: KASAN: slab-out-of-bounds in kmalloc_ !! 140 BUG: KASAN: slab-out-of-bounds in kmalloc_oob_right+0xa8/0xbc [test_kasan] 141 Write of size 1 at addr ffff8801f44ec37b b 141 Write of size 1 at addr ffff8801f44ec37b by task insmod/2760 142 142 143 CPU: 1 PID: 2760 Comm: insmod Not tainted 143 CPU: 1 PID: 2760 Comm: insmod Not tainted 4.19.0-rc3+ #698 144 Hardware name: QEMU Standard PC (i440FX + 144 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 145 Call Trace: 145 Call Trace: 146 dump_stack+0x94/0xd8 146 dump_stack+0x94/0xd8 147 print_address_description+0x73/0x280 147 print_address_description+0x73/0x280 148 kasan_report+0x144/0x187 148 kasan_report+0x144/0x187 149 __asan_report_store1_noabort+0x17/0x20 149 __asan_report_store1_noabort+0x17/0x20 150 kmalloc_oob_right+0xa8/0xbc [kasan_test] !! 150 kmalloc_oob_right+0xa8/0xbc [test_kasan] 151 kmalloc_tests_init+0x16/0x700 [kasan_test !! 151 kmalloc_tests_init+0x16/0x700 [test_kasan] 152 do_one_initcall+0xa5/0x3ae 152 do_one_initcall+0xa5/0x3ae 153 do_init_module+0x1b6/0x547 153 do_init_module+0x1b6/0x547 154 load_module+0x75df/0x8070 154 load_module+0x75df/0x8070 155 __do_sys_init_module+0x1c6/0x200 155 __do_sys_init_module+0x1c6/0x200 156 __x64_sys_init_module+0x6e/0xb0 156 __x64_sys_init_module+0x6e/0xb0 157 do_syscall_64+0x9f/0x2c0 157 do_syscall_64+0x9f/0x2c0 158 entry_SYSCALL_64_after_hwframe+0x44/0xa9 158 entry_SYSCALL_64_after_hwframe+0x44/0xa9 159 RIP: 0033:0x7f96443109da 159 RIP: 0033:0x7f96443109da 160 RSP: 002b:00007ffcf0b51b08 EFLAGS: 0000020 160 RSP: 002b:00007ffcf0b51b08 EFLAGS: 00000202 ORIG_RAX: 00000000000000af 161 RAX: ffffffffffffffda RBX: 000055dc3ee521a 161 RAX: ffffffffffffffda RBX: 000055dc3ee521a0 RCX: 00007f96443109da 162 RDX: 00007f96445cff88 RSI: 0000000000057a5 162 RDX: 00007f96445cff88 RSI: 0000000000057a50 RDI: 00007f9644992000 163 RBP: 000055dc3ee510b0 R08: 000000000000000 163 RBP: 000055dc3ee510b0 R08: 0000000000000003 R09: 0000000000000000 164 R10: 00007f964430cd0a R11: 000000000000020 164 R10: 00007f964430cd0a R11: 0000000000000202 R12: 00007f96445cff88 165 R13: 000055dc3ee51090 R14: 000000000000000 165 R13: 000055dc3ee51090 R14: 0000000000000000 R15: 0000000000000000 166 166 167 Allocated by task 2760: 167 Allocated by task 2760: 168 save_stack+0x43/0xd0 168 save_stack+0x43/0xd0 169 kasan_kmalloc+0xa7/0xd0 169 kasan_kmalloc+0xa7/0xd0 170 kmem_cache_alloc_trace+0xe1/0x1b0 170 kmem_cache_alloc_trace+0xe1/0x1b0 171 kmalloc_oob_right+0x56/0xbc [kasan_test] !! 171 kmalloc_oob_right+0x56/0xbc [test_kasan] 172 kmalloc_tests_init+0x16/0x700 [kasan_test !! 172 kmalloc_tests_init+0x16/0x700 [test_kasan] 173 do_one_initcall+0xa5/0x3ae 173 do_one_initcall+0xa5/0x3ae 174 do_init_module+0x1b6/0x547 174 do_init_module+0x1b6/0x547 175 load_module+0x75df/0x8070 175 load_module+0x75df/0x8070 176 __do_sys_init_module+0x1c6/0x200 176 __do_sys_init_module+0x1c6/0x200 177 __x64_sys_init_module+0x6e/0xb0 177 __x64_sys_init_module+0x6e/0xb0 178 do_syscall_64+0x9f/0x2c0 178 do_syscall_64+0x9f/0x2c0 179 entry_SYSCALL_64_after_hwframe+0x44/0xa9 179 entry_SYSCALL_64_after_hwframe+0x44/0xa9 180 180 181 Freed by task 815: 181 Freed by task 815: 182 save_stack+0x43/0xd0 182 save_stack+0x43/0xd0 183 __kasan_slab_free+0x135/0x190 183 __kasan_slab_free+0x135/0x190 184 kasan_slab_free+0xe/0x10 184 kasan_slab_free+0xe/0x10 185 kfree+0x93/0x1a0 185 kfree+0x93/0x1a0 186 umh_complete+0x6a/0xa0 186 umh_complete+0x6a/0xa0 187 call_usermodehelper_exec_async+0x4c3/0x64 187 call_usermodehelper_exec_async+0x4c3/0x640 188 ret_from_fork+0x35/0x40 188 ret_from_fork+0x35/0x40 189 189 190 The buggy address belongs to the object at 190 The buggy address belongs to the object at ffff8801f44ec300 191 which belongs to the cache kmalloc-128 of 191 which belongs to the cache kmalloc-128 of size 128 192 The buggy address is located 123 bytes ins 192 The buggy address is located 123 bytes inside of 193 128-byte region [ffff8801f44ec300, ffff88 193 128-byte region [ffff8801f44ec300, ffff8801f44ec380) 194 The buggy address belongs to the page: 194 The buggy address belongs to the page: 195 page:ffffea0007d13b00 count:1 mapcount:0 m 195 page:ffffea0007d13b00 count:1 mapcount:0 mapping:ffff8801f7001640 index:0x0 196 flags: 0x200000000000100(slab) 196 flags: 0x200000000000100(slab) 197 raw: 0200000000000100 ffffea0007d11dc0 000 197 raw: 0200000000000100 ffffea0007d11dc0 0000001a0000001a ffff8801f7001640 198 raw: 0000000000000000 0000000080150015 000 198 raw: 0000000000000000 0000000080150015 00000001ffffffff 0000000000000000 199 page dumped because: kasan: bad access det 199 page dumped because: kasan: bad access detected 200 200 201 Memory state around the buggy address: 201 Memory state around the buggy address: 202 ffff8801f44ec200: fc fc fc fc fc fc fc fc 202 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 203 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 204 >ffff8801f44ec300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 205 205 ^ 206 ffff8801f44ec380: fc fc fc fc fc fc fc fc 206 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 207 ffff8801f44ec400: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc 208 ========================================== 208 ================================================================== 209 209 210 報告標題總結了發生的錯誤類型以 210 報告標題總結了發生的錯誤類型以及導致該錯誤的訪問類型。緊隨其後的是錯誤訪問的 211 堆棧跟蹤、所訪問內存分配位置的 211 堆棧跟蹤、所訪問內存分配位置的堆棧跟蹤(對於訪問了slab對象的情況)以及對象 212 被釋放的位置的堆棧跟蹤(對於訪 212 被釋放的位置的堆棧跟蹤(對於訪問已釋放內存的問題報告)。接下來是對訪問的 213 slab對象的描述以及關於訪問的內存 213 slab對象的描述以及關於訪問的內存頁的信息。 214 214 215 最後,報告展示了訪問地址周圍的 215 最後,報告展示了訪問地址周圍的內存狀態。在內部,KASAN單獨跟蹤每個內存顆粒的 216 內存狀態,根據KASAN模式分爲8或16 216 內存狀態,根據KASAN模式分爲8或16個對齊字節。報告的內存狀態部分中的每個數字 217 都顯示了圍繞訪問地址的其中一個 217 都顯示了圍繞訪問地址的其中一個內存顆粒的狀態。 218 218 219 對於通用KASAN,每個內存顆粒的大 219 對於通用KASAN,每個內存顆粒的大小爲8個字節。每個顆粒的狀態被編碼在一個影子字節 220 中。這8個字節可以是可訪問的,部 220 中。這8個字節可以是可訪問的,部分訪問的,已釋放的或成爲Redzone的一部分。KASAN 221 對每個影子字節使用以下編碼:00表 221 對每個影子字節使用以下編碼:00表示對應內存區域的所有8個字節都可以訪問;數字N 222 (1 <= N <= 7)表示前N個字節可訪問, 222 (1 <= N <= 7)表示前N個字節可訪問,其他(8 - N)個字節不可訪問;任何負值都表示 223 無法訪問整個8字節。KASAN使用不同 223 無法訪問整個8字節。KASAN使用不同的負值來區分不同類型的不可訪問內存,如redzones 224 或已釋放的內存(參見 mm/kasan/kasan. 224 或已釋放的內存(參見 mm/kasan/kasan.h)。 225 225 226 在上面的報告中,箭頭指向影子字 226 在上面的報告中,箭頭指向影子字節 ``03`` ,表示訪問的地址是部分可訪問的。 227 227 228 對於基於標籤的KASAN模式,報告最 228 對於基於標籤的KASAN模式,報告最後的部分顯示了訪問地址周圍的內存標籤 229 (參考 `實施細則`_ 章節)。 229 (參考 `實施細則`_ 章節)。 230 230 231 請注意,KASAN錯誤標題(如 ``slab-out 231 請注意,KASAN錯誤標題(如 ``slab-out-of-bounds`` 或 ``use-after-free`` ) 232 是儘量接近的:KASAN根據其擁有的有 232 是儘量接近的:KASAN根據其擁有的有限信息打印出最可能的錯誤類型。錯誤的實際類型 233 可能會有所不同。 233 可能會有所不同。 234 234 235 通用KASAN還報告兩個輔助調用堆棧 235 通用KASAN還報告兩個輔助調用堆棧跟蹤。這些堆棧跟蹤指向代碼中與對象交互但不直接 236 出現在錯誤訪問堆棧跟蹤中的位置 236 出現在錯誤訪問堆棧跟蹤中的位置。目前,這包括 call_rcu() 和排隊的工作隊列。 237 237 238 實施細則 238 實施細則 239 -------- 239 -------- 240 240 241 通用KASAN 241 通用KASAN 242 ~~~~~~~~~ 242 ~~~~~~~~~ 243 243 244 軟件KASAN模式使用影子內存來記錄 244 軟件KASAN模式使用影子內存來記錄每個內存字節是否可以安全訪問,並使用編譯時工具 245 在每次內存訪問之前插入影子內存 245 在每次內存訪問之前插入影子內存檢查。 246 246 247 通用KASAN將1/8的內核內存專用於其 247 通用KASAN將1/8的內核內存專用於其影子內存(16TB以覆蓋x86_64上的128TB),並使用 248 具有比例和偏移量的直接映射將內 248 具有比例和偏移量的直接映射將內存地址轉換爲其相應的影子地址。 249 249 250 這是將地址轉換爲其相應影子地址 250 這是將地址轉換爲其相應影子地址的函數:: 251 251 252 static inline void *kasan_mem_to_shadow(co 252 static inline void *kasan_mem_to_shadow(const void *addr) 253 { 253 { 254 return (void *)((unsigned long)addr >> 254 return (void *)((unsigned long)addr >> KASAN_SHADOW_SCALE_SHIFT) 255 + KASAN_SHADOW_OFFSET; 255 + KASAN_SHADOW_OFFSET; 256 } 256 } 257 257 258 在這裏 ``KASAN_SHADOW_SCALE_SHIFT = 3`` 。 258 在這裏 ``KASAN_SHADOW_SCALE_SHIFT = 3`` 。 259 259 260 編譯時工具用於插入內存訪問檢查 260 編譯時工具用於插入內存訪問檢查。編譯器在每次訪問大小爲1、2、4、8或16的內存之前 261 插入函數調用( ``__asan_load*(addr)`` , ` 261 插入函數調用( ``__asan_load*(addr)`` , ``__asan_store*(addr)``)。這些函數通過 262 檢查相應的影子內存來檢查內存訪 262 檢查相應的影子內存來檢查內存訪問是否有效。 263 263 264 使用inline插樁,編譯器不進行函數 264 使用inline插樁,編譯器不進行函數調用,而是直接插入代碼來檢查影子內存。此選項 265 顯著地增大了內核體積,但與outline 265 顯著地增大了內核體積,但與outline插樁內核相比,它提供了x1.1-x2的性能提升。 266 266 267 通用KASAN是唯一一種通過隔離延遲 267 通用KASAN是唯一一種通過隔離延遲重新使用已釋放對象的模式 268 (參見 mm/kasan/quarantine.c 以瞭解實 268 (參見 mm/kasan/quarantine.c 以瞭解實現)。 269 269 270 基於軟件標籤的KASAN模式 270 基於軟件標籤的KASAN模式 271 ~~~~~~~~~~~~~~~~~~~~~~~ 271 ~~~~~~~~~~~~~~~~~~~~~~~ 272 272 273 基於軟件標籤的KASAN使用軟件內存 273 基於軟件標籤的KASAN使用軟件內存標籤方法來檢查訪問有效性。目前僅針對arm64架構實現。 274 274 275 基於軟件標籤的KASAN使用arm64 CPU的 275 基於軟件標籤的KASAN使用arm64 CPU的頂部字節忽略(TBI)特性在內核指針的頂部字節中 276 存儲一個指針標籤。它使用影子內 276 存儲一個指針標籤。它使用影子內存來存儲與每個16字節內存單元相關的內存標籤(因此, 277 它將內核內存的1/16專用於影子內存 277 它將內核內存的1/16專用於影子內存)。 278 278 279 在每次內存分配時,基於軟件標籤 279 在每次內存分配時,基於軟件標籤的KASAN都會生成一個隨機標籤,用這個標籤標記分配 280 的內存,並將相同的標籤嵌入到返 280 的內存,並將相同的標籤嵌入到返回的指針中。 281 281 282 基於軟件標籤的KASAN使用編譯時工 282 基於軟件標籤的KASAN使用編譯時工具在每次內存訪問之前插入檢查。這些檢查確保正在 283 訪問的內存的標籤等於用於訪問該 283 訪問的內存的標籤等於用於訪問該內存的指針的標籤。如果標籤不匹配,基於軟件標籤 284 的KASAN會打印錯誤報告。 284 的KASAN會打印錯誤報告。 285 285 286 基於軟件標籤的KASAN也有兩種插樁 286 基於軟件標籤的KASAN也有兩種插樁模式(outline,發出回調來檢查內存訪問;inline, 287 執行內聯的影子內存檢查)。使用o 287 執行內聯的影子內存檢查)。使用outline插樁模式,會從執行訪問檢查的函數打印錯誤 288 報告。使用inline插樁,編譯器會發 288 報告。使用inline插樁,編譯器會發出 ``brk`` 指令,並使用專用的 ``brk`` 處理程序 289 來打印錯誤報告。 289 來打印錯誤報告。 290 290 291 基於軟件標籤的KASAN使用0xFF作爲匹 291 基於軟件標籤的KASAN使用0xFF作爲匹配所有指針標籤(不檢查通過帶有0xFF指針標籤 292 的指針進行的訪問)。值0xFE當前保 292 的指針進行的訪問)。值0xFE當前保留用於標記已釋放的內存區域。 293 293 294 294 295 基於硬件標籤的KASAN模式 295 基於硬件標籤的KASAN模式 296 ~~~~~~~~~~~~~~~~~~~~~~~ 296 ~~~~~~~~~~~~~~~~~~~~~~~ 297 297 298 基於硬件標籤的KASAN在概念上類似 298 基於硬件標籤的KASAN在概念上類似於軟件模式,但它是使用硬件內存標籤作爲支持而 299 不是編譯器插樁和影子內存。 299 不是編譯器插樁和影子內存。 300 300 301 基於硬件標籤的KASAN目前僅針對arm64 301 基於硬件標籤的KASAN目前僅針對arm64架構實現,並且基於ARMv8.5指令集架構中引入 302 的arm64內存標記擴展(MTE)和最高字節 302 的arm64內存標記擴展(MTE)和最高字節忽略(TBI)。 303 303 304 特殊的arm64指令用於爲每次內存分 304 特殊的arm64指令用於爲每次內存分配指定內存標籤。相同的標籤被指定給指向這些分配 305 的指針。在每次內存訪問時,硬件 305 的指針。在每次內存訪問時,硬件確保正在訪問的內存的標籤等於用於訪問該內存的指針 306 的標籤。如果標籤不匹配,則會生 306 的標籤。如果標籤不匹配,則會生成故障並打印報告。 307 307 308 基於硬件標籤的KASAN使用0xFF作爲匹 308 基於硬件標籤的KASAN使用0xFF作爲匹配所有指針標籤(不檢查通過帶有0xFF指針標籤的 309 指針進行的訪問)。值0xFE當前保留 309 指針進行的訪問)。值0xFE當前保留用於標記已釋放的內存區域。 310 310 311 如果硬件不支持MTE(ARMv8.5之前), 311 如果硬件不支持MTE(ARMv8.5之前),則不會啓用基於硬件標籤的KASAN。在這種情況下, 312 所有KASAN引導參數都將被忽略。 312 所有KASAN引導參數都將被忽略。 313 313 314 請注意,啓用CONFIG_KASAN_HW_TAGS始終 314 請注意,啓用CONFIG_KASAN_HW_TAGS始終會導致啓用內核中的TBI。即使提供了 315 ``kasan.mode=off`` 或硬件不支持MTE(但 315 ``kasan.mode=off`` 或硬件不支持MTE(但支持TBI)。 316 316 317 基於硬件標籤的KASAN只報告第一個 317 基於硬件標籤的KASAN只報告第一個發現的錯誤。之後,MTE標籤檢查將被禁用。 318 318 319 影子內存 319 影子內存 320 -------- 320 -------- 321 321 322 本節的內容只適用於軟件KASAN模式 322 本節的內容只適用於軟件KASAN模式。 323 323 324 內核將內存映射到地址空間的幾個 324 內核將內存映射到地址空間的幾個不同部分。內核虛擬地址的範圍很大:沒有足夠的真實 325 內存來支持內核可以訪問的每個地 325 內存來支持內核可以訪問的每個地址的真實影子區域。因此,KASAN只爲地址空間的某些 326 部分映射真實的影子。 326 部分映射真實的影子。 327 327 328 默認行爲 328 默認行爲 329 ~~~~~~~~ 329 ~~~~~~~~ 330 330 331 默認情況下,體系結構僅將實際內 331 默認情況下,體系結構僅將實際內存映射到用於線性映射的陰影區域(以及可能的其他 332 小區域)。對於所有其他區域 —— 332 小區域)。對於所有其他區域 —— 例如vmalloc和vmemmap空間 —— 一個只讀頁面被映射 333 到陰影區域上。這個只讀的影子頁 333 到陰影區域上。這個只讀的影子頁面聲明所有內存訪問都是允許的。 334 334 335 這給模塊帶來了一個問題:它們不 335 這給模塊帶來了一個問題:它們不存在於線性映射中,而是存在於專用的模塊空間中。 336 通過連接模塊分配器,KASAN臨時映 336 通過連接模塊分配器,KASAN臨時映射真實的影子內存以覆蓋它們。例如,這允許檢測 337 對模塊全局變量的無效訪問。 337 對模塊全局變量的無效訪問。 338 338 339 這也造成了與 ``VMAP_STACK`` 的不兼容 339 這也造成了與 ``VMAP_STACK`` 的不兼容:如果堆棧位於vmalloc空間中,它將被分配 340 只讀頁面的影子內存,並且內核在 340 只讀頁面的影子內存,並且內核在嘗試爲堆棧變量設置影子數據時會出錯。 341 341 342 CONFIG_KASAN_VMALLOC 342 CONFIG_KASAN_VMALLOC 343 ~~~~~~~~~~~~~~~~~~~~ 343 ~~~~~~~~~~~~~~~~~~~~ 344 344 345 使用 ``CONFIG_KASAN_VMALLOC`` ,KASAN可以 345 使用 ``CONFIG_KASAN_VMALLOC`` ,KASAN可以以更大的內存使用爲代價覆蓋vmalloc 346 空間。目前,這在arm64、x86、riscv、 346 空間。目前,這在arm64、x86、riscv、s390和powerpc上受支持。 347 347 348 這通過連接到vmalloc和vmap並動態分 348 這通過連接到vmalloc和vmap並動態分配真實的影子內存來支持映射。 349 349 350 vmalloc空間中的大多數映射都很小, 350 vmalloc空間中的大多數映射都很小,需要不到一整頁的陰影空間。因此,爲每個映射 351 分配一個完整的影子頁面將是一種 351 分配一個完整的影子頁面將是一種浪費。此外,爲了確保不同的映射使用不同的影子 352 頁面,映射必須與 ``KASAN_GRANULE_SIZE 352 頁面,映射必須與 ``KASAN_GRANULE_SIZE * PAGE_SIZE`` 對齊。 353 353 354 相反,KASAN跨多個映射共享後備空 354 相反,KASAN跨多個映射共享後備空間。當vmalloc空間中的映射使用影子區域的特定 355 頁面時,它會分配一個後備頁面。 355 頁面時,它會分配一個後備頁面。此頁面稍後可以由其他vmalloc映射共享。 356 356 357 KASAN連接到vmap基礎架構以懶清理未 357 KASAN連接到vmap基礎架構以懶清理未使用的影子內存。 358 358 359 爲了避免交換映射的困難,KASAN預 359 爲了避免交換映射的困難,KASAN預測覆蓋vmalloc空間的陰影區域部分將不會被早期 360 的陰影頁面覆蓋,但是將不會被映 360 的陰影頁面覆蓋,但是將不會被映射。這將需要更改特定於arch的代碼。 361 361 362 這允許在x86上支持 ``VMAP_STACK`` ,並 362 這允許在x86上支持 ``VMAP_STACK`` ,並且可以簡化對沒有固定模塊區域的架構的支持。 363 363 364 對於開發者 364 對於開發者 365 ---------- 365 ---------- 366 366 367 忽略訪問 367 忽略訪問 368 ~~~~~~~~ 368 ~~~~~~~~ 369 369 370 軟件KASAN模式使用編譯器插樁來插 370 軟件KASAN模式使用編譯器插樁來插入有效性檢查。此類檢測可能與內核的某些部分 371 不兼容,因此需要禁用。 371 不兼容,因此需要禁用。 372 372 373 內核的其他部分可能會訪問已分配 373 內核的其他部分可能會訪問已分配對象的元數據。通常,KASAN會檢測並報告此類訪問, 374 但在某些情況下(例如,在內存分 374 但在某些情況下(例如,在內存分配器中),這些訪問是有效的。 375 375 376 對於軟件KASAN模式,要禁用特定文 376 對於軟件KASAN模式,要禁用特定文件或目錄的檢測,請將 ``KASAN_SANITIZE`` 添加 377 到相應的內核Makefile中: 377 到相應的內核Makefile中: 378 378 379 - 對於單個文件(例如,main.o):: 379 - 對於單個文件(例如,main.o):: 380 380 381 KASAN_SANITIZE_main.o := n 381 KASAN_SANITIZE_main.o := n 382 382 383 - 對於一個目錄下的所有文件:: 383 - 對於一個目錄下的所有文件:: 384 384 385 KASAN_SANITIZE := n 385 KASAN_SANITIZE := n 386 386 387 對於軟件KASAN模式,要在每個函數 387 對於軟件KASAN模式,要在每個函數的基礎上禁用檢測,請使用KASAN特定的 388 ``__no_sanitize_address`` 函數屬性或通 388 ``__no_sanitize_address`` 函數屬性或通用的 ``noinstr`` 。 389 389 390 請注意,禁用編譯器插樁(基於每 390 請注意,禁用編譯器插樁(基於每個文件或每個函數)會使KASAN忽略在軟件KASAN模式 391 的代碼中直接發生的訪問。當訪問 391 的代碼中直接發生的訪問。當訪問是間接發生的(通過調用檢測函數)或使用沒有編譯器 392 插樁的基於硬件標籤的模式時,它 392 插樁的基於硬件標籤的模式時,它沒有幫助。 393 393 394 對於軟件KASAN模式,要在當前任務 394 對於軟件KASAN模式,要在當前任務的一部分內核代碼中禁用KASAN報告,請使用 395 ``kasan_disable_current()``/``kasan_enable_cur 395 ``kasan_disable_current()``/``kasan_enable_current()`` 部分註釋這部分代碼。 396 這也會禁用通過函數調用發生的間 396 這也會禁用通過函數調用發生的間接訪問的報告。 397 397 398 對於基於標籤的KASAN模式,要禁用 398 對於基於標籤的KASAN模式,要禁用訪問檢查,請使用 ``kasan_reset_tag()`` 或 399 ``page_kasan_tag_reset()`` 。請注意,通 399 ``page_kasan_tag_reset()`` 。請注意,通過 ``page_kasan_tag_reset()`` 400 臨時禁用訪問檢查需要通過 ``page_ka 400 臨時禁用訪問檢查需要通過 ``page_kasan_tag`` / ``page_kasan_tag_set`` 保 401 存和恢復每頁KASAN標籤。 401 存和恢復每頁KASAN標籤。 402 402 403 測試 403 測試 404 ~~~~ 404 ~~~~ 405 405 406 有一些KASAN測試可以驗證KASAN是否正 406 有一些KASAN測試可以驗證KASAN是否正常工作並可以檢測某些類型的內存損壞。 407 測試由兩部分組成: 407 測試由兩部分組成: 408 408 409 1. 與KUnit測試框架集成的測試。使 409 1. 與KUnit測試框架集成的測試。使用 ``CONFIG_KASAN_KUNIT_TEST`` 啓用。 410 這些測試可以通過幾種不同的方式 410 這些測試可以通過幾種不同的方式自動運行和部分驗證;請參閱下面的說明。 411 411 412 2. 與KUnit不兼容的測試。使用 ``CONFI 412 2. 與KUnit不兼容的測試。使用 ``CONFIG_KASAN_MODULE_TEST`` 啓用並且只能作爲模塊 413 運行。這些測試只能通過加載內核 413 運行。這些測試只能通過加載內核模塊並檢查內核日誌以獲取KASAN報告來手動驗證。 414 414 415 如果檢測到錯誤,每個KUnit兼容的KA 415 如果檢測到錯誤,每個KUnit兼容的KASAN測試都會打印多個KASAN報告之一,然後測試打印 416 其編號和狀態。 416 其編號和狀態。 417 417 418 當測試通過:: 418 當測試通過:: 419 419 420 ok 28 - kmalloc_double_kzfree 420 ok 28 - kmalloc_double_kzfree 421 421 422 當由於 ``kmalloc`` 失敗而導致測試失 422 當由於 ``kmalloc`` 失敗而導致測試失敗時:: 423 423 424 # kmalloc_large_oob_right: ASSERTION F !! 424 # kmalloc_large_oob_right: ASSERTION FAILED at lib/test_kasan.c:163 425 Expected ptr is not null, but is 425 Expected ptr is not null, but is 426 not ok 5 - kmalloc_large_oob_right !! 426 not ok 4 - kmalloc_large_oob_right 427 427 428 當由於缺少KASAN報告而導致測試失 428 當由於缺少KASAN報告而導致測試失敗時:: 429 429 430 # kmalloc_double_kzfree: EXPECTATION F !! 430 # kmalloc_double_kzfree: EXPECTATION FAILED at lib/test_kasan.c:974 431 KASAN failure expected in "kfree_sensi 431 KASAN failure expected in "kfree_sensitive(ptr)", but none occurred 432 not ok 28 - kmalloc_double_kzfree !! 432 not ok 44 - kmalloc_double_kzfree 433 433 434 434 435 最後打印所有KASAN測試的累積狀態 435 最後打印所有KASAN測試的累積狀態。成功:: 436 436 437 ok 1 - kasan 437 ok 1 - kasan 438 438 439 或者,如果其中一項測試失敗:: 439 或者,如果其中一項測試失敗:: 440 440 441 not ok 1 - kasan 441 not ok 1 - kasan 442 442 443 有幾種方法可以運行與KUnit兼容的KA 443 有幾種方法可以運行與KUnit兼容的KASAN測試。 444 444 445 1. 可加載模塊 445 1. 可加載模塊 446 446 447 啓用 ``CONFIG_KUNIT`` 後,KASAN-KUnit 447 啓用 ``CONFIG_KUNIT`` 後,KASAN-KUnit測試可以構建爲可加載模塊,並通過使用 448 ``insmod`` 或 ``modprobe`` 加載 ``kasan_ !! 448 ``insmod`` 或 ``modprobe`` 加載 ``test_kasan.ko`` 來運行。 449 449 450 2. 內置 450 2. 內置 451 451 452 通過內置 ``CONFIG_KUNIT`` ,也可以 452 通過內置 ``CONFIG_KUNIT`` ,也可以內置KASAN-KUnit測試。在這種情況下, 453 測試將在啓動時作爲後期初始化 453 測試將在啓動時作爲後期初始化調用運行。 454 454 455 3. 使用kunit_tool 455 3. 使用kunit_tool 456 456 457 通過內置 ``CONFIG_KUNIT`` 和 ``CONFIG_ 457 通過內置 ``CONFIG_KUNIT`` 和 ``CONFIG_KASAN_KUNIT_TEST`` ,還可以使用 458 ``kunit_tool`` 以更易讀的方式查看K 458 ``kunit_tool`` 以更易讀的方式查看KUnit測試結果。這不會打印通過測試 459 的KASAN報告。有關 ``kunit_tool`` 更 459 的KASAN報告。有關 ``kunit_tool`` 更多最新信息,請參閱 460 `KUnit文檔 <https://www.kernel.org/doc/ht 460 `KUnit文檔 <https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html>`_ 。 461 461 462 .. _KUnit: https://www.kernel.org/doc/html/lat 462 .. _KUnit: https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html 463 463
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.