Commit 62660229 authored by Yicong Yang's avatar Yicong Yang Committed by Jie Liu
Browse files

irqchip/gic-v3: Fix one race condition due to NMI withdraw

kunpeng inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I9QIWG


CVE: NA

----------------------------------------------------------------------

The introduce of FEAT_NMI/FEAT_GICv3_NMI will cause a race problem that
we may handle the normal interrupt in interrupt disabled context due to
the withdraw of NMI interrupt. The flow will be like below:

[interrupt disabled]
                  <- normal interrupt pending, for example timer interrupt
                  <- NMI occurs, ISR_EL1.nmi = 1
do_el1_interrupt()
                  <- NMI withdraw, ISR_EL1.nmi = 0
  ISR_EL1.nmi = 0, not an NMI interrupt
  gic_handle_irq()
    __gic_handle_irq_from_irqson()
      irqnr = gic_read_iar() <- Oops, ack and handle an normal interrupt
                                in interrupt disabled context!

Fix this by checking the interrupt status in __gic_handle_irq_from_irqson()
and ignore the interrupt if we're in interrupt disabled context.

Fixes: 0408b5bc ("irqchip/gic-v3: Implement FEAT_GICv3_NMI support")
Signed-off-by: default avatarYicong Yang <yangyicong@hisilicon.com>
Signed-off-by: default avatarJie Liu <liujie375@h-partners.com>
parent e26dab40
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -865,6 +865,28 @@ static void __gic_handle_irq_from_irqson(struct pt_regs *regs)
	bool is_nmi;
	u32 irqnr;

	/*
	 * We should enter here with interrupts disabled, otherwise we may met
	 * a race here with FEAT_NMI/FEAT_GICv3_NMI:
	 *
	 * [interrupt disabled]
	 *                   <- normal interrupt pending, for example timer interrupt
	 *                   <- NMI occurs, ISR_EL1.nmi = 1
	 * do_el1_interrupt()
	 *                   <- NMI withdraw, ISR_EL1.nmi = 0
	 *   ISR_EL1.nmi = 0, not an NMI interrupt
	 *   gic_handle_irq()
	 *     __gic_handle_irq_from_irqson()
	 *       irqnr = gic_read_iar() <- Oops, ack and handle an normal interrupt
	 *                                 in interrupt disabled context!
	 *
	 * So if we met this case here, just return from the interrupt context.
	 * Since the interrupt is still pending, we can handle it once the
	 * interrupt re-enabled and it'll not be missing.
	 */
	if (!interrupts_enabled(regs))
		return;

	irqnr = gic_read_iar();

	is_nmi = gic_rpr_is_nmi_prio();