Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

kmsan: fix out-of-bounds access to shadow memory

Running sha224_kunit on a KMSAN-enabled kernel results in a crash in
kmsan_internal_set_shadow_origin():

BUG: unable to handle page fault for address: ffffbc3840291000
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
PGD 1810067 P4D 1810067 PUD 192d067 PMD 3c17067 PTE 0
Oops: 0000 [#1] SMP NOPTI
CPU: 0 UID: 0 PID: 81 Comm: kunit_try_catch Tainted: G N 6.17.0-rc3 #10 PREEMPT(voluntary)
Tainted: [N]=TEST
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014
RIP: 0010:kmsan_internal_set_shadow_origin+0x91/0x100
[...]
Call Trace:
<TASK>
__msan_memset+0xee/0x1a0
sha224_final+0x9e/0x350
test_hash_buffer_overruns+0x46f/0x5f0
? kmsan_get_shadow_origin_ptr+0x46/0xa0
? __pfx_test_hash_buffer_overruns+0x10/0x10
kunit_try_run_case+0x198/0xa00

This occurs when memset() is called on a buffer that is not 4-byte aligned
and extends to the end of a guard page, i.e. the next page is unmapped.

The bug is that the loop at the end of kmsan_internal_set_shadow_origin()
accesses the wrong shadow memory bytes when the address is not 4-byte
aligned. Since each 4 bytes are associated with an origin, it rounds the
address and size so that it can access all the origins that contain the
buffer. However, when it checks the corresponding shadow bytes for a
particular origin, it incorrectly uses the original unrounded shadow
address. This results in reads from shadow memory beyond the end of the
buffer's shadow memory, which crashes when that memory is not mapped.

To fix this, correctly align the shadow address before accessing the 4
shadow bytes corresponding to each origin.

Link: https://lkml.kernel.org/r/20250911195858.394235-1-ebiggers@kernel.org
Fixes: 2ef3cec44c60 ("kmsan: do not wipe out origin when doing partial unpoisoning")
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Tested-by: Alexander Potapenko <glider@google.com>
Reviewed-by: Alexander Potapenko <glider@google.com>
Cc: Dmitriy Vyukov <dvyukov@google.com>
Cc: Marco Elver <elver@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Eric Biggers and committed by
Andrew Morton
85e1ff61 14967a9c

+23 -3
+7 -3
mm/kmsan/core.c
··· 195 195 u32 origin, bool checked) 196 196 { 197 197 u64 address = (u64)addr; 198 - u32 *shadow_start, *origin_start; 198 + void *shadow_start; 199 + u32 *aligned_shadow, *origin_start; 199 200 size_t pad = 0; 200 201 201 202 KMSAN_WARN_ON(!kmsan_metadata_is_contiguous(addr, size)); ··· 215 214 } 216 215 __memset(shadow_start, b, size); 217 216 218 - if (!IS_ALIGNED(address, KMSAN_ORIGIN_SIZE)) { 217 + if (IS_ALIGNED(address, KMSAN_ORIGIN_SIZE)) { 218 + aligned_shadow = shadow_start; 219 + } else { 219 220 pad = address % KMSAN_ORIGIN_SIZE; 220 221 address -= pad; 222 + aligned_shadow = shadow_start - pad; 221 223 size += pad; 222 224 } 223 225 size = ALIGN(size, KMSAN_ORIGIN_SIZE); ··· 234 230 * corresponding shadow slot is zero. 235 231 */ 236 232 for (int i = 0; i < size / KMSAN_ORIGIN_SIZE; i++) { 237 - if (origin || !shadow_start[i]) 233 + if (origin || !aligned_shadow[i]) 238 234 origin_start[i] = origin; 239 235 } 240 236 }
+16
mm/kmsan/kmsan_test.c
··· 556 556 DEFINE_TEST_MEMSETXX(32) 557 557 DEFINE_TEST_MEMSETXX(64) 558 558 559 + /* Test case: ensure that KMSAN does not access shadow memory out of bounds. */ 560 + static void test_memset_on_guarded_buffer(struct kunit *test) 561 + { 562 + void *buf = vmalloc(PAGE_SIZE); 563 + 564 + kunit_info(test, 565 + "memset() on ends of guarded buffer should not crash\n"); 566 + 567 + for (size_t size = 0; size <= 128; size++) { 568 + memset(buf, 0xff, size); 569 + memset(buf + PAGE_SIZE - size, 0xff, size); 570 + } 571 + vfree(buf); 572 + } 573 + 559 574 static noinline void fibonacci(int *array, int size, int start) 560 575 { 561 576 if (start < 2 || (start == size)) ··· 692 677 KUNIT_CASE(test_memset16), 693 678 KUNIT_CASE(test_memset32), 694 679 KUNIT_CASE(test_memset64), 680 + KUNIT_CASE(test_memset_on_guarded_buffer), 695 681 KUNIT_CASE(test_long_origin_chain), 696 682 KUNIT_CASE(test_stackdepot_roundtrip), 697 683 KUNIT_CASE(test_unpoison_memory),