Updates for KCOV instrumentation on x86:

- Prevent spurious KCOV coverage in common_interrupt()
 
   - Fixup the KCOV Makefile directive which got stale due to a source file
     rename
 
   - Exclude stack unwinding from KCOV as it creates large amounts of
     uninteresting coverage
 
   - Provide a self test to validate that KCOV coverage of the interrupt
     handling code starts not before preempt count got updated.
 -----BEGIN PGP SIGNATURE-----
 
 iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmbpMeITHHRnbHhAbGlu
 dXRyb25peC5kZQAKCRCmGPVMDXSYoaOeD/4oO3g0soK0LIcDIwzaG0ap0hx0nucw
 aVSAESuY+ZaSbRbV0fNoYdHORvLdErs67SeyeJRSxTzSNqGH2dGoFrfbkRSXq951
 RdCSPP60T7xgqAme1YLDiChfXt/gkbWk/8V5Q7sG3oq3GaVcPUyZgPo4M4HQMdfg
 Mla3VPikW5Np3fvs0IZYWQ5VdY0fFOHY5JGMhKJznJxf+Ud+VAtxsbJUcO4MEYWW
 A9CVJNHGEXssGA6vm5kgtLu6n2QFuoSj6En/WqLEaJb8f/V332e04Xj2ZHUaOOjV
 2abVeDovv+dwUYb4SgrGVg9gfEwwcLPDnmOuuQJmQBB5kU4mJsCqI5TTS6c1fgU4
 x8tQsGSOKHFQAI14ZWtitrL4rS2uFcBkAFXo0dF8J5o4989RA8cpfeWVSVUb/UXd
 u38BWpc9iHiihHKMmMQgsa1bUMwdSUTvN5XFHkeP4oqUdMiEiWn8iM5+zXd/lfTs
 9mrTv+kcLA7mjFOmn4JyE2b+NuiPdgS2FCBGLycHvGwvJoJlO2UmSpF89AJ5vdKs
 F8vWLkV+gno/HtwS5o949cAwjYiCodfc7u1W0xj2VDAbx0RbaBw1SDhXMQcLxLgn
 BTt4yHKKIeLX++WH3fpeyL91+UJWubUzNzY4rAmLkz5DedWAkpES+45fatp1buIz
 Lp/hGiIsG9p5xw==
 =tiXT
 -----END PGP SIGNATURE-----

Merge tag 'x86-build-2024-09-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 build updates from Thomas Gleixner:
 "Updates for KCOV instrumentation on x86:

   - Prevent spurious KCOV coverage in common_interrupt()

   - Fixup the KCOV Makefile directive which got stale due to a source
     file rename

   - Exclude stack unwinding from KCOV as it creates large amounts of
     uninteresting coverage

   - Provide a self test to validate that KCOV coverage of the interrupt
     handling code starts not before preempt count got updated"

* tag 'x86-build-2024-09-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86: Ignore stack unwinding in KCOV
  module: Fix KCOV-ignored file name
  kcov: Add interrupt handling self test
  x86/entry: Remove unwanted instrumentation in common_interrupt()
This commit is contained in:
Linus Torvalds 2024-09-17 12:40:34 +02:00
commit 5ba202a7c9
6 changed files with 57 additions and 6 deletions

View file

@ -69,7 +69,11 @@ extern u64 arch_irq_stat(void);
#define local_softirq_pending_ref pcpu_hot.softirq_pending #define local_softirq_pending_ref pcpu_hot.softirq_pending
#if IS_ENABLED(CONFIG_KVM_INTEL) #if IS_ENABLED(CONFIG_KVM_INTEL)
static inline void kvm_set_cpu_l1tf_flush_l1d(void) /*
* This function is called from noinstr interrupt contexts
* and must be inlined to not get instrumentation.
*/
static __always_inline void kvm_set_cpu_l1tf_flush_l1d(void)
{ {
__this_cpu_write(irq_stat.kvm_cpu_l1tf_flush_l1d, 1); __this_cpu_write(irq_stat.kvm_cpu_l1tf_flush_l1d, 1);
} }
@ -84,7 +88,7 @@ static __always_inline bool kvm_get_cpu_l1tf_flush_l1d(void)
return __this_cpu_read(irq_stat.kvm_cpu_l1tf_flush_l1d); return __this_cpu_read(irq_stat.kvm_cpu_l1tf_flush_l1d);
} }
#else /* !IS_ENABLED(CONFIG_KVM_INTEL) */ #else /* !IS_ENABLED(CONFIG_KVM_INTEL) */
static inline void kvm_set_cpu_l1tf_flush_l1d(void) { } static __always_inline void kvm_set_cpu_l1tf_flush_l1d(void) { }
#endif /* IS_ENABLED(CONFIG_KVM_INTEL) */ #endif /* IS_ENABLED(CONFIG_KVM_INTEL) */
#endif /* _ASM_X86_HARDIRQ_H */ #endif /* _ASM_X86_HARDIRQ_H */

View file

