When calling KMSAN-instrumented functions from non-instrumented functions, function parameters may not be initialized properly, leading to false positive reports. In particular, this happens all the time when calling interrupt handlers from `noinstr` IDT entries. Fortunately, x86 code has instrumentation_begin() and instrumentation_end(), which denote the regions where `noinstr` code calls potentially instrumented code. We add calls to kmsan_instrumentation_begin() to those regions, which: - wipe the current KMSAN state at the beginning of the region, ensuring that the first call of an instrumented function receives initialized parameters (this is a pretty good approximation of having all other instrumented functions receive initialized parameters); - unpoison the `struct pt_regs` set up by the non-instrumented assembly code. Signed-off-by: Alexander Potapenko <glider@xxxxxxxxxx> --- Link: https://linux-review.googlesource.com/id/I435ec076cd21752c2f877f5da81f5eced62a2ea4 --- arch/x86/entry/common.c | 2 ++ arch/x86/include/asm/idtentry.h | 5 +++++ arch/x86/kernel/cpu/mce/core.c | 1 + arch/x86/kernel/kvm.c | 1 + arch/x86/kernel/nmi.c | 1 + arch/x86/kernel/sev.c | 2 ++ arch/x86/kernel/traps.c | 7 +++++++ arch/x86/mm/fault.c | 1 + kernel/entry/common.c | 3 +++ 9 files changed, 23 insertions(+) diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index 6c2826417b337..a0f90588c514e 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -14,6 +14,7 @@ #include <linux/mm.h> #include <linux/smp.h> #include <linux/errno.h> +#include <linux/kmsan.h> #include <linux/ptrace.h> #include <linux/export.h> #include <linux/nospec.h> @@ -76,6 +77,7 @@ __visible noinstr void do_syscall_64(struct pt_regs *regs, int nr) nr = syscall_enter_from_user_mode(regs, nr); instrumentation_begin(); + kmsan_instrumentation_begin(regs); if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) { /* Invalid system call, but still a system call. */ diff --git a/arch/x86/include/asm/idtentry.h b/arch/x86/include/asm/idtentry.h index 1345088e99025..f025fdc0f25df 100644 --- a/arch/x86/include/asm/idtentry.h +++ b/arch/x86/include/asm/idtentry.h @@ -52,6 +52,7 @@ __visible noinstr void func(struct pt_regs *regs) \ irqentry_state_t state = irqentry_enter(regs); \ \ instrumentation_begin(); \ + kmsan_instrumentation_begin(regs); \ __##func (regs); \ instrumentation_end(); \ irqentry_exit(regs, state); \ @@ -99,6 +100,7 @@ __visible noinstr void func(struct pt_regs *regs, \ irqentry_state_t state = irqentry_enter(regs); \ \ instrumentation_begin(); \ + kmsan_instrumentation_begin(regs); \ __##func (regs, error_code); \ instrumentation_end(); \ irqentry_exit(regs, state); \ @@ -196,6 +198,7 @@ __visible noinstr void func(struct pt_regs *regs, \ u32 vector = (u32)(u8)error_code; \ \ instrumentation_begin(); \ + kmsan_instrumentation_begin(regs); \ kvm_set_cpu_l1tf_flush_l1d(); \ run_irq_on_irqstack_cond(__##func, regs, vector); \ instrumentation_end(); \ @@ -236,6 +239,7 @@ __visible noinstr void func(struct pt_regs *regs) \ irqentry_state_t state = irqentry_enter(regs); \ \ instrumentation_begin(); \ + kmsan_instrumentation_begin(regs); \ kvm_set_cpu_l1tf_flush_l1d(); \ run_sysvec_on_irqstack_cond(__##func, regs); \ instrumentation_end(); \ @@ -263,6 +267,7 @@ __visible noinstr void func(struct pt_regs *regs) \ irqentry_state_t state = irqentry_enter(regs); \ \ instrumentation_begin(); \ + kmsan_instrumentation_begin(regs); \ __irq_enter_raw(); \ kvm_set_cpu_l1tf_flush_l1d(); \ __##func (regs); \ diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index 6ed365337a3b1..b49e2c6bb8ca2 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -1314,6 +1314,7 @@ static void queue_task_work(struct mce *m, char *msg, void (*func)(struct callba static noinstr void unexpected_machine_check(struct pt_regs *regs) { instrumentation_begin(); + kmsan_instrumentation_begin(regs); pr_err("CPU#%d: Unexpected int18 (Machine Check)\n", smp_processor_id()); instrumentation_end(); diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index 59abbdad7729c..55ffe1bc73b00 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -250,6 +250,7 @@ noinstr bool __kvm_handle_async_pf(struct pt_regs *regs, u32 token) state = irqentry_enter(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); /* * If the host managed to inject an async #PF into an interrupt diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index 4bce802d25fb1..d91327d271359 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c @@ -330,6 +330,7 @@ static noinstr void default_do_nmi(struct pt_regs *regs) __this_cpu_write(last_nmi_rip, regs->ip); instrumentation_begin(); + kmsan_instrumentation_begin(regs); handled = nmi_handle(NMI_LOCAL, regs); __this_cpu_add(nmi_stats.normal, handled); diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c index a9fc2ac7a8bd5..421d59b982cae 100644 --- a/arch/x86/kernel/sev.c +++ b/arch/x86/kernel/sev.c @@ -1426,6 +1426,7 @@ DEFINE_IDTENTRY_VC_KERNEL(exc_vmm_communication) irq_state = irqentry_nmi_enter(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); if (!vc_raw_handle_exception(regs, error_code)) { /* Show some debug info */ @@ -1458,6 +1459,7 @@ DEFINE_IDTENTRY_VC_USER(exc_vmm_communication) irqentry_enter_from_user_mode(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); if (!vc_raw_handle_exception(regs, error_code)) { /* diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index c9d566dcf89a0..3a821010def63 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -230,6 +230,7 @@ static noinstr bool handle_bug(struct pt_regs *regs) * All lies, just get the WARN/BUG out. */ instrumentation_begin(); + kmsan_instrumentation_begin(regs); /* * Since we're emulating a CALL with exceptions, restore the interrupt * state to what it was at the exception site. @@ -261,6 +262,7 @@ DEFINE_IDTENTRY_RAW(exc_invalid_op) state = irqentry_enter(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); handle_invalid_op(regs); instrumentation_end(); irqentry_exit(regs, state); @@ -415,6 +417,7 @@ DEFINE_IDTENTRY_DF(exc_double_fault) irqentry_nmi_enter(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV); tsk->thread.error_code = error_code; @@ -690,6 +693,7 @@ DEFINE_IDTENTRY_RAW(exc_int3) if (user_mode(regs)) { irqentry_enter_from_user_mode(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); do_int3_user(regs); instrumentation_end(); irqentry_exit_to_user_mode(regs); @@ -697,6 +701,7 @@ DEFINE_IDTENTRY_RAW(exc_int3) irqentry_state_t irq_state = irqentry_nmi_enter(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); if (!do_int3(regs)) die("int3", regs, 0); instrumentation_end(); @@ -896,6 +901,7 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs, unsigned long dr7 = local_db_save(); irqentry_state_t irq_state = irqentry_nmi_enter(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); /* * If something gets miswired and we end up here for a user mode @@ -975,6 +981,7 @@ static __always_inline void exc_debug_user(struct pt_regs *regs, irqentry_enter_from_user_mode(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); /* * Start the virtual/ptrace DR6 value with just the DR_STEP mask diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index abed0aedf00d2..0437d2fe31ecb 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -1558,6 +1558,7 @@ DEFINE_IDTENTRY_RAW_ERRORCODE(exc_page_fault) state = irqentry_enter(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); handle_page_fault(regs, error_code, address); instrumentation_end(); diff --git a/kernel/entry/common.c b/kernel/entry/common.c index d5a61d565ad5d..3a569ea5a78fb 100644 --- a/kernel/entry/common.c +++ b/kernel/entry/common.c @@ -104,6 +104,7 @@ noinstr long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall) __enter_from_user_mode(regs); instrumentation_begin(); + kmsan_instrumentation_begin(regs); local_irq_enable(); ret = __syscall_enter_from_user_work(regs, syscall); instrumentation_end(); @@ -297,6 +298,7 @@ void syscall_exit_to_user_mode_work(struct pt_regs *regs) __visible noinstr void syscall_exit_to_user_mode(struct pt_regs *regs) { instrumentation_begin(); + kmsan_instrumentation_begin(regs); __syscall_exit_to_user_mode_work(regs); instrumentation_end(); __exit_to_user_mode(); @@ -310,6 +312,7 @@ noinstr void irqentry_enter_from_user_mode(struct pt_regs *regs) noinstr void irqentry_exit_to_user_mode(struct pt_regs *regs) { instrumentation_begin(); + kmsan_instrumentation_begin(regs); exit_to_user_mode_prepare(regs); instrumentation_end(); __exit_to_user_mode(); -- 2.34.1.173.g76aa8bc2d0-goog