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]hPortuguese (Brazilian)}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/pt_BR/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)}(hjh]h!Documentation/dev-tools/kmsan.rst}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(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&]uh1hhjhhhKubj)}(h2刘浩阳 Haoyang Liu h]h)}(h1刘浩阳 Haoyang Liu h](h刘浩阳 Haoyang Liu <}(hj3hhhNhNubh reference)}(htttturtleruss@hust.edu.cnh]htttturtleruss@hust.edu.cn}(hj=hhhNhNubah}(h]h ]h"]h$]h&]refuri mailto:tttturtleruss@hust.edu.cnuh1j;hj3ubh>}(hj3hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj/ubah}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhsection)}(hhh](htitle)}(h 内核内存消毒剂(KMSAN)h]h 内核内存消毒剂(KMSAN)}(hjphhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjkhhhhhK ubh)}(hKMSAN 是一个动态错误检测器,旨在查找未初始化值的使用。它基于编译器插桩,类似于用 户空间的 `MemorySanitizer tool`_。h](hKMSAN 是一个动态错误检测器,旨在查找未初始化值的使用。它基于编译器插桩,类似于用 户空间的 }(hj~hhhNhNubj<)}(h`MemorySanitizer tool`_h]hMemorySanitizer tool}(hjhhhNhNubah}(h]h ]h"]h$]h&]nameMemorySanitizer toolrefuri0https://clang.llvm.org/docs/MemorySanitizer.htmluh1j;hj~resolvedKubh。}(hj~hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK hjkhhubh)}(h|需要注意的是 KMSAN 并不适合生产环境,因为它会大幅增加内核内存占用并降低系统运行速度。h]h|需要注意的是 KMSAN 并不适合生产环境,因为它会大幅增加内核内存占用并降低系统运行速度。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjkhhubjj)}(hhh](jo)}(h 使用方法h]h 使用方法}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjhhhhhKubjj)}(hhh](jo)}(h 构建内核h]h 构建内核}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjhhhhhKubh)}(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&]uh1jihjhhhhhKubjj)}(hhh](jo)}(h 示例报告h]h 示例报告}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jnhj hhhhhKubh)}(h'以下是一个 KMSAN 报告的示例::h]h&以下是一个 KMSAN 报告的示例:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh 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 =====================================================}hj-sbah}(h]h ]h"]h$]h&]hhuh1j+hhhK!hj hhubh)}(h报告指出本地变量 ``uninit`` 在 ``do_uninit_local_array()`` 中未初始化。 第三个堆栈跟踪对应于该变量创建的位置。h](h报告指出本地变量 }(hj;hhhNhNubhliteral)}(h ``uninit``h]huninit}(hjEhhhNhNubah}(h]h ]h"]h$]h&]uh1jChj;ubh 在 }(hj;hhhNhNubjD)}(h``do_uninit_local_array()``h]hdo_uninit_local_array()}(hjWhhhNhNubah}(h]h ]h"]h$]h&]uh1jChj;ubhM 中未初始化。 第三个堆栈跟踪对应于该变量创建的位置。}(hj;hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK>hj hhubh)}(h第一个堆栈跟踪显示了未初始化值的使用位置(在 ``test_uninit_kmsan_check_memory()``)。 工具显示了局部变量中未初始化的字节及其被复制到其他内存位置前的堆栈。h](hC第一个堆栈跟踪显示了未初始化值的使用位置(在 }(hjohhhNhNubjD)}(h$``test_uninit_kmsan_check_memory()``h]h test_uninit_kmsan_check_memory()}(hjwhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjoubhm)。 工具显示了局部变量中未初始化的字节及其被复制到其他内存位置前的堆栈。}(hjohhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKAhj hhubh)}(h:KMSAN 会在以下情况下报告未初始化的值 ``v``:h](h4KMSAN 会在以下情况下报告未初始化的值 }(hjhhhNhNubjD)}(h``v``h]hv}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKEhj hhubh 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在条件判断中,例如 }(hjhhhNhNubjD)}(h``if (v) { ... }``h]hif (v) { ... }}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh;}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKGhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hB在索引或指针解引用中,例如 ``array[v]`` 或 ``*v``;h]h)}(hjh](h(在索引或指针解引用中,例如 }(hjhhhNhNubjD)}(h ``array[v]``h]harray[v]}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 或 }(hjhhhNhNubjD)}(h``*v``h]h*v}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh;}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKHhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hU当它被复制到用户空间或硬件时,例如 ``copy_to_user(..., &v, ...)``;h]h)}(hj"h](h4当它被复制到用户空间或硬件时,例如 }(hj$hhhNhNubjD)}(h``copy_to_user(..., &v, ...)``h]hcopy_to_user(..., &v, ...)}(hj+hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj$ubh;}(hj$hhhNhNubeh}(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.当它作为函数参数传递,并且启用 }(hjMhhhNhNubjD)}(h#``CONFIG_KMSAN_CHECK_PARAM_RETVAL``h]hCONFIG_KMSAN_CHECK_PARAM_RETVAL}(hjUhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjMubh 时(见下文)。}(hjMhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKJhjIubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]bullet-uh1jhhhKGhjubah}(h]h ]h"]h$]h&]uh1jhhhKGhj hhubh)}(h这些情况(除了复制数据到用户空间或硬件外,这是一个安全问题)被视为 C11 标准下的未定义行为。h]h这些情况(除了复制数据到用户空间或硬件外,这是一个安全问题)被视为 C11 标准下的未定义行为。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKLhj hhubeh}(h]id3ah ]h"] 示例报告ah$]h&]uh1jihjhhhhhKubjj)}(hhh](jo)}(h 禁用插桩h]h 禁用插桩}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjhhhhhKOubh)}(h可以用 ``__no_kmsan_checks`` 标记函数。这样,KMSAN 会忽略该函数中的未初始化值, 并将其输出标记为已初始化。如此,用户不会收到与该函数相关的 KMSAN 报告。h](h 可以用 }(hjhhhNhNubjD)}(h``__no_kmsan_checks``h]h__no_kmsan_checks}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 标记函数。这样,KMSAN 会忽略该函数中的未初始化值, 并将其输出标记为已初始化。如此,用户不会收到与该函数相关的 KMSAN 报告。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKQhjhhubh)}(hXKMSAN 还支持 ``__no_sanitize_memory`` 函数属性。KMSAN 不会对拥有该属性的函数进行 插桩,这在我们不希望编译器干扰某些底层代码(例如标记为 ``noinstr`` 的代码,该 代码隐式添加了 ``__no_sanitize_memory``)时可能很有用。h](hKMSAN 还支持 }(hjhhhNhNubjD)}(h``__no_sanitize_memory``h]h__no_sanitize_memory}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 函数属性。KMSAN 不会对拥有该属性的函数进行 插桩,这在我们不希望编译器干扰某些底层代码(例如标记为 }(hjhhhNhNubjD)}(h ``noinstr``h]hnoinstr}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh' 的代码,该 代码隐式添加了 }(hjhhhNhNubjD)}(h``__no_sanitize_memory``h]h__no_sanitize_memory}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh)时可能很有用。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKThjhhubh)}(h然而,这会有代价:此类函数的栈分配将具有不正确的影子/初始值,可能导致误报。来 自非插桩代码的函数也可能接收到不正确的元数据。h]h然而,这会有代价:此类函数的栈分配将具有不正确的影子/初始值,可能导致误报。来 自非插桩代码的函数也可能接收到不正确的元数据。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKXhjhhubh)}(hC作为经验之谈,避免显式使用 ``__no_sanitize_memory``。h](h(作为经验之谈,避免显式使用 }(hjhhhNhNubjD)}(h``__no_sanitize_memory``h]h__no_sanitize_memory}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh。}(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}hjHsbah}(h]h ]h"]h$]h&]hhuh1j+hhhK`hjhhubh)}(h或者对整个目录::h]h或者对整个目录:}(hjVhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKbhjhhubj,)}(hKMSAN_SANITIZE := nh]hKMSAN_SANITIZE := n}hjdsbah}(h]h ]h"]h$]h&]hhuh1j+hhhKdhjhhubh)}(h将其应用到文件或目录中的每个函数。大多数用户不会需要 KMSAN_SANITIZE, 除非他们的代码被 KMSAN 破坏(例如在早期启动时运行的代码)。h]h将其应用到文件或目录中的每个函数。大多数用户不会需要 KMSAN_SANITIZE, 除非他们的代码被 KMSAN 破坏(例如在早期启动时运行的代码)。}(hjrhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKfhjhhubh)}(hXU还可以通过调用 ``kmsan_disable_current()`` 和 ``kmsan_enable_current()`` 暂时对当前任务禁用 KMSAN 检查。每个 ``kmsan_enable_current()`` 必须在 ``kmsan_disable_current()`` 之后调用;这些调用对可以嵌套。在调用时需要注意保持 嵌套区域简短,并且尽可能使用其他方法禁用插桩。h](h还可以通过调用 }(hjhhhNhNubjD)}(h``kmsan_disable_current()``h]hkmsan_disable_current()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 和 }(hjhhhNhNubjD)}(h``kmsan_enable_current()``h]hkmsan_enable_current()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh3 暂时对当前任务禁用 KMSAN 检查。每个 }(hjhhhNhNubjD)}(h``kmsan_enable_current()``h]hkmsan_enable_current()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 必须在 }(hjhhhNhNubjD)}(h``kmsan_disable_current()``h]hkmsan_disable_current()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 之后调用;这些调用对可以嵌套。在调用时需要注意保持 嵌套区域简短,并且尽可能使用其他方法禁用插桩。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKihjhhubeh}(h]id4ah ]h"] 禁用插桩ah$]h&]uh1jihjhhhhhKOubeh}(h]id1ah ]h"] 使用方法ah$]h&]uh1jihjkhhhhhKubjj)}(hhh](jo)}(h支持h]h支持}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjhhhhhKoubh)}(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&]uh1jihjkhhhhhKoubjj)}(hhh](jo)}(hKMSAN 的工作原理h]hKMSAN 的工作原理}(hj2hhhNhNubah}(h]h ]h"]h$]h&]uh1jnhj/hhhhhKwubjj)}(hhh](jo)}(hKMSAN 阴影内存h]hKMSAN 阴影内存}(hjChhhNhNubah}(h]h ]h"]h$]h&]uh1jnhj@hhhhhKzubh)}(hXsKMSAN 将一个元数据字节(也称为阴影字节)与每个内核内存字节关联。仅当内核内存字节 的相应位未初始化时,阴影字节中的一个比特位才会被设置。将内存标记为未初始化(即 将其阴影字节设置为 ``0xff``)称为中毒,将其标记为已初始化(将阴影字节设置为 ``0x00``)称为解毒。h](hXKMSAN 将一个元数据字节(也称为阴影字节)与每个内核内存字节关联。仅当内核内存字节 的相应位未初始化时,阴影字节中的一个比特位才会被设置。将内存标记为未初始化(即 将其阴影字节设置为 }(hjQhhhNhNubjD)}(h``0xff``h]h0xff}(hjYhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjQubhI)称为中毒,将其标记为已初始化(将阴影字节设置为 }(hjQhhhNhNubjD)}(h``0x00``h]h0x00}(hjkhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjQubh)称为解毒。}(hjQhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK|hj@hhubh)}(h当在栈上分配新变量时,默认情况下它会中毒,这由编译器插入的插桩代码完成(除非它 是立即初始化的栈变量)。任何未使用 ``__GFP_ZERO`` 的堆分配也会中毒。h](h当在栈上分配新变量时,默认情况下它会中毒,这由编译器插入的插桩代码完成(除非它 是立即初始化的栈变量)。任何未使用 }(hjhhhNhNubjD)}(h``__GFP_ZERO``h]h __GFP_ZERO}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 的堆分配也会中毒。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj@hhubh)}(h编译器插桩还跟踪阴影值在代码中的使用。当需要时,插桩代码会调用 ``mm/kmsan/`` 中 的运行时库以持久化阴影值。h](h^编译器插桩还跟踪阴影值在代码中的使用。当需要时,插桩代码会调用 }(hjhhhNhNubjD)}(h ``mm/kmsan/``h]h mm/kmsan/}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh, 中 的运行时库以持久化阴影值。}(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&]hhuh1j+hhhKhj@hhubh)}(h在这种情况下, ``a`` 的阴影为 ``0``, ``b`` 的阴影为 ``0xffffffff``, ``c`` 的阴影为 ``0xffffff00``。这意味着 ``c`` 的高三个字节未初始化,而低字节已 初始化。h](h在这种情况下, }(hjhhhNhNubjD)}(h``a``h]ha}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 的阴影为 }(hjhhhNhNubjD)}(h``0``h]h0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh, }(hjhhhNhNubjD)}(h``b``h]hb}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 的阴影为 }hjsbjD)}(h``0xffffffff``h]h 0xffffffff}(hj+hhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh, }(hjhhhNhNubjD)}(h``c``h]hc}(hj=hhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 的阴影为 }hjsbjD)}(h``0xffffff00``h]h 0xffffff00}(hjOhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh。这意味着 }(hjhhhNhNubjD)}(h``c``h]hc}(hjahhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh> 的高三个字节未初始化,而低字节已 初始化。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj@hhubeh}(h]id7ah ]h"]kmsan 阴影内存ah$]h&]uh1jihj/hhhhhKzubjj)}(hhh](jo)}(h 起源跟踪h]h 起源跟踪}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjhhhhhKubh)}(hX每四字节的内核内存都有一个所谓的源点与之映射。这个源点描述了在程序执行中,未初 始化值的创建点。每个源点都与完整的分配栈(对于堆分配的内存)或包含未初始化变 量的函数(对于局部变量)相关联。h]hX每四字节的内核内存都有一个所谓的源点与之映射。这个源点描述了在程序执行中,未初 始化值的创建点。每个源点都与完整的分配栈(对于堆分配的内存)或包含未初始化变 量的函数(对于局部变量)相关联。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX当一个未初始化的变量在栈或堆上分配时,会创建一个新的源点值,并将该变量的初始值 填充为这个值。当从内存中读取一个值时,其初始值也会被读取并与阴影一起保留。对于 每个接受一个或多个值的指令,结果的源点是与任何未初始化输入相对应的源点之一。如 果一个污染值被写入内存,其起源也会被写入相应的存储中。h]hX当一个未初始化的变量在栈或堆上分配时,会创建一个新的源点值,并将该变量的初始值 填充为这个值。当从内存中读取一个值时,其初始值也会被读取并与阴影一起保留。对于 每个接受一个或多个值的指令,结果的源点是与任何未初始化输入相对应的源点之一。如 果一个污染值被写入内存,其起源也会被写入相应的存储中。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h 示例 1::h]h 示例 1:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj,)}(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&]hhuh1j+hhhKhjhhubh)}(h在这种情况下, ``b`` 的源点是在函数入口时生成的,并在加法结果写入内存之前存储到 ``c`` 的源点中。h](h在这种情况下, }(hjhhhNhNubjD)}(h``b``h]hb}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubhY 的源点是在函数入口时生成的,并在加法结果写入内存之前存储到 }(hjhhhNhNubjD)}(h``c``h]hc}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 的源点中。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX(如果几个变量共享相同的源点地址,则它们被存储在同一个四字节块中。在这种情况下, 对任何变量的每次写入都会更新所有变量的源点。在这种情况下我们必须牺牲精度,因 为为单独的位(甚至字节)存储源点成本过高。h]hX(如果几个变量共享相同的源点地址,则它们被存储在同一个四字节块中。在这种情况下, 对任何变量的每次写入都会更新所有变量的源点。在这种情况下我们必须牺牲精度,因 为为单独的位(甚至字节)存储源点成本过高。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h 示例 2::h]h 示例 2:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj,)}(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&]hhuh1j+hhhKhjhhubh)}(h如果 ``a`` 已初始化而 ``b`` 未初始化,则结果的阴影为 0xffff0000,结果的源点为 ``b`` 的源点。 ``ret.s[0]`` 会有相同的起源,但它不会被使用,因为该变量已初始化。h](h如果 }(hj&hhhNhNubjD)}(h``a``h]ha}(hj.hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj&ubh 已初始化而 }(hj&hhhNhNubjD)}(h``b``h]hb}(hj@hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj&ubhF 未初始化,则结果的阴影为 0xffff0000,结果的源点为 }(hj&hhhNhNubjD)}(h``b``h]hb}(hjRhhhNhNubah}(h]h ]h"]h$]h&]uh1jChj&ubh 的源点。 }(hj&hhhNhNubjD)}(h ``ret.s[0]``h]hret.s[0]}(hjdhhhNhNubah}(h]h ]h"]h$]h&]uh1jChj&ubhO 会有相同的起源,但它不会被使用,因为该变量已初始化。}(hj&hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hQ如果两个函数参数都未初始化,则只保留第二个参数的源点。h]hQ如果两个函数参数都未初始化,则只保留第二个参数的源点。}(hj|hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubjj)}(hhh](jo)}(h 源点链h]h 源点链}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjhhhhhKubh)}(hX为了便于调试,KMSAN 在每次将未初始化值存储到内存时都会创建一个新的源点。新的源点 引用了其创建栈以及值的前一个起源。这可能导致内存消耗增加,因此我们在运行时限制 了源点链的长度。h]hX为了便于调试,KMSAN 在每次将未初始化值存储到内存时都会创建一个新的源点。新的源点 引用了其创建栈以及值的前一个起源。这可能导致内存消耗增加,因此我们在运行时限制 了源点链的长度。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id9ah ]h"] 源点链ah$]h&]uh1jihjhhhhhKubeh}(h]id8ah ]h"] 起源跟踪ah$]h&]uh1jihj/hhhhhKubjj)}(hhh](jo)}(hClang 插桩 APIh]hClang 插桩 API}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjhhhhhKubh)}(hrClang 插桩通过在内核代码中插入定义在 ``mm/kmsan/instrumentation.c`` 中的函数调用 来实现。h](h4Clang 插桩通过在内核代码中插入定义在 }(hjhhhNhNubjD)}(h``mm/kmsan/instrumentation.c``h]hmm/kmsan/instrumentation.c}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 中的函数调用 来实现。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubjj)}(hhh](jo)}(h 阴影操作h]h 阴影操作}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjhhhhhKubh)}(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)}hj sbah}(h]h ]h"]h$]h&]hhuh1j+hhhKhjhhubh)}(h*函数名依赖于内存访问的大小。h]h*函数名依赖于内存访问的大小。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h编译器确保对于每个加载的值,其阴影和原始值都从内存中读取。当一个值存储到内存时 ,其阴影和原始值也会通过元数据指针进行存储。h]h编译器确保对于每个加载的值,其阴影和原始值都从内存中读取。当一个值存储到内存时 ,其阴影和原始值也会通过元数据指针进行存储。}(hj%hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id10ah ]h"] 阴影操作ah$]h&]uh1jihjhhhhhKubjj)}(hhh](jo)}(h处理局部变量h]h处理局部变量}(hj>hhhNhNubah}(h]h ]h"]h$]h&]uh1jnhj;hhhhhKubh)}(ht一个特殊的函数用于为局部变量创建一个新的原始值,并将该变量的原始值设置为该值::h]hs一个特殊的函数用于为局部变量创建一个新的原始值,并将该变量的原始值设置为该值:}(hjLhhhNhNubah}(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)}hjZsbah}(h]h ]h"]h$]h&]hhuh1j+hhhKhj;hhubeh}(h]id11ah ]h"]处理局部变量ah$]h&]uh1jihjhhhhhKubjj)}(hhh](jo)}(h访问每个任务数据h]h访问每个任务数据}(hjshhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjphhhhhKubh)}(he在每个插桩函数的开始处,KMSAN 插入一个对 ``__msan_get_context_state()`` 的调用 ::h](h:在每个插桩函数的开始处,KMSAN 插入一个对 }(hjhhhNhNubjD)}(h``__msan_get_context_state()``h]h__msan_get_context_state()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 的调用}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjphhubj,)}(h3kmsan_context_state *__msan_get_context_state(void)h]h3kmsan_context_state *__msan_get_context_state(void)}hjsbah}(h]h ]h"]h$]h&]hhuh1j+hhhKhjphhubh)}(hA``kmsan_context_state`` 在 ``include/linux/kmsan.h`` 中声明::h](jD)}(h``kmsan_context_state``h]hkmsan_context_state}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 在 }(hjhhhNhNubjD)}(h``include/linux/kmsan.h``h]hinclude/linux/kmsan.h}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jChjubh 中声明:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjphhubj,)}(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&]hhuh1j+hhhKhjphhubhdefinition_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](jD)}(h#``CONFIG_KMSAN_CHECK_PARAM_RETVAL``h]hCONFIG_KMSAN_CHECK_PARAM_RETVAL}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh 检查参数)。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1j hjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjphhhhhNubeh}(h]id12ah ]h"]访问每个任务数据ah$]h&]uh1jihjhhhhhKubjj)}(hhh](jo)}(h$将未初始化的值传递给函数h]h$将未初始化的值传递给函数}(hjD hhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjA hhhhhMubh)}(hClang 的 MemorySanitizer 插桩有一个选项 ``-fsanitize-memory-param-retval``,该 选项使编译器检查按值传递的函数参数,以及函数返回值。h](h0Clang 的 MemorySanitizer 插桩有一个选项 }(hjR hhhNhNubjD)}(h"``-fsanitize-memory-param-retval``h]h-fsanitize-memory-param-retval}(hjZ hhhNhNubah}(h]h ]h"]h$]h&]uh1jChjR ubhU,该 选项使编译器检查按值传递的函数参数,以及函数返回值。}(hjR hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjA hhubh)}(h该选项由 ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 控制,默认启用以便 KMSAN 更早报告 未初始化的值。有关更多细节,请参考 `LKML discussion`_。h](h 该选项由 }(hjr hhhNhNubjD)}(h#``CONFIG_KMSAN_CHECK_PARAM_RETVAL``h]hCONFIG_KMSAN_CHECK_PARAM_RETVAL}(hjz hhhNhNubah}(h]h ]h"]h$]h&]uh1jChjr ubhd 控制,默认启用以便 KMSAN 更早报告 未初始化的值。有关更多细节,请参考 }(hjr hhhNhNubj<)}(h`LKML discussion`_h]hLKML discussion}(hj hhhNhNubah}(h]h ]h"]h$]h&]nameLKML discussionjGhttps://lore.kernel.org/all/20220614144853.3693273-1-glider@google.com/uh1j;hjr jKubh。}(hjr hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjA hhubh)}(h由于 LLVM 中的实现检查的方式(它们仅应用于标记为 ``noundef`` 的参数),并不是所 有参数都能保证被检查,因此我们不能放弃 ``kmsan_context_state`` 中的元数据存储 。h](hF由于 LLVM 中的实现检查的方式(它们仅应用于标记为 }(hj hhhNhNubjD)}(h ``noundef``h]hnoundef}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubhW 的参数),并不是所 有参数都能保证被检查,因此我们不能放弃 }(hj hhhNhNubjD)}(h``kmsan_context_state``h]hkmsan_context_state}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh 中的元数据存储 。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM hjA hhubeh}(h]id13ah ]h"]$将未初始化的值传递给函数ah$]h&]uh1jihjhhhhhMubjj)}(hhh](jo)}(h字符串函数h]h字符串函数}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jnhj hhhhhMubh)}(h编译器将对 ``memcpy()``/``memmove()``/``memset()`` 的调用替换为以下函数。这些函 数在数据结构初始化或复制时也会被调用,确保阴影和原始值与数据一起复制::h](h编译器将对 }(hj hhhNhNubjD)}(h ``memcpy()``h]hmemcpy()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh/}(hj hhhNhNubjD)}(h ``memmove()``h]h memmove()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh/}hj sbjD)}(h ``memset()``h]hmemset()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj 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)}hj6 sbah}(h]h ]h"]h$]h&]hhuh1j+hhhMhj hhubeh}(h]id14ah ]h"]字符串函数ah$]h&]uh1jihjhhhhhMubjj)}(hhh](jo)}(h 错误报告h]h 错误报告}(hjO hhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjL hhhhhMubh)}(ht对于每个值的使用,编译器发出一个阴影检查,在值中毒的情况下调用 ``__msan_warning()``::h](h^对于每个值的使用,编译器发出一个阴影检查,在值中毒的情况下调用 }(hj] hhhNhNubjD)}(h``__msan_warning()``h]h__msan_warning()}(hje hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj] ubh:}(hj] hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjL hhubj,)}(hvoid __msan_warning(u32 origin)h]hvoid __msan_warning(u32 origin)}hj} sbah}(h]h ]h"]h$]h&]hhuh1j+hhhMhjL hhubh)}(h=``__msan_warning()`` 使 KMSAN 运行时打印错误报告。h](jD)}(h``__msan_warning()``h]h__msan_warning()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh) 使 KMSAN 运行时打印错误报告。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjL hhubeh}(h]id15ah ]h"] 错误报告ah$]h&]uh1jihjhhhhhMubjj)}(hhh](jo)}(h内联汇编插桩h]h内联汇编插桩}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jnhj hhhhhM"ubh)}(h8KMSAN 对每个内联汇编输出进行插桩,调用::h]h7KMSAN 对每个内联汇编输出进行插桩,调用:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM$hj hhubj,)}(hkmsan.cstate`` 来 保存函数参数和返回值的元数据。h](h4在内核任务上下文中运行时,KMSAN 使用 }(hj hhhNhNubjD)}(h``current->kmsan.cstate``h]hcurrent->kmsan.cstate}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh2 来 保存函数参数和返回值的元数据。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMJhj| hhubh)}(h但在内核运行于中断、softirq 或 NMI 上下文中, ``current`` 不可用时, KMSAN 切换到每 CPU 中断状态::h](h>但在内核运行于中断、softirq 或 NMI 上下文中, }(hj hhhNhNubjD)}(h ``current``h]hcurrent}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh5 不可用时, KMSAN 切换到每 CPU 中断状态:}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMMhj| 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&]hhuh1j+hhhMPhj| hhubeh}(h]id19ah ]h"]kmsan 上下文ah$]h&]uh1jihj hhhhhMHubjj)}(hhh](jo)}(h元数据分配h]h元数据分配}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jnhj hhhhhMSubh)}(h*内核中有多个地方存储元数据。h]h*内核中有多个地方存储元数据。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMUhj hhubh)}(hV1. 每个 ``struct page`` 实例包含两个指向其影子和内存页面的指针 ::h](h 1. 每个 }(hj hhhNhNubjD)}(h``struct page``h]h struct page}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh: 实例包含两个指向其影子和内存页面的指针}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMWhj hhubj,)}(h hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMchj hhubh)}(h实际上,由相同 ``alloc_pages()`` 调用返回的连续内存页面将具有连续的元数据,而 如果这些页面属于两个不同的分配,它们的元数据页面可能会被碎片化。h](h实际上,由相同 }(hjL hhhNhNubjD)}(h``alloc_pages()``h]h alloc_pages()}(hjT hhhNhNubah}(h]h ]h"]h$]h&]uh1jChjL ubh 调用返回的连续内存页面将具有连续的元数据,而 如果这些页面属于两个不同的分配,它们的元数据页面可能会被碎片化。}(hjL hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMfhj hhubh)}(hw对于内核数据( ``.data``、 ``.bss`` 等)和每 CPU 内存区域,也没有对元数据连续 性的保证。h](h对于内核数据( }(hjl hhhNhNubjD)}(h ``.data``h]h.data}(hjt hhhNhNubah}(h]h ]h"]h$]h&]uh1jChjl ubh、 }(hjl hhhNhNubjD)}(h``.bss``h]h.bss}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChjl ubhL 等)和每 CPU 内存区域,也没有对元数据连续 性的保证。}(hjl hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMihj hhubh)}(h在 ``__msan_metadata_ptr_for_XXX_YYY()`` 遇到两个页面之间的 非连续元数据边界时,它返回指向假影子/源区域的指针::h](h在 }(hj hhhNhNubjD)}(h%``__msan_metadata_ptr_for_XXX_YYY()``h]h!__msan_metadata_ptr_for_XXX_YYY()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubhg 遇到两个页面之间的 非连续元数据边界时,它返回指向假影子/源区域的指针:}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMlhj hhubj,)}(hchar dummy_load_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); char dummy_store_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));h]hchar dummy_load_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); char dummy_store_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));}hj sbah}(h]h ]h"]h$]h&]hhuh1j+hhhMohj hhubh)}(h``dummy_load_page`` 被初始化为零,因此读取它始终返回零。对 ``dummy_store_page`` 的 所有写入都被忽略。h](jD)}(h``dummy_load_page``h]hdummy_load_page}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh; 被初始化为零,因此读取它始终返回零。对 }(hj hhhNhNubjD)}(h``dummy_store_page``h]hdummy_store_page}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh 的 所有写入都被忽略。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMrhj hhubh)}(hX2. 对于 vmalloc 内存和模块,内存范围、影子和源之间有一个直接映射。KMSAN 将 vmalloc 区域缩小了 3/4,仅使前四分之一可用于 ``vmalloc()``。vmalloc 区域的第二个四分之一包含第一个四分之一的影子内存,第三个四分之一保存源。第四个 四分之一的小部分包含内核模块的影子和源。有关更多详细信息,请参阅 ``arch/x86/include/asm/pgtable_64_types.h``。h](h2. 对于 vmalloc 内存和模块,内存范围、影子和源之间有一个直接映射。KMSAN 将 vmalloc 区域缩小了 3/4,仅使前四分之一可用于 }(hj hhhNhNubjD)}(h ``vmalloc()``h]h vmalloc()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh。vmalloc 区域的第二个四分之一包含第一个四分之一的影子内存,第三个四分之一保存源。第四个 四分之一的小部分包含内核模块的影子和源。有关更多详细信息,请参阅 }(hj hhhNhNubjD)}(h+``arch/x86/include/asm/pgtable_64_types.h``h]h'arch/x86/include/asm/pgtable_64_types.h}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jChj ubh。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMuhj hhubh)}(h当一系列页面映射到一个连续的虚拟内存空间时,它们的影子和源页面也以连续区域的方 式映射。h]h当一系列页面映射到一个连续的虚拟内存空间时,它们的影子和源页面也以连续区域的方 式映射。}(hj, hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM{hj hhubeh}(h]id20ah ]h"]元数据分配ah$]h&]uh1jihj hhhhhMSubeh}(h]id17ah ]h"] 运行时库ah$]h&]uh1jihj/hhhhhM0ubeh}(h]id6ah ]h"]kmsan 的工作原理ah$]h&]uh1jihjkhhhhhKwubjj)}(hhh](jo)}(h 参考文献h]h 参考文献}(hjU hhhNhNubah}(h]h ]h"]h$]h&]uh1jnhjR hhhhhMubh)}(hE. Stepanov, K. Serebryany. `MemorySanitizer: fast detector of uninitialized memory use in C++ `_. In Proceedings of CGO 2015.h](hE. Stepanov, K. Serebryany. }(hjc hhhNhNubj<)}(h`MemorySanitizer: fast detector of uninitialized memory use in C++ `_h]hAMemorySanitizer: fast detector of uninitialized memory use in C++}(hjk 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;hjc 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&]refurij{ uh1j| referencedKhjc ubh. In Proceedings of CGO 2015.}(hjc hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjR hhubj} )}(hJ.. _MemorySanitizer tool: https://clang.llvm.org/docs/MemorySanitizer.htmlh]h}(h]memorysanitizer-toolah ]h"]memorysanitizer toolah$]h&]jjuh1j| hMhjR hhhhj Kubj} )}(hA.. _LLVM documentation: https://llvm.org/docs/GettingStarted.htmlh]h}(h]llvm-documentationah ]h"]llvm documentationah$]h&]jjuh1j| hMhjR hhhhj Kubj} )}(h\.. _LKML discussion: https://lore.kernel.org/all/20220614144853.3693273-1-glider@google.com/h]h}(h]lkml-discussionah ]h"]lkml discussionah$]h&]jj uh1j| hMhjR hhhhj Kubeh}(h]id21ah ]h"] 参考文献ah$]h&]uh1jihjkhhhhhMubeh}(h]kmsanah ]h"] 内核内存消毒剂(kmsan)ah$]h&]uh1jihhhhhhhK ubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(jnN 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_sourcehnj _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](jjellvm documentation]jalkml discussion]j aurefids}nameids}(j j jjj jjjjjj,j)jO jL j~j{jjjjj j j8j5jmjjj> j; j j jI jF j j j j jG jD jy jv j j j? j< j j j j j j j j j j u nametypes}(j jj jjj,jO j~jjj j8jmj> j jI j j jG jy j j? j j j j j uh}(j jkjjjjjj jjj)jjL j/j{j@jjjjj jj5jjjj;j; jpj jA jF j j jL j j jD j jv jG j j| j< j j jR j j~ 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.}(hjZhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjWubah}(h]h ]h"]h$]h&]levelKtypeINFOlineKsourcehuh1jUhjphhhhhKubjV)}(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.}(hjvhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjsubah}(h]h ]h"]h$]h&]levelKtypejplineMXsourcehuh1jUhj hhhhhMXubetransform_messages] transformerN include_log]4Documentation/translations/zh_CN/dev-tools/kmsan.rst(NNNNta decorationNhhub.