@ -212,8 +212,8 @@ __visible noinstr void func(struct pt_regs *regs, \
irqentry_state_t state = irqentry_enter(regs); \ irqentry_state_t state = irqentry_enter(regs); \
u32 vector = (u32)(u8)error_code; \ u32 vector = (u32)(u8)error_code; \
\ \
kvm_set_cpu_l1tf_flush_l1d(); \
instrumentation_begin(); \ instrumentation_begin(); \
kvm_set_cpu_l1tf_flush_l1d(); \
run_irq_on_irqstack_cond(__##func, regs, vector); \ run_irq_on_irqstack_cond(__##func, regs, vector); \
instrumentation_end(); \ instrumentation_end(); \
irqentry_exit(regs, state); \ irqentry_exit(regs, state); \
@ -250,7 +250,6 @@ static void __##func(struct pt_regs *regs); \
\ \
static __always_inline void instr_##func(struct pt_regs *regs) \ static __always_inline void instr_##func(struct pt_regs *regs) \
{ \ { \
kvm_set_cpu_l1tf_flush_l1d(); \
run_sysvec_on_irqstack_cond(__##func, regs); \ run_sysvec_on_irqstack_cond(__##func, regs); \
} \ } \
\ \
@ -258,6 +257,7 @@ __visible noinstr void func(struct pt_regs *regs) \
{ \ { \
irqentry_state_t state = irqentry_enter(regs); \ irqentry_state_t state = irqentry_enter(regs); \
\ \
kvm_set_cpu_l1tf_flush_l1d(); \
instrumentation_begin(); \ instrumentation_begin(); \
instr_##func (regs); \ instr_##func (regs); \
instrumentation_end(); \ instrumentation_end(); \
@ -288,7 +288,6 @@ static __always_inline void __##func(struct pt_regs *regs); \
static __always_inline void instr_##func(struct pt_regs *regs) \ static __always_inline void instr_##func(struct pt_regs *regs) \
{ \ { \
__irq_enter_raw(); \ __irq_enter_raw(); \
kvm_set_cpu_l1tf_flush_l1d(); \
__##func (regs); \ __##func (regs); \
__irq_exit_raw(); \ __irq_exit_raw(); \
} \ } \
@ -297,6 +296,7 @@ __visible noinstr void func(struct pt_regs *regs) \
{ \ { \
irqentry_state_t state = irqentry_enter(regs); \ irqentry_state_t state = irqentry_enter(regs); \
\ \
kvm_set_cpu_l1tf_flush_l1d(); \
instrumentation_begin(); \ instrumentation_begin(); \
instr_##func (regs); \ instr_##func (regs); \
instrumentation_end(); \ instrumentation_end(); \

View file

@ -35,6 +35,14 @@ KMSAN_SANITIZE_nmi.o := n
# If instrumentation of the following files is enabled, boot hangs during # If instrumentation of the following files is enabled, boot hangs during
# first second. # first second.
KCOV_INSTRUMENT_head$(BITS).o := n KCOV_INSTRUMENT_head$(BITS).o := n
# These are called from save_stack_trace() on debug paths,
# and produce large amounts of uninteresting coverage.
KCOV_INSTRUMENT_stacktrace.o := n
KCOV_INSTRUMENT_dumpstack.o := n
KCOV_INSTRUMENT_dumpstack_$(BITS).o := n
KCOV_INSTRUMENT_unwind_orc.o := n
KCOV_INSTRUMENT_unwind_frame.o := n
KCOV_INSTRUMENT_unwind_guess.o := n
CFLAGS_irq.o := -I $(src)/../include/asm/trace CFLAGS_irq.o := -I $(src)/../include/asm/trace

View file

@ -11,6 +11,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/hashtable.h> #include <linux/hashtable.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kmsan-checks.h> #include <linux/kmsan-checks.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/preempt.h> #include <linux/preempt.h>
@ -1067,6 +1068,32 @@ u64 kcov_common_handle(void)
} }
EXPORT_SYMBOL(kcov_common_handle); EXPORT_SYMBOL(kcov_common_handle);
#ifdef CONFIG_KCOV_SELFTEST
static void __init selftest(void)
{
unsigned long start;
pr_err("running self test\n");
/*
* Test that interrupts don't produce spurious coverage.
* The coverage callback filters out interrupt code, but only
* after the handler updates preempt count. Some code periodically
* 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
* potentially traced functions in this region.
*/
start = jiffies;
current->kcov_mode = KCOV_MODE_TRACE_PC;
while ((jiffies - start) * MSEC_PER_SEC / HZ < 300)
;
current->kcov_mode = 0;
pr_err("done running self test\n");
}
#endif
static int __init kcov_init(void) static int __init kcov_init(void)
{ {
int cpu; int cpu;
@ -1086,6 +1113,10 @@ static int __init kcov_init(void)
*/ */
debugfs_create_file_unsafe("kcov", 0600, NULL, NULL, &kcov_fops); debugfs_create_file_unsafe("kcov", 0600, NULL, NULL, &kcov_fops);
#ifdef CONFIG_KCOV_SELFTEST
selftest();
#endif
return 0; return 0;
} }

View file

@ -5,7 +5,7 @@
# These are called from save_stack_trace() on slub debug path, # These are called from save_stack_trace() on slub debug path,
# and produce insane amounts of uninteresting coverage. # and produce insane amounts of uninteresting coverage.
KCOV_INSTRUMENT_module.o := n KCOV_INSTRUMENT_main.o := n
obj-y += main.o obj-y += main.o
obj-y += strict_rwx.o obj-y += strict_rwx.o

View file

@ -2173,6 +2173,14 @@ config KCOV_IRQ_AREA_SIZE
soft interrupts. This specifies the size of those areas in the soft interrupts. This specifies the size of those areas in the
number of unsigned long words. number of unsigned long words.
config KCOV_SELFTEST
bool "Perform short selftests on boot"
depends on KCOV
help
Run short KCOV coverage collection selftests on boot.
On test failure, causes the kernel to panic. Recommended to be
enabled, ensuring critical functionality works as intended.
menuconfig RUNTIME_TESTING_MENU menuconfig RUNTIME_TESTING_MENU
bool "Runtime Testing" bool "Runtime Testing"
default y default y