.. SPDX-License-Identifier: GPL-2.0 .. include:: ../disclaimer-zh_CN.rst :Original: Documentation/dev-tools/kmsan.rst :Translator: 刘浩阳 Haoyang Liu <tttturtleruss@hust.edu.cn> ======================= å†…æ ¸å†…å˜æ¶ˆæ¯’剂(KMSAN) ======================= KMSAN 是一个动æ€é”™è¯¯æ£€æµ‹å™¨ï¼Œæ—¨åœ¨æŸ¥æ‰¾æœªåˆå§‹åŒ–值的使用。它基于编译器æ’桩,类似于用 户空间的 `MemorySanitizer tool`_。 需è¦æ³¨æ„的是 KMSAN 并ä¸é€‚åˆç”Ÿäº§çŽ¯å¢ƒï¼Œå› ä¸ºå®ƒä¼šå¤§å¹…å¢žåŠ å†…æ ¸å†…å˜å 用并é™ä½Žç³»ç»Ÿè¿è¡Œé€Ÿåº¦ã€‚ 使用方法 ======== æž„å»ºå†…æ ¸ -------- è¦æž„建带有 KMSAN çš„å†…æ ¸ï¼Œä½ éœ€è¦ä¸€ä¸ªè¾ƒæ–°çš„ Clang (14.0.6+)。 请å‚阅 `LLVM documentation`_ 了解如何构建 Clang。 现在é…置并构建一个å¯ç”¨ CONFIG_KMSAN çš„å†…æ ¸ã€‚ 示例报告 -------- 以下是一个 KMSAN 报告的示例:: ===================================================== 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 ===================================================== 报告指出本地å˜é‡ ``uninit`` 在 ``do_uninit_local_array()`` ä¸æœªåˆå§‹åŒ–。 ç¬¬ä¸‰ä¸ªå †æ ˆè·Ÿè¸ªå¯¹åº”äºŽè¯¥å˜é‡åˆ›å»ºçš„ä½ç½®ã€‚ ç¬¬ä¸€ä¸ªå †æ ˆè·Ÿè¸ªæ˜¾ç¤ºäº†æœªåˆå§‹åŒ–值的使用ä½ç½®ï¼ˆåœ¨ ``test_uninit_kmsan_check_memory()``)。 工具显示了局部å˜é‡ä¸æœªåˆå§‹åŒ–çš„å—节åŠå…¶è¢«å¤åˆ¶åˆ°å…¶ä»–内å˜ä½ç½®å‰çš„å †æ ˆã€‚ KMSAN 会在以下情况下报告未åˆå§‹åŒ–的值 ``v``: - 在æ¡ä»¶åˆ¤æ–ä¸ï¼Œä¾‹å¦‚ ``if (v) { ... }``ï¼› - 在索引或指针解引用ä¸ï¼Œä¾‹å¦‚ ``array[v]`` 或 ``*v``ï¼› - 当它被å¤åˆ¶åˆ°ç”¨æˆ·ç©ºé—´æˆ–硬件时,例如 ``copy_to_user(..., &v, ...)``ï¼› - 当它作为函数å‚æ•°ä¼ é€’ï¼Œå¹¶ä¸”å¯ç”¨ ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 时(è§ä¸‹æ–‡ï¼‰ã€‚ 这些情况(除了å¤åˆ¶æ•°æ®åˆ°ç”¨æˆ·ç©ºé—´æˆ–硬件外,这是一个安全问题)被视为 C11 æ ‡å‡†ä¸‹çš„æœªå®šä¹‰è¡Œä¸ºã€‚ ç¦ç”¨æ’æ¡© -------- å¯ä»¥ç”¨ ``__no_kmsan_checks`` æ ‡è®°å‡½æ•°ã€‚è¿™æ ·ï¼ŒKMSAN 会忽略该函数ä¸çš„未åˆå§‹åŒ–值, å¹¶å°†å…¶è¾“å‡ºæ ‡è®°ä¸ºå·²åˆå§‹åŒ–。如æ¤ï¼Œç”¨æˆ·ä¸ä¼šæ”¶åˆ°ä¸Žè¯¥å‡½æ•°ç›¸å…³çš„ KMSAN 报告。 KMSAN è¿˜æ”¯æŒ ``__no_sanitize_memory`` 函数属性。KMSAN ä¸ä¼šå¯¹æ‹¥æœ‰è¯¥å±žæ€§çš„函数进行 æ’桩,这在我们ä¸å¸Œæœ›ç¼–译器干扰æŸäº›åº•å±‚代ç ï¼ˆä¾‹å¦‚æ ‡è®°ä¸º ``noinstr`` 的代ç ,该 代ç éšå¼æ·»åŠ 了 ``__no_sanitize_memory``)时å¯èƒ½å¾ˆæœ‰ç”¨ã€‚ 然而,这会有代价:æ¤ç±»å‡½æ•°çš„æ ˆåˆ†é…将具有ä¸æ£ç¡®çš„å½±å/åˆå§‹å€¼ï¼Œå¯èƒ½å¯¼è‡´è¯¯æŠ¥ã€‚æ¥ è‡ªéžæ’桩代ç 的函数也å¯èƒ½æŽ¥æ”¶åˆ°ä¸æ£ç¡®çš„元数æ®ã€‚ 作为ç»éªŒä¹‹è°ˆï¼Œé¿å…显å¼ä½¿ç”¨ ``__no_sanitize_memory``。 也å¯ä»¥é€šè¿‡ Makefile ç¦ç”¨ KMSAN 对æŸä¸ªæ–‡ä»¶ï¼ˆä¾‹å¦‚ main.o)的作用:: KMSAN_SANITIZE_main.o := n 或者对整个目录:: KMSAN_SANITIZE := n 将其应用到文件或目录ä¸çš„æ¯ä¸ªå‡½æ•°ã€‚大多数用户ä¸ä¼šéœ€è¦ KMSAN_SANITIZE, 除éžä»–们的代ç 被 KMSAN ç ´å(例如在早期å¯åŠ¨æ—¶è¿è¡Œçš„代ç )。 还å¯ä»¥é€šè¿‡è°ƒç”¨ ``kmsan_disable_current()`` å’Œ ``kmsan_enable_current()`` 暂时对当å‰ä»»åŠ¡ç¦ç”¨ KMSAN 检查。æ¯ä¸ª ``kmsan_enable_current()`` 必须在 ``kmsan_disable_current()`` 之åŽè°ƒç”¨ï¼›è¿™äº›è°ƒç”¨å¯¹å¯ä»¥åµŒå¥—。在调用时需è¦æ³¨æ„ä¿æŒ 嵌套区域简çŸï¼Œå¹¶ä¸”å°½å¯èƒ½ä½¿ç”¨å…¶ä»–方法ç¦ç”¨æ’桩。 æ”¯æŒ ==== 为了使用 KMSANï¼Œå†…æ ¸å¿…é¡»ä½¿ç”¨ Clang 构建,到目å‰ä¸ºæ¢ï¼ŒClang æ˜¯å”¯ä¸€æ”¯æŒ KMSAN çš„ç¼–è¯‘å™¨ã€‚å†…æ ¸æ’桩过程基于用户空间的 `MemorySanitizer tool`_。 ç›®å‰è¿è¡Œæ—¶åº“ä»…æ”¯æŒ x86_64 架构。 KMSAN çš„å·¥ä½œåŽŸç† ================ KMSAN é˜´å½±å†…å˜ -------------- KMSAN 将一个元数æ®å—节(也称为阴影å—节)与æ¯ä¸ªå†…æ ¸å†…å˜å—节关è”ã€‚ä»…å½“å†…æ ¸å†…å˜å—节 的相应ä½æœªåˆå§‹åŒ–时,阴影å—节ä¸çš„一个比特ä½æ‰ä¼šè¢«è®¾ç½®ã€‚将内å˜æ ‡è®°ä¸ºæœªåˆå§‹åŒ–ï¼ˆå³ å°†å…¶é˜´å½±å—节设置为 ``0xff``)称为ä¸æ¯’ï¼Œå°†å…¶æ ‡è®°ä¸ºå·²åˆå§‹åŒ–(将阴影å—节设置为 ``0x00``)称为解毒。 å½“åœ¨æ ˆä¸Šåˆ†é…æ–°å˜é‡æ—¶ï¼Œé»˜è®¤æƒ…况下它会ä¸æ¯’,这由编译器æ’入的æ’桩代ç 完æˆï¼ˆé™¤éžå®ƒ 是立å³åˆå§‹åŒ–çš„æ ˆå˜é‡ï¼‰ã€‚任何未使用 ``__GFP_ZERO`` çš„å †åˆ†é…也会ä¸æ¯’。 编译器æ’桩还跟踪阴影值在代ç ä¸çš„使用。当需è¦æ—¶ï¼Œæ’桩代ç 会调用 ``mm/kmsan/`` ä¸ çš„è¿è¡Œæ—¶åº“以æŒä¹…化阴影值。 基本或å¤åˆç±»åž‹çš„阴影值是长度相åŒçš„å—节数组。当常é‡å€¼å†™å…¥å†…å˜æ—¶ï¼Œè¯¥å†…å˜ä¼šè¢«è§£æ¯’ 。当从内å˜è¯»å–值时,其阴影内å˜ä¹Ÿä¼šè¢«èŽ·å–ï¼Œå¹¶ä¼ é€’åˆ°æ‰€æœ‰ä½¿ç”¨è¯¥å€¼çš„æ“作ä¸ã€‚å¯¹äºŽæ¯ ä¸ªéœ€è¦ä¸€ä¸ªæˆ–多个值的指令,编译器会生æˆä»£ç æ ¹æ®è¿™äº›å€¼åŠå…¶é˜´å½±æ¥è®¡ç®—结果的阴影。 示例:: int a = 0xff; // i.e. 0x000000ff int b; int c = a | b; 在这ç§æƒ…况下, ``a`` 的阴影为 ``0``, ``b`` 的阴影为 ``0xffffffff``, ``c`` 的阴影为 ``0xffffff00``。这æ„å‘³ç€ ``c`` 的高三个å—节未åˆå§‹åŒ–,而低å—节已 åˆå§‹åŒ–。 èµ·æºè·Ÿè¸ª -------- æ¯å››å—èŠ‚çš„å†…æ ¸å†…å˜éƒ½æœ‰ä¸€ä¸ªæ‰€è°“çš„æºç‚¹ä¸Žä¹‹æ˜ 射。这个æºç‚¹æ述了在程åºæ‰§è¡Œä¸ï¼Œæœªåˆ 始化值的创建点。æ¯ä¸ªæºç‚¹éƒ½ä¸Žå®Œæ•´çš„分é…æ ˆï¼ˆå¯¹äºŽå †åˆ†é…的内å˜ï¼‰æˆ–包å«æœªåˆå§‹åŒ–å˜ é‡çš„函数(对于局部å˜é‡ï¼‰ç›¸å…³è”。 当一个未åˆå§‹åŒ–çš„å˜é‡åœ¨æ ˆæˆ–å †ä¸Šåˆ†é…时,会创建一个新的æºç‚¹å€¼ï¼Œå¹¶å°†è¯¥å˜é‡çš„åˆå§‹å€¼ 填充为这个值。当从内å˜ä¸è¯»å–一个值时,其åˆå§‹å€¼ä¹Ÿä¼šè¢«è¯»å–并与阴影一起ä¿ç•™ã€‚对于 æ¯ä¸ªæŽ¥å—一个或多个值的指令,结果的æºç‚¹æ˜¯ä¸Žä»»ä½•æœªåˆå§‹åŒ–输入相对应的æºç‚¹ä¹‹ä¸€ã€‚如 果一个污染值被写入内å˜ï¼Œå…¶èµ·æºä¹Ÿä¼šè¢«å†™å…¥ç›¸åº”çš„å˜å‚¨ä¸ã€‚ 示例 1:: int a = 42; int b; int c = a + b; 在这ç§æƒ…况下, ``b`` çš„æºç‚¹æ˜¯åœ¨å‡½æ•°å…¥å£æ—¶ç”Ÿæˆçš„ï¼Œå¹¶åœ¨åŠ æ³•ç»“æžœå†™å…¥å†…å˜ä¹‹å‰å˜å‚¨åˆ° ``c`` çš„æºç‚¹ä¸ã€‚ å¦‚æžœå‡ ä¸ªå˜é‡å…±äº«ç›¸åŒçš„æºç‚¹åœ°å€ï¼Œåˆ™å®ƒä»¬è¢«å˜å‚¨åœ¨åŒä¸€ä¸ªå››å—节å—ä¸ã€‚在这ç§æƒ…况下, 对任何å˜é‡çš„æ¯æ¬¡å†™å…¥éƒ½ä¼šæ›´æ–°æ‰€æœ‰å˜é‡çš„æºç‚¹ã€‚在这ç§æƒ…å†µä¸‹æˆ‘ä»¬å¿…é¡»ç‰ºç‰²ç²¾åº¦ï¼Œå› ä¸ºä¸ºå•ç‹¬çš„ä½ï¼ˆç”šè‡³å—节)å˜å‚¨æºç‚¹æˆæœ¬è¿‡é«˜ã€‚ 示例 2:: int 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; } 如果 ``a`` å·²åˆå§‹åŒ–而 ``b`` 未åˆå§‹åŒ–,则结果的阴影为 0xffff0000,结果的æºç‚¹ä¸º ``b`` çš„æºç‚¹ã€‚ ``ret.s[0]`` 会有相åŒçš„èµ·æºï¼Œä½†å®ƒä¸ä¼šè¢«ä½¿ç”¨ï¼Œå› 为该å˜é‡å·²åˆå§‹åŒ–。 如果两个函数å‚数都未åˆå§‹åŒ–,则åªä¿ç•™ç¬¬äºŒä¸ªå‚æ•°çš„æºç‚¹ã€‚ æºç‚¹é“¾ ~~~~~~ 为了便于调试,KMSAN 在æ¯æ¬¡å°†æœªåˆå§‹åŒ–值å˜å‚¨åˆ°å†…å˜æ—¶éƒ½ä¼šåˆ›å»ºä¸€ä¸ªæ–°çš„æºç‚¹ã€‚æ–°çš„æºç‚¹ å¼•ç”¨äº†å…¶åˆ›å»ºæ ˆä»¥åŠå€¼çš„å‰ä¸€ä¸ªèµ·æºã€‚è¿™å¯èƒ½å¯¼è‡´å†…å˜æ¶ˆè€—å¢žåŠ ï¼Œå› æ¤æˆ‘们在è¿è¡Œæ—¶é™åˆ¶ 了æºç‚¹é“¾çš„长度。 Clang æ’æ¡© API -------------- Clang æ’æ¡©é€šè¿‡åœ¨å†…æ ¸ä»£ç ä¸æ’入定义在 ``mm/kmsan/instrumentation.c`` ä¸çš„函数调用 æ¥å®žçŽ°ã€‚ 阴影æ“作 ~~~~~~~~ 对于æ¯æ¬¡å†…å˜è®¿é—®ï¼Œç¼–译器都会å‘出一个函数调用,该函数返回一对指针,指å‘ç»™å®šå†…å˜ çš„é˜´å½±å’ŒåŽŸå§‹åœ°å€:: typedef 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) 函数åä¾èµ–于内å˜è®¿é—®çš„大å°ã€‚ 编译器确ä¿å¯¹äºŽæ¯ä¸ªåŠ 载的值,其阴影和原始值都从内å˜ä¸è¯»å–。当一个值å˜å‚¨åˆ°å†…å˜æ—¶ ,其阴影和原始值也会通过元数æ®æŒ‡é’ˆè¿›è¡Œå˜å‚¨ã€‚ 处ç†å±€éƒ¨å˜é‡ ~~~~~~~~~~~~ 一个特殊的函数用于为局部å˜é‡åˆ›å»ºä¸€ä¸ªæ–°çš„原始值,并将该å˜é‡çš„原始值设置为该值:: void __msan_poison_alloca(void *addr, uintptr_t size, char *descr) 访问æ¯ä¸ªä»»åŠ¡æ•°æ® ~~~~~~~~~~~~~~~~ 在æ¯ä¸ªæ’桩函数的开始处,KMSAN æ’入一个对 ``__msan_get_context_state()`` 的调用 :: kmsan_context_state *__msan_get_context_state(void) ``kmsan_context_state`` 在 ``include/linux/kmsan.h`` ä¸å£°æ˜Ž:: 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; }; KMSAN 使用æ¤ç»“构体在æ’æ¡©å‡½æ•°ä¹‹é—´ä¼ é€’å‚数阴影和原始值(除éžç«‹åˆ»é€šè¿‡ ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 检查å‚数)。 将未åˆå§‹åŒ–çš„å€¼ä¼ é€’ç»™å‡½æ•° ~~~~~~~~~~~~~~~~~~~~~~~~ Clang çš„ MemorySanitizer æ’桩有一个选项 ``-fsanitize-memory-param-retval``,该 é€‰é¡¹ä½¿ç¼–è¯‘å™¨æ£€æŸ¥æŒ‰å€¼ä¼ é€’çš„å‡½æ•°å‚数,以åŠå‡½æ•°è¿”回值。 该选项由 ``CONFIG_KMSAN_CHECK_PARAM_RETVAL`` 控制,默认å¯ç”¨ä»¥ä¾¿ KMSAN 更早报告 未åˆå§‹åŒ–的值。有关更多细节,请å‚考 `LKML discussion`_。 由于 LLVM ä¸çš„实现检查的方å¼ï¼ˆå®ƒä»¬ä»…åº”ç”¨äºŽæ ‡è®°ä¸º ``noundef`` çš„å‚数),并ä¸æ˜¯æ‰€ 有å‚数都能ä¿è¯è¢«æ£€æŸ¥ï¼Œå› æ¤æˆ‘们ä¸èƒ½æ”¾å¼ƒ ``kmsan_context_state`` ä¸çš„元数æ®å˜å‚¨ 。 å—符串函数 ~~~~~~~~~~~ 编译器将对 ``memcpy()``/``memmove()``/``memset()`` 的调用替æ¢ä¸ºä»¥ä¸‹å‡½æ•°ã€‚这些函 数在数æ®ç»“æž„åˆå§‹åŒ–或å¤åˆ¶æ—¶ä¹Ÿä¼šè¢«è°ƒç”¨ï¼Œç¡®ä¿é˜´å½±å’ŒåŽŸå§‹å€¼ä¸Žæ•°æ®ä¸€èµ·å¤åˆ¶:: void *__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) 错误报告 ~~~~~~~~ 对于æ¯ä¸ªå€¼çš„使用,编译器å‘出一个阴影检查,在值ä¸æ¯’的情况下调用 ``__msan_warning()``:: void __msan_warning(u32 origin) ``__msan_warning()`` 使 KMSAN è¿è¡Œæ—¶æ‰“å°é”™è¯¯æŠ¥å‘Šã€‚ 内è”汇编æ’æ¡© ~~~~~~~~~~~~ KMSAN 对æ¯ä¸ªå†…è”汇编输出进行æ’桩,调用:: void __msan_instrument_asm_store(void *addr, uintptr_t size) ,该函数解除内å˜åŒºåŸŸçš„污染。 è¿™ç§æ–¹æ³•å¯èƒ½ä¼šæŽ©ç›–æŸäº›é”™è¯¯ï¼Œä½†ä¹Ÿæœ‰åŠ©äºŽé¿å…许多ä½æ“作ã€åŽŸåæ“作ç‰ä¸çš„å‡é˜³æ€§ã€‚ æœ‰æ—¶ä¼ é€’ç»™å†…è”汇编的指针ä¸æŒ‡å‘有效内å˜ã€‚在这ç§æƒ…况下,它们在è¿è¡Œæ—¶è¢«å¿½ç•¥ã€‚ è¿è¡Œæ—¶åº“ -------- 代ç ä½äºŽ ``mm/kmsan/``。 æ¯ä¸ªä»»åŠ¡ KMSAN çŠ¶æ€ ~~~~~~~~~~~~~~~~~~~ æ¯ä¸ª task_struct 都有一个关è”çš„ KMSAN 任务状æ€ï¼Œå®ƒä¿å˜ KMSAN 上下文(è§ä¸Šæ–‡ï¼‰å’Œä¸€ä¸ªæ¯ä¸ªä»»åŠ¡è®¡æ•°å™¨ä»¥ç¦æ¢ KMSAN 报告:: struct kmsan_context { ... unsigned int depth; struct kmsan_context_state cstate; ... } struct task_struct { ... struct kmsan_context kmsan; ... } KMSAN 上下文 ~~~~~~~~~~~~ åœ¨å†…æ ¸ä»»åŠ¡ä¸Šä¸‹æ–‡ä¸è¿è¡Œæ—¶ï¼ŒKMSAN 使用 ``current->kmsan.cstate`` æ¥ ä¿å˜å‡½æ•°å‚数和返回值的元数æ®ã€‚ ä½†åœ¨å†…æ ¸è¿è¡ŒäºŽä¸æ–ã€softirq 或 NMI 上下文ä¸ï¼Œ ``current`` ä¸å¯ç”¨æ—¶ï¼Œ KMSAN 切æ¢åˆ°æ¯ CPU ä¸æ–状æ€:: DEFINE_PER_CPU(struct kmsan_ctx, kmsan_percpu_ctx); 元数æ®åˆ†é… ~~~~~~~~~~ å†…æ ¸ä¸æœ‰å¤šä¸ªåœ°æ–¹å˜å‚¨å…ƒæ•°æ®ã€‚ 1. æ¯ä¸ª ``struct page`` 实例包å«ä¸¤ä¸ªæŒ‡å‘其影å和内å˜é¡µé¢çš„指针 :: struct page { ... struct page *shadow, *origin; ... }; 在å¯åŠ¨æ—¶ï¼Œå†…æ ¸ä¸ºæ¯ä¸ªå¯ç”¨çš„å†…æ ¸é¡µé¢åˆ†é…å½±åå’Œæºé¡µé¢ã€‚è¿™æ˜¯åœ¨å†…æ ¸åœ°å€ç©ºé—´å·²ç»ç¢Žç‰‡ 化时åŽå®Œæˆçš„,完æˆçš„ç›¸å½“æ™šï¼Œå› æ¤æ™®é€šæ•°æ®é¡µé¢å¯èƒ½ä¸Žå…ƒæ•°æ®é¡µé¢ä»»æ„交错。 è¿™æ„味ç€é€šå¸¸ä¸¤ä¸ªç›¸é‚»çš„内å˜é¡µé¢ï¼Œå®ƒä»¬çš„å½±å/æºé¡µé¢å¯èƒ½ä¸æ˜¯è¿žç»çš„ã€‚å› æ¤ï¼Œå¦‚æžœå†…å˜ è®¿é—®è·¨è¶Šå†…å˜å—的边界,访问影å/æºå†…å˜å¯èƒ½ä¼šç ´å其他页é¢æˆ–从ä¸è¯»å–错误的值。 å®žé™…ä¸Šï¼Œç”±ç›¸åŒ ``alloc_pages()`` 调用返回的连ç»å†…å˜é¡µé¢å°†å…·æœ‰è¿žç»çš„元数æ®ï¼Œè€Œ 如果这些页é¢å±žäºŽä¸¤ä¸ªä¸åŒçš„分é…,它们的元数æ®é¡µé¢å¯èƒ½ä¼šè¢«ç¢Žç‰‡åŒ–。 å¯¹äºŽå†…æ ¸æ•°æ®ï¼ˆ ``.data``〠``.bss`` ç‰ï¼‰å’Œæ¯ CPU 内å˜åŒºåŸŸï¼Œä¹Ÿæ²¡æœ‰å¯¹å…ƒæ•°æ®è¿žç» 性的ä¿è¯ã€‚ 在 ``__msan_metadata_ptr_for_XXX_YYY()`` é‡åˆ°ä¸¤ä¸ªé¡µé¢ä¹‹é—´çš„ éžè¿žç»å…ƒæ•°æ®è¾¹ç•Œæ—¶ï¼Œå®ƒè¿”回指å‘å‡å½±å/æºåŒºåŸŸçš„指针:: char dummy_load_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); char dummy_store_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); ``dummy_load_page`` 被åˆå§‹åŒ–ä¸ºé›¶ï¼Œå› æ¤è¯»å–它始终返回零。对 ``dummy_store_page`` çš„ 所有写入都被忽略。 2. 对于 vmalloc 内å˜å’Œæ¨¡å—,内å˜èŒƒå›´ã€å½±åå’Œæºä¹‹é—´æœ‰ä¸€ä¸ªç›´æŽ¥æ˜ 射。KMSAN å°† vmalloc 区域缩å°äº† 3/4,仅使å‰å››åˆ†ä¹‹ä¸€å¯ç”¨äºŽ ``vmalloc()``。vmalloc 区域的第二个四分之一包å«ç¬¬ä¸€ä¸ªå››åˆ†ä¹‹ä¸€çš„å½±å内å˜ï¼Œç¬¬ä¸‰ä¸ªå››åˆ†ä¹‹ä¸€ä¿å˜æºã€‚第四个 四分之一的å°éƒ¨åˆ†åŒ…å«å†…æ ¸æ¨¡å—çš„å½±åå’Œæºã€‚有关更多详细信æ¯ï¼Œè¯·å‚阅 ``arch/x86/include/asm/pgtable_64_types.h``。 当一系列页é¢æ˜ 射到一个连ç»çš„虚拟内å˜ç©ºé—´æ—¶ï¼Œå®ƒä»¬çš„å½±åå’Œæºé¡µé¢ä¹Ÿä»¥è¿žç»åŒºåŸŸçš„æ–¹ å¼æ˜ 射。 å‚考文献 ======== E. Stepanov, K. Serebryany. `MemorySanitizer: fast detector of uninitialized memory use in C++ <https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43308.pdf>`_. In Proceedings of CGO 2015. .. _MemorySanitizer tool: https://clang.llvm.org/docs/MemorySanitizer.html .. _LLVM documentation: https://llvm.org/docs/GettingStarted.html .. _LKML discussion: https://lore.kernel.org/all/20220614144853.3693273-1-glider@google.com/