Skip to content

Commit 2112a9d

Browse files
paulmckrcugregkh
authored andcommitted
x86/nmi: Fix out-of-order NMI nesting checks & false positive warning
[ Upstream commit f44075e ] The ->idt_seq and ->recv_jiffies variables added by: 1a3ea61 ("x86/nmi: Accumulate NMI-progress evidence in exc_nmi()") ... place the exit-time check of the bottom bit of ->idt_seq after the this_cpu_dec_return() that re-enables NMI nesting. This can result in the following sequence of events on a given CPU in kernels built with CONFIG_NMI_CHECK_CPU=y: o An NMI arrives, and ->idt_seq is incremented to an odd number. In addition, nmi_state is set to NMI_EXECUTING==1. o The NMI is processed. o The this_cpu_dec_return(nmi_state) zeroes nmi_state and returns NMI_EXECUTING==1, thus opting out of the "goto nmi_restart". o Another NMI arrives and ->idt_seq is incremented to an even number, triggering the warning. But all is just fine, at least assuming we don't get so many closely spaced NMIs that the stack overflows or some such. Experience on the fleet indicates that the MTBF of this false positive is about 70 years. Or, for those who are not quite that patient, the MTBF appears to be about one per week per 4,000 systems. Fix this false-positive warning by moving the "nmi_restart" label before the initial ->idt_seq increment/check and moving the this_cpu_dec_return() to follow the final ->idt_seq increment/check. This way, all nested NMIs that get past the NMI_NOT_RUNNING check get a clean ->idt_seq slate. And if they don't get past that check, they will set nmi_state to NMI_LATCHED, which will cause the this_cpu_dec_return(nmi_state) to restart. Fixes: 1a3ea61 ("x86/nmi: Accumulate NMI-progress evidence in exc_nmi()") Reported-by: Chris Mason <[email protected]> Signed-off-by: Paul E. McKenney <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Link: https://lore.kernel.org/r/0cbff831-6e3d-431c-9830-ee65ee7787ff@paulmck-laptop Signed-off-by: Sasha Levin <[email protected]>
1 parent 567d915 commit 2112a9d

File tree

1 file changed

+7
-6
lines changed

1 file changed

+7
-6
lines changed

arch/x86/kernel/nmi.c

+7-6
Original file line numberDiff line numberDiff line change
@@ -507,12 +507,13 @@ DEFINE_IDTENTRY_RAW(exc_nmi)
507507
}
508508
this_cpu_write(nmi_state, NMI_EXECUTING);
509509
this_cpu_write(nmi_cr2, read_cr2());
510+
511+
nmi_restart:
510512
if (IS_ENABLED(CONFIG_NMI_CHECK_CPU)) {
511513
WRITE_ONCE(nsp->idt_seq, nsp->idt_seq + 1);
512514
WARN_ON_ONCE(!(nsp->idt_seq & 0x1));
513515
WRITE_ONCE(nsp->recv_jiffies, jiffies);
514516
}
515-
nmi_restart:
516517

517518
/*
518519
* Needs to happen before DR7 is accessed, because the hypervisor can
@@ -548,16 +549,16 @@ DEFINE_IDTENTRY_RAW(exc_nmi)
548549

549550
if (unlikely(this_cpu_read(nmi_cr2) != read_cr2()))
550551
write_cr2(this_cpu_read(nmi_cr2));
551-
if (this_cpu_dec_return(nmi_state))
552-
goto nmi_restart;
553-
554-
if (user_mode(regs))
555-
mds_user_clear_cpu_buffers();
556552
if (IS_ENABLED(CONFIG_NMI_CHECK_CPU)) {
557553
WRITE_ONCE(nsp->idt_seq, nsp->idt_seq + 1);
558554
WARN_ON_ONCE(nsp->idt_seq & 0x1);
559555
WRITE_ONCE(nsp->recv_jiffies, jiffies);
560556
}
557+
if (this_cpu_dec_return(nmi_state))
558+
goto nmi_restart;
559+
560+
if (user_mode(regs))
561+
mds_user_clear_cpu_buffers();
561562
}
562563

563564
#if IS_ENABLED(CONFIG_KVM_INTEL)

0 commit comments

Comments
 (0)