kasan: detect false-positives in tests
authorAndrey Konovalov <andreyknvl@google.com>
Fri, 30 Apr 2021 06:00:49 +0000 (23:00 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 30 Apr 2021 18:20:42 +0000 (11:20 -0700)
Currently, KASAN-KUnit tests can check that a particular annotated part of
code causes a KASAN report.  However, they do not check that no unwanted
reports happen between the annotated parts.

This patch implements these checks.

It is done by setting report_data.report_found to false in
kasan_test_init() and at the end of KUNIT_EXPECT_KASAN_FAIL() and then
checking that it remains false at the beginning of
KUNIT_EXPECT_KASAN_FAIL() and in kasan_test_exit().

kunit_add_named_resource() call is moved to kasan_test_init(), and the
value of fail_data.report_expected is kept as false in between
KUNIT_EXPECT_KASAN_FAIL() annotations for consistency.

Link: https://lkml.kernel.org/r/48079c52cc329fbc52f4386996598d58022fb872.1617207873.git.andreyknvl@google.com
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Reviewed-by: Marco Elver <elver@google.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
lib/test_kasan.c

index 0882d6c..dc05cfc 100644 (file)
@@ -54,6 +54,10 @@ static int kasan_test_init(struct kunit *test)
 
        multishot = kasan_save_enable_multi_shot();
        kasan_set_tagging_report_once(false);
+       fail_data.report_found = false;
+       fail_data.report_expected = false;
+       kunit_add_named_resource(test, NULL, NULL, &resource,
+                                       "kasan_data", &fail_data);
        return 0;
 }
 
@@ -61,6 +65,7 @@ static void kasan_test_exit(struct kunit *test)
 {
        kasan_set_tagging_report_once(true);
        kasan_restore_multi_shot(multishot);
+       KUNIT_EXPECT_FALSE(test, fail_data.report_found);
 }
 
 /**
@@ -78,33 +83,31 @@ static void kasan_test_exit(struct kunit *test)
  * fields, it can reorder or optimize away the accesses to those fields.
  * Use READ/WRITE_ONCE() for the accesses and compiler barriers around the
  * expression to prevent that.
+ *
+ * In between KUNIT_EXPECT_KASAN_FAIL checks, fail_data.report_found is kept as
+ * false. This allows detecting KASAN reports that happen outside of the checks
+ * by asserting !fail_data.report_found at the start of KUNIT_EXPECT_KASAN_FAIL
+ * and in kasan_test_exit.
  */
-#define KUNIT_EXPECT_KASAN_FAIL(test, expression) do {         \
-       if (IS_ENABLED(CONFIG_KASAN_HW_TAGS) &&                 \
-           !kasan_async_mode_enabled())                        \
-               migrate_disable();                              \
-       WRITE_ONCE(fail_data.report_expected, true);            \
-       WRITE_ONCE(fail_data.report_found, false);              \
-       kunit_add_named_resource(test,                          \
-                               NULL,                           \
-                               NULL,                           \
-                               &resource,                      \
-                               "kasan_data", &fail_data);      \
-       barrier();                                              \
-       expression;                                             \
-       barrier();                                              \
-       if (kasan_async_mode_enabled())                         \
-               kasan_force_async_fault();                      \
-       barrier();                                              \
-       KUNIT_EXPECT_EQ(test,                                   \
-                       READ_ONCE(fail_data.report_expected),   \
-                       READ_ONCE(fail_data.report_found));     \
-       if (IS_ENABLED(CONFIG_KASAN_HW_TAGS) &&                 \
-           !kasan_async_mode_enabled()) {                      \
-               if (READ_ONCE(fail_data.report_found))          \
-                       kasan_enable_tagging_sync();            \
-               migrate_enable();                               \
-       }                                                       \
+#define KUNIT_EXPECT_KASAN_FAIL(test, expression) do {                 \
+       if (IS_ENABLED(CONFIG_KASAN_HW_TAGS) &&                         \
+           !kasan_async_mode_enabled())                                \
+               migrate_disable();                                      \
+       KUNIT_EXPECT_FALSE(test, READ_ONCE(fail_data.report_found));    \
+       WRITE_ONCE(fail_data.report_expected, true);                    \
+       barrier();                                                      \
+       expression;                                                     \
+       barrier();                                                      \
+       KUNIT_EXPECT_EQ(test,                                           \
+                       READ_ONCE(fail_data.report_expected),           \
+                       READ_ONCE(fail_data.report_found));             \
+       if (IS_ENABLED(CONFIG_KASAN_HW_TAGS)) {                         \
+               if (READ_ONCE(fail_data.report_found))                  \
+                       kasan_enable_tagging_sync();                    \
+               migrate_enable();                                       \
+       }                                                               \
+       WRITE_ONCE(fail_data.report_found, false);                      \
+       WRITE_ONCE(fail_data.report_expected, false);                   \
 } while (0)
 
 #define KASAN_TEST_NEEDS_CONFIG_ON(test, config) do {                  \