| From: Alexander Potapenko <glider@google.com> |
| Subject: kmsan: another take at fixing memcpy tests |
| Date: Fri, 3 Mar 2023 15:14:31 +0100 |
| |
| commit 5478afc55a21 ("kmsan: fix memcpy tests") uses OPTIMIZER_HIDE_VAR() |
| to hide the uninitialized var from the compiler optimizations. |
| |
| However OPTIMIZER_HIDE_VAR(uninit) enforces an immediate check of @uninit, |
| so memcpy tests did not actually check the behavior of memcpy(), because |
| they always contained a KMSAN report. |
| |
| Replace OPTIMIZER_HIDE_VAR() with a file-local macro that just clobbers |
| the memory with a barrier(), and add a test case for memcpy() that does |
| not expect an error report. |
| |
| Also reflow kmsan_test.c with clang-format. |
| |
| Link: https://lkml.kernel.org/r/20230303141433.3422671-2-glider@google.com |
| Signed-off-by: Alexander Potapenko <glider@google.com> |
| Reviewed-by: Marco Elver <elver@google.com> |
| Cc: Daniel Vetter <daniel@ffwll.ch> |
| Cc: Geert Uytterhoeven <geert@linux-m68k.org> |
| Cc: Helge Deller <deller@gmx.de> |
| Cc: Kees Cook <keescook@chromium.org> |
| Cc: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| mm/kmsan/kmsan_test.c | 44 ++++++++++++++++++++++++++++++++++------ |
| 1 file changed, 38 insertions(+), 6 deletions(-) |
| |
| --- a/mm/kmsan/kmsan_test.c~kmsan-another-take-at-fixing-memcpy-tests |
| +++ a/mm/kmsan/kmsan_test.c |
| @@ -408,6 +408,37 @@ static void test_printk(struct kunit *te |
| } |
| |
| /* |
| + * Prevent the compiler from optimizing @var away. Without this, Clang may |
| + * notice that @var is uninitialized and drop memcpy() calls that use it. |
| + * |
| + * There is OPTIMIZER_HIDE_VAR() in linux/compier.h that we cannot use here, |
| + * because it is implemented as inline assembly receiving @var as a parameter |
| + * and will enforce a KMSAN check. Same is true for e.g. barrier_data(var). |
| + */ |
| +#define DO_NOT_OPTIMIZE(var) barrier() |
| + |
| +/* |
| + * Test case: ensure that memcpy() correctly copies initialized values. |
| + * Also serves as a regression test to ensure DO_NOT_OPTIMIZE() does not cause |
| + * extra checks. |
| + */ |
| +static void test_init_memcpy(struct kunit *test) |
| +{ |
| + EXPECTATION_NO_REPORT(expect); |
| + volatile int src; |
| + volatile int dst = 0; |
| + |
| + DO_NOT_OPTIMIZE(src); |
| + src = 1; |
| + kunit_info( |
| + test, |
| + "memcpy()ing aligned initialized src to aligned dst (no reports)\n"); |
| + memcpy((void *)&dst, (void *)&src, sizeof(src)); |
| + kmsan_check_memory((void *)&dst, sizeof(dst)); |
| + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); |
| +} |
| + |
| +/* |
| * Test case: ensure that memcpy() correctly copies uninitialized values between |
| * aligned `src` and `dst`. |
| */ |
| @@ -420,7 +451,7 @@ static void test_memcpy_aligned_to_align |
| kunit_info( |
| test, |
| "memcpy()ing aligned uninit src to aligned dst (UMR report)\n"); |
| - OPTIMIZER_HIDE_VAR(uninit_src); |
| + DO_NOT_OPTIMIZE(uninit_src); |
| memcpy((void *)&dst, (void *)&uninit_src, sizeof(uninit_src)); |
| kmsan_check_memory((void *)&dst, sizeof(dst)); |
| KUNIT_EXPECT_TRUE(test, report_matches(&expect)); |
| @@ -443,7 +474,7 @@ static void test_memcpy_aligned_to_unali |
| kunit_info( |
| test, |
| "memcpy()ing aligned uninit src to unaligned dst (UMR report)\n"); |
| - OPTIMIZER_HIDE_VAR(uninit_src); |
| + DO_NOT_OPTIMIZE(uninit_src); |
| memcpy((void *)&dst[1], (void *)&uninit_src, sizeof(uninit_src)); |
| kmsan_check_memory((void *)dst, 4); |
| KUNIT_EXPECT_TRUE(test, report_matches(&expect)); |
| @@ -467,13 +498,14 @@ static void test_memcpy_aligned_to_unali |
| kunit_info( |
| test, |
| "memcpy()ing aligned uninit src to unaligned dst - part 2 (UMR report)\n"); |
| - OPTIMIZER_HIDE_VAR(uninit_src); |
| + DO_NOT_OPTIMIZE(uninit_src); |
| memcpy((void *)&dst[1], (void *)&uninit_src, sizeof(uninit_src)); |
| kmsan_check_memory((void *)&dst[4], sizeof(uninit_src)); |
| KUNIT_EXPECT_TRUE(test, report_matches(&expect)); |
| } |
| |
| -static noinline void fibonacci(int *array, int size, int start) { |
| +static noinline void fibonacci(int *array, int size, int start) |
| +{ |
| if (start < 2 || (start == size)) |
| return; |
| array[start] = array[start - 1] + array[start - 2]; |
| @@ -482,8 +514,7 @@ static noinline void fibonacci(int *arra |
| |
| static void test_long_origin_chain(struct kunit *test) |
| { |
| - EXPECTATION_UNINIT_VALUE_FN(expect, |
| - "test_long_origin_chain"); |
| + EXPECTATION_UNINIT_VALUE_FN(expect, "test_long_origin_chain"); |
| /* (KMSAN_MAX_ORIGIN_DEPTH * 2) recursive calls to fibonacci(). */ |
| volatile int accum[KMSAN_MAX_ORIGIN_DEPTH * 2 + 2]; |
| int last = ARRAY_SIZE(accum) - 1; |
| @@ -515,6 +546,7 @@ static struct kunit_case kmsan_test_case |
| KUNIT_CASE(test_uaf), |
| KUNIT_CASE(test_percpu_propagate), |
| KUNIT_CASE(test_printk), |
| + KUNIT_CASE(test_init_memcpy), |
| KUNIT_CASE(test_memcpy_aligned_to_aligned), |
| KUNIT_CASE(test_memcpy_aligned_to_unaligned), |
| KUNIT_CASE(test_memcpy_aligned_to_unaligned2), |
| _ |