diff --git a/kernel/kcov.c b/kernel/kcov.c index 54eaae97bfd6..9402227bd7ba 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -198,6 +198,16 @@ static notrace unsigned long canonicalize_ip(unsigned long ip) return ip; } +#ifdef CONFIG_KCOV_SELFTEST +static unsigned long __init decanonicalize_ip(unsigned long ip) +{ +#ifdef CONFIG_RANDOMIZE_BASE + ip += kaslr_offset(); +#endif + return ip; +} +#endif + /* * Entry point from instrumented code. * This is called once per basic-block/edge. @@ -1107,9 +1117,11 @@ struct kcov_common_handle_id kcov_common_handle(void) EXPORT_SYMBOL(kcov_common_handle); #ifdef CONFIG_KCOV_SELFTEST +static unsigned long selftest_area[16] __initdata; + static void __init selftest(void) { - unsigned long start; + unsigned long start, i, ip, count; pr_err("running self test\n"); /* @@ -1119,15 +1131,26 @@ static void __init selftest(void) * leaks out of that section and leads to spurious coverage. * It's hard to call the actual interrupt handler directly, * so we just loop here for a bit waiting for a timer interrupt. - * We set kcov_mode to enable tracing, but don't setup the area, - * so any attempt to trace will crash. Note: we must not call any + * A small coverage area records the PCs of any leaks. The first word + * holds the count, leaving room for 15 PCs. Note: we must not call any * potentially traced functions in this region. */ + kcov_start(current, NULL, ARRAY_SIZE(selftest_area), + selftest_area, KCOV_MODE_TRACE_PC, 0); start = jiffies; - WRITE_ONCE(current->kcov_mode, KCOV_MODE_TRACE_PC); while ((jiffies - start) * MSEC_PER_SEC / HZ < 300) - ; - WRITE_ONCE(current->kcov_mode, 0); + cpu_relax(); + kcov_stop(current); + + count = READ_ONCE(selftest_area[0]); + if (count) { + pr_err("spurious coverage detected during interrupt selftest:\n"); + for (i = 1; i <= count; i++) { + ip = decanonicalize_ip(selftest_area[i]); + pr_err(" %pB\n", (void *)ip); + } + panic("kcov: interrupt selftest detected spurious coverage"); + } pr_err("done running self test\n"); } #endif