Skip to content

Commit

Permalink
kasan: add support for kasan.fault=panic_on_write
Browse files Browse the repository at this point in the history
KASAN's boot time kernel parameter 'kasan.fault=' currently supports
'report' and 'panic', which results in either only reporting bugs or also
panicking on reports.

However, some users may wish to have more control over when KASAN reports
result in a kernel panic: in particular, KASAN reported invalid _writes_
are of special interest, because they have greater potential to corrupt
random kernel memory or be more easily exploited.

To panic on invalid writes only, introduce 'kasan.fault=panic_on_write',
which allows users to choose to continue running on invalid reads, but
panic only on invalid writes.

Link: https://lkml.kernel.org/r/20230614095158.1133673-1-elver@google.com
Signed-off-by: Marco Elver <elver@google.com>
Reviewed-by: Alexander Potapenko <glider@google.com>
Cc: Aleksandr Nogikh <nogikh@google.com>
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Taras Madan <tarasmadan@google.com>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
  • Loading branch information
Marco Elver authored and Andrew Morton committed Jun 19, 2023
1 parent cb0551a commit 452c03f
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 8 deletions.
7 changes: 4 additions & 3 deletions Documentation/dev-tools/kasan.rst
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,10 @@ effectively disables ``panic_on_warn`` for KASAN reports.
Alternatively, independent of ``panic_on_warn``, the ``kasan.fault=`` boot
parameter can be used to control panic and reporting behaviour:

- ``kasan.fault=report`` or ``=panic`` controls whether to only print a KASAN
report or also panic the kernel (default: ``report``). The panic happens even
if ``kasan_multi_shot`` is enabled.
- ``kasan.fault=report``, ``=panic``, or ``=panic_on_write`` controls whether
to only print a KASAN report, panic the kernel, or panic the kernel on
invalid writes only (default: ``report``). The panic happens even if
``kasan_multi_shot`` is enabled.

Software and Hardware Tag-Based KASAN modes (see the section about various
modes below) support altering stack trace collection behavior:
Expand Down
31 changes: 26 additions & 5 deletions mm/kasan/report.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum kasan_arg_fault {
KASAN_ARG_FAULT_DEFAULT,
KASAN_ARG_FAULT_REPORT,
KASAN_ARG_FAULT_PANIC,
KASAN_ARG_FAULT_PANIC_ON_WRITE,
};

static enum kasan_arg_fault kasan_arg_fault __ro_after_init = KASAN_ARG_FAULT_DEFAULT;
Expand All @@ -57,6 +58,8 @@ static int __init early_kasan_fault(char *arg)
kasan_arg_fault = KASAN_ARG_FAULT_REPORT;
else if (!strcmp(arg, "panic"))
kasan_arg_fault = KASAN_ARG_FAULT_PANIC;
else if (!strcmp(arg, "panic_on_write"))
kasan_arg_fault = KASAN_ARG_FAULT_PANIC_ON_WRITE;
else
return -EINVAL;

Expand Down Expand Up @@ -211,7 +214,7 @@ static void start_report(unsigned long *flags, bool sync)
pr_err("==================================================================\n");
}

static void end_report(unsigned long *flags, const void *addr)
static void end_report(unsigned long *flags, const void *addr, bool is_write)
{
if (addr)
trace_error_report_end(ERROR_DETECTOR_KASAN,
Expand All @@ -220,8 +223,18 @@ static void end_report(unsigned long *flags, const void *addr)
spin_unlock_irqrestore(&report_lock, *flags);
if (!test_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags))
check_panic_on_warn("KASAN");
if (kasan_arg_fault == KASAN_ARG_FAULT_PANIC)
switch (kasan_arg_fault) {
case KASAN_ARG_FAULT_DEFAULT:
case KASAN_ARG_FAULT_REPORT:
break;
case KASAN_ARG_FAULT_PANIC:
panic("kasan.fault=panic set ...\n");
break;
case KASAN_ARG_FAULT_PANIC_ON_WRITE:
if (is_write)
panic("kasan.fault=panic_on_write set ...\n");
break;
}
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
lockdep_on();
report_suppress_stop();
Expand Down Expand Up @@ -536,7 +549,11 @@ void kasan_report_invalid_free(void *ptr, unsigned long ip, enum kasan_report_ty

print_report(&info);

end_report(&flags, ptr);
/*
* Invalid free is considered a "write" since the allocator's metadata
* updates involves writes.
*/
end_report(&flags, ptr, true);
}

/*
Expand Down Expand Up @@ -570,7 +587,7 @@ bool kasan_report(const void *addr, size_t size, bool is_write,

print_report(&info);

end_report(&irq_flags, (void *)addr);
end_report(&irq_flags, (void *)addr, is_write);

out:
user_access_restore(ua_flags);
Expand All @@ -596,7 +613,11 @@ void kasan_report_async(void)
pr_err("Asynchronous fault: no details available\n");
pr_err("\n");
dump_stack_lvl(KERN_ERR);
end_report(&flags, NULL);
/*
* Conservatively set is_write=true, because no details are available.
* In this mode, kasan.fault=panic_on_write is like kasan.fault=panic.
*/
end_report(&flags, NULL, true);
}
#endif /* CONFIG_KASAN_HW_TAGS */

Expand Down

0 comments on commit 452c03f

Please sign in to comment.