Message ID | 20230301143933.2374658-2-glider@google.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | [1/4] x86: kmsan: Don't rename memintrinsics in uninstrumented files | expand |
On Wed, 1 Mar 2023 at 15:39, 'Alexander Potapenko' via kasan-dev <kasan-dev@googlegroups.com> wrote: > > 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 asm macro that just > clobbers the memory, and add a test case for memcpy() that does not > expect an error report. > > Also reflow kmsan_test.c with clang-format. > > Signed-off-by: Alexander Potapenko <glider@google.com> > --- > mm/kmsan/kmsan_test.c | 43 +++++++++++++++++++++++++++++++++++++------ > 1 file changed, 37 insertions(+), 6 deletions(-) > > diff --git a/mm/kmsan/kmsan_test.c b/mm/kmsan/kmsan_test.c > index 088e21a48dc4b..cc98a3f4e0899 100644 > --- a/mm/kmsan/kmsan_test.c > +++ b/mm/kmsan/kmsan_test.c > @@ -407,6 +407,36 @@ static void test_printk(struct kunit *test) > KUNIT_EXPECT_TRUE(test, report_matches(&expect)); > } > > +/* > + * 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. > + */ > +#define DO_NOT_OPTIMIZE(var) asm("" ::: "memory") That's just a normal "barrier()" - use that instead? > +/* > + * Test case: ensure that memcpy() correctly copies initialized values. > + */ > +static void test_init_memcpy(struct kunit *test) > +{ > + EXPECTATION_NO_REPORT(expect); > + volatile int src; > + volatile int dst = 0; > + > + // Ensure DO_NOT_OPTIMIZE() does not cause extra checks. ^^ this comment seems redundant now, given DO_NOT_OPTIMIZE() has a comment (it's also using //-style comment). > + 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 +450,7 @@ static void test_memcpy_aligned_to_aligned(struct kunit *test) > 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 +473,7 @@ static void test_memcpy_aligned_to_unaligned(struct kunit *test) > 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 +497,14 @@ static void test_memcpy_aligned_to_unaligned2(struct kunit *test) > 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 +513,7 @@ static noinline void fibonacci(int *array, int size, int start) { > > 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 +545,7 @@ static struct kunit_case kmsan_test_cases[] = { > 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), > -- > 2.39.2.722.g9855ee24e9-goog > > -- > You received this message because you are subscribed to the Google Groups "kasan-dev" group. > To unsubscribe from this group and stop receiving emails from it, send an email to kasan-dev+unsubscribe@googlegroups.com. > To view this discussion on the web visit https://groups.google.com/d/msgid/kasan-dev/20230301143933.2374658-2-glider%40google.com.
> > +#define DO_NOT_OPTIMIZE(var) asm("" ::: "memory") > > That's just a normal "barrier()" - use that instead? Ok, will do (I still think I'd better hide it behind a macro so that we can change the implementation of DO_NOT_OPTIMIZE in the future if the compiler starts outsmarting us again. > > +/* > > + * Test case: ensure that memcpy() correctly copies initialized values. > > + */ > > +static void test_init_memcpy(struct kunit *test) > > +{ > > + EXPECTATION_NO_REPORT(expect); > > + volatile int src; > > + volatile int dst = 0; > > + > > + // Ensure DO_NOT_OPTIMIZE() does not cause extra checks. > > ^^ this comment seems redundant now, given DO_NOT_OPTIMIZE() has a > comment (it's also using //-style comment). Moved it to the test description: /* * 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. */ I think it's still relevant here.
diff --git a/mm/kmsan/kmsan_test.c b/mm/kmsan/kmsan_test.c index 088e21a48dc4b..cc98a3f4e0899 100644 --- a/mm/kmsan/kmsan_test.c +++ b/mm/kmsan/kmsan_test.c @@ -407,6 +407,36 @@ static void test_printk(struct kunit *test) KUNIT_EXPECT_TRUE(test, report_matches(&expect)); } +/* + * 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. + */ +#define DO_NOT_OPTIMIZE(var) asm("" ::: "memory") + +/* + * Test case: ensure that memcpy() correctly copies initialized values. + */ +static void test_init_memcpy(struct kunit *test) +{ + EXPECTATION_NO_REPORT(expect); + volatile int src; + volatile int dst = 0; + + // Ensure DO_NOT_OPTIMIZE() does not cause extra checks. + 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 +450,7 @@ static void test_memcpy_aligned_to_aligned(struct kunit *test) 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 +473,7 @@ static void test_memcpy_aligned_to_unaligned(struct kunit *test) 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 +497,14 @@ static void test_memcpy_aligned_to_unaligned2(struct kunit *test) 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 +513,7 @@ static noinline void fibonacci(int *array, int size, int start) { 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 +545,7 @@ static struct kunit_case kmsan_test_cases[] = { 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),
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 asm macro that just clobbers the memory, and add a test case for memcpy() that does not expect an error report. Also reflow kmsan_test.c with clang-format. Signed-off-by: Alexander Potapenko <glider@google.com> --- mm/kmsan/kmsan_test.c | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-)