From: Andrey Konovalov <andreyknvl@xxxxxxxxxx> Subject: kasan: respect KASAN_BIT_REPORTED in all reporting routines Currently, only kasan_report() checks the KASAN_BIT_REPORTED and KASAN_BIT_MULTI_SHOT flags. Make other reporting routines check these flags as well. Also add explanatory comments. Note that the current->kasan_depth check is split out into report_suppressed() and only called for kasan_report(). Link: https://lkml.kernel.org/r/715e346b10b398e29ba1b425299dcd79e29d58ce.1646237226.git.andreyknvl@xxxxxxxxxx Signed-off-by: Andrey Konovalov <andreyknvl@xxxxxxxxxx> Cc: Alexander Potapenko <glider@xxxxxxxxxx> Cc: Andrey Ryabinin <ryabinin.a.a@xxxxxxxxx> Cc: Dmitry Vyukov <dvyukov@xxxxxxxxxx> Cc: Marco Elver <elver@xxxxxxxxxx> Signed-off-by: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> --- mm/kasan/report.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) --- a/mm/kasan/report.c~kasan-respect-kasan_bit_reported-in-all-reporting-routines +++ a/mm/kasan/report.c @@ -381,12 +381,26 @@ static void print_memory_metadata(const } } -static bool report_enabled(void) +/* + * Used to suppress reports within kasan_disable/enable_current() critical + * sections, which are used for marking accesses to slab metadata. + */ +static bool report_suppressed(void) { #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) if (current->kasan_depth) - return false; + return true; #endif + return false; +} + +/* + * Used to avoid reporting more than one KASAN bug unless kasan_multi_shot + * is enabled. Note that KASAN tests effectively enable kasan_multi_shot + * for their duration. + */ +static bool report_enabled(void) +{ if (test_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags)) return true; return !test_and_set_bit(KASAN_BIT_REPORTED, &kasan_flags); @@ -416,6 +430,14 @@ void kasan_report_invalid_free(void *ptr unsigned long flags; struct kasan_report_info info; + /* + * Do not check report_suppressed(), as an invalid-free cannot be + * caused by accessing slab metadata and thus should not be + * suppressed by kasan_disable/enable_current() critical sections. + */ + if (unlikely(!report_enabled())) + return; + start_report(&flags, true); info.type = KASAN_REPORT_INVALID_FREE; @@ -444,7 +466,7 @@ bool kasan_report(unsigned long addr, si unsigned long irq_flags; struct kasan_report_info info; - if (unlikely(!report_enabled())) { + if (unlikely(report_suppressed()) || unlikely(!report_enabled())) { ret = false; goto out; } @@ -473,6 +495,13 @@ void kasan_report_async(void) { unsigned long flags; + /* + * Do not check report_suppressed(), as kasan_disable/enable_current() + * critical sections do not affect Hardware Tag-Based KASAN. + */ + if (unlikely(!report_enabled())) + return; + start_report(&flags, false); pr_err("BUG: KASAN: invalid-access\n"); pr_err("Asynchronous fault: no details available\n"); _