sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/dev-tools/kmsanmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/zh_TW/dev-tools/kmsanmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/it_IT/dev-tools/kmsanmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/ja_JP/dev-tools/kmsanmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/ko_KR/dev-tools/kmsanmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/sp_SP/dev-tools/kmsanmodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhcomment)}(h SPDX-License-Identifier: GPL-2.0h]h SPDX-License-Identifier: GPL-2.0}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhhP/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/dev-tools/kmsan.rsthKubhnote)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]h paragraph)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hh5Documentation/translations/zh_CN/disclaimer-zh_CN.rsthKhhubah}(h]h ]h"]h$]h&]uh1hhhhhhhhNubh field_list)}(hhh](hfield)}(hhh](h field_name)}(hOriginalh]hOriginal}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhKubh field_body)}(h!Documentation/dev-tools/kmsan.rsth]h)}(hhh]h!Documentation/dev-tools/kmsan.rst}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h Translatorh]h Translator}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj hhhKubh)}(h2刘浩阳 Haoyang Liu h]h)}(h1刘浩阳 Haoyang Liu h](h刘浩阳 Haoyang Liu <}(hjhhhNhNubh reference)}(htttturtleruss@hust.edu.cnh]htttturtleruss@hust.edu.cn}(hj)hhhNhNubah}(h]h ]h"]h$]h&]refuri mailto:tttturtleruss@hust.edu.cnuh1j'hjubh>}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhsection)}(hhh](htitle)}(h 内核内存消毒剂(KMSAN)h]h 内核内存消毒剂(KMSAN)}(hj\hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjWhhhhhK ubh)}(hKMSAN 是一个动态错误检测器,旨在查找未初始化值的使用。它基于编译器插桩,类似于用 户空间的 `MemorySanitizer tool`_。h](hKMSAN 是一个动态错误检测器,旨在查找未初始化值的使用。它基于编译器插桩,类似于用 户空间的 }(hjjhhhNhNubj()}(h`MemorySanitizer tool`_h]hMemorySanitizer tool}(hjrhhhNhNubah}(h]h ]h"]h$]h&]nameMemorySanitizer toolrefuri0https://clang.llvm.org/docs/MemorySanitizer.htmluh1j'hjjresolvedKubh。}(hjjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK hjWhhubh)}(h|需要注意的是 KMSAN 并不适合生产环境,因为它会大幅增加内核内存占用并降低系统运行速度。h]h|需要注意的是 KMSAN 并不适合生产环境,因为它会大幅增加内核内存占用并降低系统运行速度。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjWhhubjV)}(hhh](j[)}(h 使用方法h]h 使用方法}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjhhhhhKubjV)}(hhh](j[)}(h 构建内核h]h 构建内核}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjhhhhhKubh)}(h要构建带有 KMSAN 的内核,你需要一个较新的 Clang (14.0.6+)。 请参阅 `LLVM documentation`_ 了解如何构建 Clang。h](hX要构建带有 KMSAN 的内核,你需要一个较新的 Clang (14.0.6+)。 请参阅 }(hjhhhNhNubj()}(h`LLVM documentation`_h]hLLVM documentation}(hjhhhNhNubah}(h]h ]h"]h$]h&]nameLLVM documentationj)https://llvm.org/docs/GettingStarted.htmluh1j'hjjKubh 了解如何构建 Clang。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h;现在配置并构建一个启用 CONFIG_KMSAN 的内核。h]h;现在配置并构建一个启用 CONFIG_KMSAN 的内核。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id2ah ]h"] 构建内核ah$]h&]uh1jUhjhhhhhKubjV)}(hhh](j[)}(h 示例报告h]h 示例报告}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjhhhhhKubh)}(h'以下是一个 KMSAN 报告的示例::h]h&以下是一个 KMSAN 报告的示例:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh literal_block)}(hX+===================================================== BUG: KMSAN: uninit-value in test_uninit_kmsan_check_memory+0x1be/0x380 [kmsan_test] test_uninit_kmsan_check_memory+0x1be/0x380 mm/kmsan/kmsan_test.c:273 kunit_run_case_internal lib/kunit/test.c:333 kunit_try_run_case+0x206/0x420 lib/kunit/test.c:374 kunit_generic_run_threadfn_adapter+0x6d/0xc0 lib/kunit/try-catch.c:28 kthread+0x721/0x850 kernel/kthread.c:327 ret_from_fork+0x1f/0x30 ??:? Uninit was stored to memory at: do_uninit_local_array+0xfa/0x110 mm/kmsan/kmsan_test.c:260 test_uninit_kmsan_check_memory+0x1a2/0x380 mm/kmsan/kmsan_test.c:271 kunit_run_case_internal lib/kunit/test.c:333 kunit_try_run_case+0x206/0x420 lib/kunit/test.c:374 kunit_generic_run_threadfn_adapter+0x6d/0xc0 lib/kunit/try-catch.c:28 kthread+0x721/0x850 kernel/kthread.c:327 ret_from_fork+0x1f/0x30 ??:? Local variable uninit created at: do_uninit_local_array+0x4a/0x110 mm/kmsan/kmsan_test.c:256 test_uninit_kmsan_check_memory+0x1a2/0x380 mm/kmsan/kmsan_test.c:271 Bytes 4-7 of 8 are uninitialized Memory access of size 8 starts at ffff888083fe3da0 CPU: 0 PID: 6731 Comm: kunit_try_catch Tainted: G B E 5.16.0-rc3+ #104 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014 =====================================================h]hX+===================================================== BUG: KMSAN: uninit-value in test_uninit_kmsan_check_memory+0x1be/0x380 [kmsan_test] test_uninit_kmsan_check_memory+0x1be/0x380 mm/kmsan/kmsan_test.c:273 kunit_run_case_internal lib/kunit/test.c:333 kunit_try_run_case+0x206/0x420 lib/kunit/test.c:374 kunit_generic_run_threadfn_adapter+0x6d/0xc0 lib/kunit/try-catch.c:28 kthread+0x721/0x850 kernel/kthread.c:327 ret_from_fork+0x1f/0x30 ??:? Uninit was stored to memory at: do_uninit_local_array+0xfa/0x110 mm/kmsan/kmsan_test.c:260 test_uninit_kmsan_check_memory+0x1a2/0x380 mm/kmsan/kmsan_test.c:271 kunit_run_case_internal lib/kunit/test.c:333 kunit_try_run_case+0x206/0x420 lib/kunit/test.c:374 kunit_generic_run_threadfn_adapter+0x6d/0xc0 lib/kunit/try-catch.c:28 kthread+0x721/0x850 kernel/kthread.c:327 ret_from_fork+0x1f/0x30 ??:? Local variable uninit created at: do_uninit_local_array+0x4a/0x110 mm/kmsan/kmsan_test.c:256 test_uninit_kmsan_check_memory+0x1a2/0x380 mm/kmsan/kmsan_test.c:271 Bytes 4-7 of 8 are uninitialized Memory access of size 8 starts at ffff888083fe3da0 CPU: 0 PID: 6731 Comm: kunit_try_catch Tainted: G B E 5.16.0-rc3+ #104 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014 =====================================================}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhK!hjhhubh)}(h报告指出本地变量 ``uninit`` 在 ``do_uninit_local_array()`` 中未初始化。 第三个堆栈跟踪对应于该变量创建的位置。h](h报告指出本地变量 }(hj'hhhNhNubhliteral)}(h ``uninit``h]huninit}(hj1hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj'ubh 在 }(hj'hhhNhNubj0)}(h``do_uninit_local_array()``h]hdo_uninit_local_array()}(hjChhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj'ubhM 中未初始化。 第三个堆栈跟踪对应于该变量创建的位置。}(hj'hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK>hjhhubh)}(h第一个堆栈跟踪显示了未初始化值的使用位置(在 ``test_uninit_kmsan_check_memory()``)。 工具显示了局部变量中未初始化的字节及其被复制到其他内存位置前的堆栈。h](hC第一个堆栈跟踪显示了未初始化值的使用位置(在 }(hj[hhhNhNubj0)}(h$``test_uninit_kmsan_check_memory()``h]h test_uninit_kmsan_check_memory()}(hjchhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj[ubhm)。 工具显示了局部变量中未初始化的字节及其被复制到其他内存位置前的堆栈。}(hj[hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKAhjhhubh)}(h:KMSAN 会在以下情况下报告未初始化的值 ``v``:h](h4KMSAN 会在以下情况下报告未初始化的值 }(hj{hhhNhNubj0)}(h``v``h]hv}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj{ubh:}(hj{hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKEhjhhubh block_quote)}(hX;- 在条件判断中,例如 ``if (v) { ... }``; - 在索引或指针解引用中,例如 ``array[v]`` 或 ``*v``; - 当它被复制到用户空间或硬件时,例如 ``copy_to_user(..., &v, ...)``; - 当它作为函数参数传递,并且启用 ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 时(见下文)。 h]h bullet_list)}(hhh](h list_item)}(h1在条件判断中,例如 ``if (v) { ... }``;h]h)}(hjh](h在条件判断中,例如 }(hjhhhNhNubj0)}(h``if (v) { ... }``h]hif (v) { ... }}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh;}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKGhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hB在索引或指针解引用中,例如 ``array[v]`` 或 ``*v``;h]h)}(hjh](h(在索引或指针解引用中,例如 }(hjhhhNhNubj0)}(h ``array[v]``h]harray[v]}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 或 }(hjhhhNhNubj0)}(h``*v``h]h*v}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh;}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKHhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hU当它被复制到用户空间或硬件时,例如 ``copy_to_user(..., &v, ...)``;h]h)}(hjh](h4当它被复制到用户空间或硬件时,例如 }(hjhhhNhNubj0)}(h``copy_to_user(..., &v, ...)``h]hcopy_to_user(..., &v, ...)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh;}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKIhj ubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hh当它作为函数参数传递,并且启用 ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 时(见下文)。 h]h)}(hg当它作为函数参数传递,并且启用 ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 时(见下文)。h](h.当它作为函数参数传递,并且启用 }(hj9hhhNhNubj0)}(h#``CONFIG_KMSAN_CHECK_PARAM_RETVAL``h]hCONFIG_KMSAN_CHECK_PARAM_RETVAL}(hjAhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj9ubh 时(见下文)。}(hj9hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKJhj5ubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]bullet-uh1jhhhKGhjubah}(h]h ]h"]h$]h&]uh1jhhhKGhjhhubh)}(h这些情况(除了复制数据到用户空间或硬件外,这是一个安全问题)被视为 C11 标准下的未定义行为。h]h这些情况(除了复制数据到用户空间或硬件外,这是一个安全问题)被视为 C11 标准下的未定义行为。}(hjmhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKLhjhhubeh}(h]id3ah ]h"] 示例报告ah$]h&]uh1jUhjhhhhhKubjV)}(hhh](j[)}(h 禁用插桩h]h 禁用插桩}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjhhhhhKOubh)}(h可以用 ``__no_kmsan_checks`` 标记函数。这样,KMSAN 会忽略该函数中的未初始化值, 并将其输出标记为已初始化。如此,用户不会收到与该函数相关的 KMSAN 报告。h](h 可以用 }(hjhhhNhNubj0)}(h``__no_kmsan_checks``h]h__no_kmsan_checks}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 标记函数。这样,KMSAN 会忽略该函数中的未初始化值, 并将其输出标记为已初始化。如此,用户不会收到与该函数相关的 KMSAN 报告。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKQhjhhubh)}(hXKMSAN 还支持 ``__no_sanitize_memory`` 函数属性。KMSAN 不会对拥有该属性的函数进行 插桩,这在我们不希望编译器干扰某些底层代码(例如标记为 ``noinstr`` 的代码,该 代码隐式添加了 ``__no_sanitize_memory``)时可能很有用。h](hKMSAN 还支持 }(hjhhhNhNubj0)}(h``__no_sanitize_memory``h]h__no_sanitize_memory}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 函数属性。KMSAN 不会对拥有该属性的函数进行 插桩,这在我们不希望编译器干扰某些底层代码(例如标记为 }(hjhhhNhNubj0)}(h ``noinstr``h]hnoinstr}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh' 的代码,该 代码隐式添加了 }(hjhhhNhNubj0)}(h``__no_sanitize_memory``h]h__no_sanitize_memory}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh)时可能很有用。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKThjhhubh)}(h然而,这会有代价:此类函数的栈分配将具有不正确的影子/初始值,可能导致误报。来 自非插桩代码的函数也可能接收到不正确的元数据。h]h然而,这会有代价:此类函数的栈分配将具有不正确的影子/初始值,可能导致误报。来 自非插桩代码的函数也可能接收到不正确的元数据。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKXhjhhubh)}(hC作为经验之谈,避免显式使用 ``__no_sanitize_memory``。h](h(作为经验之谈,避免显式使用 }(hjhhhNhNubj0)}(h``__no_sanitize_memory``h]h__no_sanitize_memory}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK\hjhhubh)}(hS也可以通过 Makefile 禁用 KMSAN 对某个文件(例如 main.o)的作用::h]hR也可以通过 Makefile 禁用 KMSAN 对某个文件(例如 main.o)的作用:}(hj&hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK^hjhhubj)}(hKMSAN_SANITIZE_main.o := nh]hKMSAN_SANITIZE_main.o := n}hj4sbah}(h]h ]h"]h$]h&]hhuh1jhhhK`hjhhubh)}(h或者对整个目录::h]h或者对整个目录:}(hjBhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKbhjhhubj)}(hKMSAN_SANITIZE := nh]hKMSAN_SANITIZE := n}hjPsbah}(h]h ]h"]h$]h&]hhuh1jhhhKdhjhhubh)}(h将其应用到文件或目录中的每个函数。大多数用户不会需要 KMSAN_SANITIZE, 除非他们的代码被 KMSAN 破坏(例如在早期启动时运行的代码)。h]h将其应用到文件或目录中的每个函数。大多数用户不会需要 KMSAN_SANITIZE, 除非他们的代码被 KMSAN 破坏(例如在早期启动时运行的代码)。}(hj^hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKfhjhhubh)}(hXU还可以通过调用 ``kmsan_disable_current()`` 和 ``kmsan_enable_current()`` 暂时对当前任务禁用 KMSAN 检查。每个 ``kmsan_enable_current()`` 必须在 ``kmsan_disable_current()`` 之后调用;这些调用对可以嵌套。在调用时需要注意保持 嵌套区域简短,并且尽可能使用其他方法禁用插桩。h](h还可以通过调用 }(hjlhhhNhNubj0)}(h``kmsan_disable_current()``h]hkmsan_disable_current()}(hjthhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjlubh 和 }(hjlhhhNhNubj0)}(h``kmsan_enable_current()``h]hkmsan_enable_current()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjlubh3 暂时对当前任务禁用 KMSAN 检查。每个 }(hjlhhhNhNubj0)}(h``kmsan_enable_current()``h]hkmsan_enable_current()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjlubh 必须在 }(hjlhhhNhNubj0)}(h``kmsan_disable_current()``h]hkmsan_disable_current()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjlubh 之后调用;这些调用对可以嵌套。在调用时需要注意保持 嵌套区域简短,并且尽可能使用其他方法禁用插桩。}(hjlhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKihjhhubeh}(h]id4ah ]h"] 禁用插桩ah$]h&]uh1jUhjhhhhhKOubeh}(h]id1ah ]h"] 使用方法ah$]h&]uh1jUhjWhhhhhKubjV)}(hhh](j[)}(h支持h]h支持}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjhhhhhKoubh)}(h为了使用 KMSAN,内核必须使用 Clang 构建,到目前为止,Clang 是唯一支持 KMSAN 的编译器。内核插桩过程基于用户空间的 `MemorySanitizer tool`_。h](h为了使用 KMSAN,内核必须使用 Clang 构建,到目前为止,Clang 是唯一支持 KMSAN 的编译器。内核插桩过程基于用户空间的 }(hjhhhNhNubj()}(h`MemorySanitizer tool`_h]hMemorySanitizer tool}(hjhhhNhNubah}(h]h ]h"]h$]h&]nameMemorySanitizer tooljjuh1j'hjjKubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKqhjhhubh)}(h,目前运行时库仅支持 x86_64 架构。h]h,目前运行时库仅支持 x86_64 架构。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKthjhhubeh}(h]id5ah ]h"]支持ah$]h&]uh1jUhjWhhhhhKoubjV)}(hhh](j[)}(hKMSAN 的工作原理h]hKMSAN 的工作原理}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjhhhhhKwubjV)}(hhh](j[)}(hKMSAN 阴影内存h]hKMSAN 阴影内存}(hj/hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhj,hhhhhKzubh)}(hXsKMSAN 将一个元数据字节(也称为阴影字节)与每个内核内存字节关联。仅当内核内存字节 的相应位未初始化时,阴影字节中的一个比特位才会被设置。将内存标记为未初始化(即 将其阴影字节设置为 ``0xff``)称为中毒,将其标记为已初始化(将阴影字节设置为 ``0x00``)称为解毒。h](hXKMSAN 将一个元数据字节(也称为阴影字节)与每个内核内存字节关联。仅当内核内存字节 的相应位未初始化时,阴影字节中的一个比特位才会被设置。将内存标记为未初始化(即 将其阴影字节设置为 }(hj=hhhNhNubj0)}(h``0xff``h]h0xff}(hjEhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj=ubhI)称为中毒,将其标记为已初始化(将阴影字节设置为 }(hj=hhhNhNubj0)}(h``0x00``h]h0x00}(hjWhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj=ubh)称为解毒。}(hj=hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK|hj,hhubh)}(h当在栈上分配新变量时,默认情况下它会中毒,这由编译器插入的插桩代码完成(除非它 是立即初始化的栈变量)。任何未使用 ``__GFP_ZERO`` 的堆分配也会中毒。h](h当在栈上分配新变量时,默认情况下它会中毒,这由编译器插入的插桩代码完成(除非它 是立即初始化的栈变量)。任何未使用 }(hjohhhNhNubj0)}(h``__GFP_ZERO``h]h __GFP_ZERO}(hjwhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjoubh 的堆分配也会中毒。}(hjohhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj,hhubh)}(h编译器插桩还跟踪阴影值在代码中的使用。当需要时,插桩代码会调用 ``mm/kmsan/`` 中 的运行时库以持久化阴影值。h](h^编译器插桩还跟踪阴影值在代码中的使用。当需要时,插桩代码会调用 }(hjhhhNhNubj0)}(h ``mm/kmsan/``h]h mm/kmsan/}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh, 中 的运行时库以持久化阴影值。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj,hhubh)}(hXa基本或复合类型的阴影值是长度相同的字节数组。当常量值写入内存时,该内存会被解毒 。当从内存读取值时,其阴影内存也会被获取,并传递到所有使用该值的操作中。对于每 个需要一个或多个值的指令,编译器会生成代码根据这些值及其阴影来计算结果的阴影。h]hXa基本或复合类型的阴影值是长度相同的字节数组。当常量值写入内存时,该内存会被解毒 。当从内存读取值时,其阴影内存也会被获取,并传递到所有使用该值的操作中。对于每 个需要一个或多个值的指令,编译器会生成代码根据这些值及其阴影来计算结果的阴影。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj,hhubh)}(h示例::h]h示例:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj,hhubj)}(h7int a = 0xff; // i.e. 0x000000ff int b; int c = a | b;h]h7int a = 0xff; // i.e. 0x000000ff int b; int c = a | b;}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhj,hhubh)}(h在这种情况下, ``a`` 的阴影为 ``0``, ``b`` 的阴影为 ``0xffffffff``, ``c`` 的阴影为 ``0xffffff00``。这意味着 ``c`` 的高三个字节未初始化,而低字节已 初始化。h](h在这种情况下, }(hjhhhNhNubj0)}(h``a``h]ha}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 的阴影为 }(hjhhhNhNubj0)}(h``0``h]h0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh, }(hjhhhNhNubj0)}(h``b``h]hb}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 的阴影为 }hjsbj0)}(h``0xffffffff``h]h 0xffffffff}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh, }(hjhhhNhNubj0)}(h``c``h]hc}(hj)hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 的阴影为 }hjsbj0)}(h``0xffffff00``h]h 0xffffff00}(hj;hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh。这意味着 }(hjhhhNhNubj0)}(h``c``h]hc}(hjMhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh> 的高三个字节未初始化,而低字节已 初始化。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj,hhubeh}(h]id7ah ]h"]kmsan 阴影内存ah$]h&]uh1jUhjhhhhhKzubjV)}(hhh](j[)}(h 起源跟踪h]h 起源跟踪}(hjphhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjmhhhhhKubh)}(hX每四字节的内核内存都有一个所谓的源点与之映射。这个源点描述了在程序执行中,未初 始化值的创建点。每个源点都与完整的分配栈(对于堆分配的内存)或包含未初始化变 量的函数(对于局部变量)相关联。h]hX每四字节的内核内存都有一个所谓的源点与之映射。这个源点描述了在程序执行中,未初 始化值的创建点。每个源点都与完整的分配栈(对于堆分配的内存)或包含未初始化变 量的函数(对于局部变量)相关联。}(hj~hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjmhhubh)}(hX当一个未初始化的变量在栈或堆上分配时,会创建一个新的源点值,并将该变量的初始值 填充为这个值。当从内存中读取一个值时,其初始值也会被读取并与阴影一起保留。对于 每个接受一个或多个值的指令,结果的源点是与任何未初始化输入相对应的源点之一。如 果一个污染值被写入内存,其起源也会被写入相应的存储中。h]hX当一个未初始化的变量在栈或堆上分配时,会创建一个新的源点值,并将该变量的初始值 填充为这个值。当从内存中读取一个值时,其初始值也会被读取并与阴影一起保留。对于 每个接受一个或多个值的指令,结果的源点是与任何未初始化输入相对应的源点之一。如 果一个污染值被写入内存,其起源也会被写入相应的存储中。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjmhhubh)}(h 示例 1::h]h 示例 1:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjmhhubj)}(h!int a = 42; int b; int c = a + b;h]h!int a = 42; int b; int c = a + b;}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjmhhubh)}(h在这种情况下, ``b`` 的源点是在函数入口时生成的,并在加法结果写入内存之前存储到 ``c`` 的源点中。h](h在这种情况下, }(hjhhhNhNubj0)}(h``b``h]hb}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubhY 的源点是在函数入口时生成的,并在加法结果写入内存之前存储到 }(hjhhhNhNubj0)}(h``c``h]hc}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 的源点中。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjmhhubh)}(hX(如果几个变量共享相同的源点地址,则它们被存储在同一个四字节块中。在这种情况下, 对任何变量的每次写入都会更新所有变量的源点。在这种情况下我们必须牺牲精度,因 为为单独的位(甚至字节)存储源点成本过高。h]hX(如果几个变量共享相同的源点地址,则它们被存储在同一个四字节块中。在这种情况下, 对任何变量的每次写入都会更新所有变量的源点。在这种情况下我们必须牺牲精度,因 为为单独的位(甚至字节)存储源点成本过高。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjmhhubh)}(h 示例 2::h]h 示例 2:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjmhhubj)}(hint combine(short a, short b) { union ret_t { int i; short s[2]; } ret; ret.s[0] = a; ret.s[1] = b; return ret.i; }h]hint combine(short a, short b) { union ret_t { int i; short s[2]; } ret; ret.s[0] = a; ret.s[1] = b; return ret.i; }}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjmhhubh)}(h如果 ``a`` 已初始化而 ``b`` 未初始化,则结果的阴影为 0xffff0000,结果的源点为 ``b`` 的源点。 ``ret.s[0]`` 会有相同的起源,但它不会被使用,因为该变量已初始化。h](h如果 }(hjhhhNhNubj0)}(h``a``h]ha}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 已初始化而 }(hjhhhNhNubj0)}(h``b``h]hb}(hj,hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubhF 未初始化,则结果的阴影为 0xffff0000,结果的源点为 }(hjhhhNhNubj0)}(h``b``h]hb}(hj>hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 的源点。 }(hjhhhNhNubj0)}(h ``ret.s[0]``h]hret.s[0]}(hjPhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubhO 会有相同的起源,但它不会被使用,因为该变量已初始化。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjmhhubh)}(hQ如果两个函数参数都未初始化,则只保留第二个参数的源点。h]hQ如果两个函数参数都未初始化,则只保留第二个参数的源点。}(hjhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjmhhubjV)}(hhh](j[)}(h 源点链h]h 源点链}(hjyhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjvhhhhhKubh)}(hX为了便于调试,KMSAN 在每次将未初始化值存储到内存时都会创建一个新的源点。新的源点 引用了其创建栈以及值的前一个起源。这可能导致内存消耗增加,因此我们在运行时限制 了源点链的长度。h]hX为了便于调试,KMSAN 在每次将未初始化值存储到内存时都会创建一个新的源点。新的源点 引用了其创建栈以及值的前一个起源。这可能导致内存消耗增加,因此我们在运行时限制 了源点链的长度。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjvhhubeh}(h]id9ah ]h"] 源点链ah$]h&]uh1jUhjmhhhhhKubeh}(h]id8ah ]h"] 起源跟踪ah$]h&]uh1jUhjhhhhhKubjV)}(hhh](j[)}(hClang 插桩 APIh]hClang 插桩 API}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjhhhhhKubh)}(hrClang 插桩通过在内核代码中插入定义在 ``mm/kmsan/instrumentation.c`` 中的函数调用 来实现。h](h4Clang 插桩通过在内核代码中插入定义在 }(hjhhhNhNubj0)}(h``mm/kmsan/instrumentation.c``h]hmm/kmsan/instrumentation.c}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 中的函数调用 来实现。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubjV)}(hhh](j[)}(h 阴影操作h]h 阴影操作}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjhhhhhKubh)}(h对于每次内存访问,编译器都会发出一个函数调用,该函数返回一对指针,指向给定内存 的阴影和原始地址::h]h对于每次内存访问,编译器都会发出一个函数调用,该函数返回一对指针,指向给定内存 的阴影和原始地址:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hXntypedef struct { void *shadow, *origin; } shadow_origin_ptr_t shadow_origin_ptr_t __msan_metadata_ptr_for_load_{1,2,4,8}(void *addr) shadow_origin_ptr_t __msan_metadata_ptr_for_store_{1,2,4,8}(void *addr) shadow_origin_ptr_t __msan_metadata_ptr_for_load_n(void *addr, uintptr_t size) shadow_origin_ptr_t __msan_metadata_ptr_for_store_n(void *addr, uintptr_t size)h]hXntypedef struct { void *shadow, *origin; } shadow_origin_ptr_t shadow_origin_ptr_t __msan_metadata_ptr_for_load_{1,2,4,8}(void *addr) shadow_origin_ptr_t __msan_metadata_ptr_for_store_{1,2,4,8}(void *addr) shadow_origin_ptr_t __msan_metadata_ptr_for_load_n(void *addr, uintptr_t size) shadow_origin_ptr_t __msan_metadata_ptr_for_store_n(void *addr, uintptr_t size)}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjhhubh)}(h*函数名依赖于内存访问的大小。h]h*函数名依赖于内存访问的大小。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h编译器确保对于每个加载的值,其阴影和原始值都从内存中读取。当一个值存储到内存时 ,其阴影和原始值也会通过元数据指针进行存储。h]h编译器确保对于每个加载的值,其阴影和原始值都从内存中读取。当一个值存储到内存时 ,其阴影和原始值也会通过元数据指针进行存储。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id10ah ]h"] 阴影操作ah$]h&]uh1jUhjhhhhhKubjV)}(hhh](j[)}(h处理局部变量h]h处理局部变量}(hj*hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhj'hhhhhKubh)}(ht一个特殊的函数用于为局部变量创建一个新的原始值,并将该变量的原始值设置为该值::h]hs一个特殊的函数用于为局部变量创建一个新的原始值,并将该变量的原始值设置为该值:}(hj8hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj'hhubj)}(hBvoid __msan_poison_alloca(void *addr, uintptr_t size, char *descr)h]hBvoid __msan_poison_alloca(void *addr, uintptr_t size, char *descr)}hjFsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhj'hhubeh}(h]id11ah ]h"]处理局部变量ah$]h&]uh1jUhjhhhhhKubjV)}(hhh](j[)}(h访问每个任务数据h]h访问每个任务数据}(hj_hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhj\hhhhhKubh)}(he在每个插桩函数的开始处,KMSAN 插入一个对 ``__msan_get_context_state()`` 的调用 ::h](h:在每个插桩函数的开始处,KMSAN 插入一个对 }(hjmhhhNhNubj0)}(h``__msan_get_context_state()``h]h__msan_get_context_state()}(hjuhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjmubh 的调用}(hjmhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj\hhubj)}(h3kmsan_context_state *__msan_get_context_state(void)h]h3kmsan_context_state *__msan_get_context_state(void)}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhj\hhubh)}(hA``kmsan_context_state`` 在 ``include/linux/kmsan.h`` 中声明::h](j0)}(h``kmsan_context_state``h]hkmsan_context_state}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 在 }(hjhhhNhNubj0)}(h``include/linux/kmsan.h``h]hinclude/linux/kmsan.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 中声明:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj\hhubj)}(hX/struct kmsan_context_state { char param_tls[KMSAN_PARAM_SIZE]; char retval_tls[KMSAN_RETVAL_SIZE]; char va_arg_tls[KMSAN_PARAM_SIZE]; char va_arg_origin_tls[KMSAN_PARAM_SIZE]; u64 va_arg_overflow_size_tls; char param_origin_tls[KMSAN_PARAM_SIZE]; depot_stack_handle_t retval_origin_tls; };h]hX/struct kmsan_context_state { char param_tls[KMSAN_PARAM_SIZE]; char retval_tls[KMSAN_RETVAL_SIZE]; char va_arg_tls[KMSAN_PARAM_SIZE]; char va_arg_origin_tls[KMSAN_PARAM_SIZE]; u64 va_arg_overflow_size_tls; char param_origin_tls[KMSAN_PARAM_SIZE]; depot_stack_handle_t retval_origin_tls; };}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhj\hhubhdefinition_list)}(hhh]hdefinition_list_item)}(hKMSAN 使用此结构体在插桩函数之间传递参数阴影和原始值(除非立刻通过 ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 检查参数)。 h](hterm)}(h`KMSAN 使用此结构体在插桩函数之间传递参数阴影和原始值(除非立刻通过h]h`KMSAN 使用此结构体在插桩函数之间传递参数阴影和原始值(除非立刻通过}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubh definition)}(hhh]h)}(h6``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 检查参数)。h](j0)}(h#``CONFIG_KMSAN_CHECK_PARAM_RETVAL``h]hCONFIG_KMSAN_CHECK_PARAM_RETVAL}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjubh 检查参数)。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhj\hhhhhNubeh}(h]id12ah ]h"]访问每个任务数据ah$]h&]uh1jUhjhhhhhKubjV)}(hhh](j[)}(h$将未初始化的值传递给函数h]h$将未初始化的值传递给函数}(hj0 hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhj- hhhhhMubh)}(hClang 的 MemorySanitizer 插桩有一个选项 ``-fsanitize-memory-param-retval``,该 选项使编译器检查按值传递的函数参数,以及函数返回值。h](h0Clang 的 MemorySanitizer 插桩有一个选项 }(hj> hhhNhNubj0)}(h"``-fsanitize-memory-param-retval``h]h-fsanitize-memory-param-retval}(hjF hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj> ubhU,该 选项使编译器检查按值传递的函数参数,以及函数返回值。}(hj> hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj- hhubh)}(h该选项由 ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 控制,默认启用以便 KMSAN 更早报告 未初始化的值。有关更多细节,请参考 `LKML discussion`_。h](h 该选项由 }(hj^ hhhNhNubj0)}(h#``CONFIG_KMSAN_CHECK_PARAM_RETVAL``h]hCONFIG_KMSAN_CHECK_PARAM_RETVAL}(hjf hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj^ ubhd 控制,默认启用以便 KMSAN 更早报告 未初始化的值。有关更多细节,请参考 }(hj^ hhhNhNubj()}(h`LKML discussion`_h]hLKML discussion}(hjx hhhNhNubah}(h]h ]h"]h$]h&]nameLKML discussionjGhttps://lore.kernel.org/all/20220614144853.3693273-1-glider@google.com/uh1j'hj^ jKubh。}(hj^ hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj- hhubh)}(h由于 LLVM 中的实现检查的方式(它们仅应用于标记为 ``noundef`` 的参数),并不是所 有参数都能保证被检查,因此我们不能放弃 ``kmsan_context_state`` 中的元数据存储 。h](hF由于 LLVM 中的实现检查的方式(它们仅应用于标记为 }(hj hhhNhNubj0)}(h ``noundef``h]hnoundef}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj ubhW 的参数),并不是所 有参数都能保证被检查,因此我们不能放弃 }(hj hhhNhNubj0)}(h``kmsan_context_state``h]hkmsan_context_state}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj ubh 中的元数据存储 。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM hj- hhubeh}(h]id13ah ]h"]$将未初始化的值传递给函数ah$]h&]uh1jUhjhhhhhMubjV)}(hhh](j[)}(h字符串函数h]h字符串函数}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhj hhhhhMubh)}(h编译器将对 ``memcpy()``/``memmove()``/``memset()`` 的调用替换为以下函数。这些函 数在数据结构初始化或复制时也会被调用,确保阴影和原始值与数据一起复制::h](h编译器将对 }(hj hhhNhNubj0)}(h ``memcpy()``h]hmemcpy()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj ubh/}(hj hhhNhNubj0)}(h ``memmove()``h]h memmove()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj ubh/}hj sbj0)}(h ``memset()``h]hmemset()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj ubh 的调用替换为以下函数。这些函 数在数据结构初始化或复制时也会被调用,确保阴影和原始值与数据一起复制:}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj hhubj)}(hvoid *__msan_memcpy(void *dst, void *src, uintptr_t n) void *__msan_memmove(void *dst, void *src, uintptr_t n) void *__msan_memset(void *dst, int c, uintptr_t n)h]hvoid *__msan_memcpy(void *dst, void *src, uintptr_t n) void *__msan_memmove(void *dst, void *src, uintptr_t n) void *__msan_memset(void *dst, int c, uintptr_t n)}hj" sbah}(h]h ]h"]h$]h&]hhuh1jhhhMhj hhubeh}(h]id14ah ]h"]字符串函数ah$]h&]uh1jUhjhhhhhMubjV)}(hhh](j[)}(h 错误报告h]h 错误报告}(hj; hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhj8 hhhhhMubh)}(ht对于每个值的使用,编译器发出一个阴影检查,在值中毒的情况下调用 ``__msan_warning()``::h](h^对于每个值的使用,编译器发出一个阴影检查,在值中毒的情况下调用 }(hjI hhhNhNubj0)}(h``__msan_warning()``h]h__msan_warning()}(hjQ hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjI ubh:}(hjI hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj8 hhubj)}(hvoid __msan_warning(u32 origin)h]hvoid __msan_warning(u32 origin)}hji sbah}(h]h ]h"]h$]h&]hhuh1jhhhMhj8 hhubh)}(h=``__msan_warning()`` 使 KMSAN 运行时打印错误报告。h](j0)}(h``__msan_warning()``h]h__msan_warning()}(hj{ hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjw ubh) 使 KMSAN 运行时打印错误报告。}(hjw hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj8 hhubeh}(h]id15ah ]h"] 错误报告ah$]h&]uh1jUhjhhhhhMubjV)}(hhh](j[)}(h内联汇编插桩h]h内联汇编插桩}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhj hhhhhM"ubh)}(h8KMSAN 对每个内联汇编输出进行插桩,调用::h]h7KMSAN 对每个内联汇编输出进行插桩,调用:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM$hj hhubj)}(hkmsan.cstate`` 来 保存函数参数和返回值的元数据。h](h4在内核任务上下文中运行时,KMSAN 使用 }(hjy hhhNhNubj0)}(h``current->kmsan.cstate``h]hcurrent->kmsan.cstate}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hjy ubh2 来 保存函数参数和返回值的元数据。}(hjy hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMJhjh hhubh)}(h但在内核运行于中断、softirq 或 NMI 上下文中, ``current`` 不可用时, KMSAN 切换到每 CPU 中断状态::h](h>但在内核运行于中断、softirq 或 NMI 上下文中, }(hj hhhNhNubj0)}(h ``current``h]hcurrent}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj ubh5 不可用时, KMSAN 切换到每 CPU 中断状态:}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMMhjh hhubj)}(h3DEFINE_PER_CPU(struct kmsan_ctx, kmsan_percpu_ctx);h]h3DEFINE_PER_CPU(struct kmsan_ctx, kmsan_percpu_ctx);}hj sbah}(h]h ]h"]h$]h&]hhuh1jhhhMPhjh hhubeh}(h]id19ah ]h"]kmsan 上下文ah$]h&]uh1jUhj hhhhhMHubjV)}(hhh](j[)}(h元数据分配h]h元数据分配}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhj hhhhhMSubh)}(h*内核中有多个地方存储元数据。h]h*内核中有多个地方存储元数据。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMUhj hhubh)}(hV1. 每个 ``struct page`` 实例包含两个指向其影子和内存页面的指针 ::h](h 1. 每个 }(hj hhhNhNubj0)}(h``struct page``h]h struct page}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j/hj ubh: 实例包含两个指向其影子和内存页面的指针}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMWhj hhubj)}(h hhhhhMubh)}(hE. Stepanov, K. Serebryany. `MemorySanitizer: fast detector of uninitialized memory use in C++ `_. In Proceedings of CGO 2015.h](hE. Stepanov, K. Serebryany. }(hjO hhhNhNubj()}(h`MemorySanitizer: fast detector of uninitialized memory use in C++ `_h]hAMemorySanitizer: fast detector of uninitialized memory use in C++}(hjW hhhNhNubah}(h]h ]h"]h$]h&]nameAMemorySanitizer: fast detector of uninitialized memory use in C++jYhttps://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43308.pdfuh1j'hjO ubhtarget)}(h\ h]h}(h]>memorysanitizer-fast-detector-of-uninitialized-memory-use-in-cah ]h"]Amemorysanitizer: fast detector of uninitialized memory use in c++ah$]h&]refurijg uh1jh referencedKhjO ubh. In Proceedings of CGO 2015.}(hjO hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj> hhubji )}(hJ.. _MemorySanitizer tool: https://clang.llvm.org/docs/MemorySanitizer.htmlh]h}(h]memorysanitizer-toolah ]h"]memorysanitizer toolah$]h&]jjuh1jh hMhj> hhhhjw Kubji )}(hA.. _LLVM documentation: https://llvm.org/docs/GettingStarted.htmlh]h}(h]llvm-documentationah ]h"]llvm documentationah$]h&]jjuh1jh hMhj> hhhhjw Kubji )}(h\.. _LKML discussion: https://lore.kernel.org/all/20220614144853.3693273-1-glider@google.com/h]h}(h]lkml-discussionah ]h"]lkml discussionah$]h&]jj uh1jh hMhj> hhhhjw Kubeh}(h]id21ah ]h"] 参考文献ah$]h&]uh1jUhjWhhhhhMubeh}(h]kmsanah ]h"] 内核内存消毒剂(kmsan)ah$]h&]uh1jUhhhhhhhK ubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(jZN generatorN datestampN source_linkN source_urlN toc_backlinksentryfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerj error_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourceh _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}(memorysanitizer tool](jrjellvm documentation]jalkml discussion]jx aurefids}nameids}(j j jjjjjj}jjjjj; j8 jjjgjjjjj j j$j!jYjVj* j' j j j5 j2 j j j j j3 j0 je jb j j j+ j( j j js jp j j j j j j u nametypes}(j jjjjjj; jjjjj j$jYj* j j5 j j j3 je j j+ j js j j j uh}(j jWjjjjj}jjjjjj8 jjgj,jjmjjvj jj!jjVj'j' j\j j- j2 j j j8 j j j0 j jb j3 j jh j( j j j> jp jj j j j j j j u footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}j KsRparse_messages](hsystem_message)}(hhh]h)}(hfPossible title underline, too short for the title. Treating it as ordinary text because it's so short.h]hhPossible title underline, too short for the title. Treating it as ordinary text because it’s so short.}(hjFhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjCubah}(h]h ]h"]h$]h&]levelKtypeINFOlineKsourcehuh1jAhj\hhhhhKubjB)}(hhh]h)}(hfPossible title underline, too short for the title. Treating it as ordinary text because it's so short.h]hhPossible title underline, too short for the title. Treating it as ordinary text because it’s so short.}(hjbhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj_ubah}(h]h ]h"]h$]h&]levelKtypej\lineMXsourcehuh1jAhj hhhhhMXubetransform_messages] transformerN include_log]4Documentation/translations/zh_CN/dev-tools/kmsan.rst(NNNNta decorationNhhub.