x86/irq: fix infinite loop in irq_move_cleanup_interrupt

If Xen enters irq_move_cleanup_interrupt with a dynamic vector below
IRQ_MOVE_CLEANUP_VECTOR pending in IRR (0x20 or 0x21) that's also
designated for a cleanup it will enter a loop where
irq_move_cleanup_interrupt continuously sends a cleanup IPI (vector
0x22) to itself while waiting for the vector with lower priority to be
injected - which will never happen because IRQ_MOVE_CLEANUP_VECTOR
takes precedence and it's always injected first.

Fix this by making sure vectors below IRQ_MOVE_CLEANUP_VECTOR are
marked as used and thus not available for APs. Also add some logic to
assert and prevent irq_move_cleanup_interrupt from entering such an
infinite loop, albeit that should never happen given the current code.

This is XSA-356 / CVE-2020-29567.

Fixes: 3fba06ba9f ('x86/IRQ: re-use legacy vector ranges on APs')
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Roger Pau Monné authored and Jan Beulich committed 2020-12-15 13:42:16 +01:00
1 parent e6ebd39438
commit ca85682e8c
1 file changed
+14 -6
+14 -6
View File
@@ -448,8 +448,15 @@ int __init init_irq_data(void)
set_bit(HYPERCALL_VECTOR, used_vectors);
#endif
/* IRQ_MOVE_CLEANUP_VECTOR used for clean up vectors */
set_bit(IRQ_MOVE_CLEANUP_VECTOR, used_vectors);
/*
* Mark vectors up to the cleanup one as used, to prevent an infinite loop
* invoking irq_move_cleanup_interrupt.
*/
BUILD_BUG_ON(IRQ_MOVE_CLEANUP_VECTOR < FIRST_DYNAMIC_VECTOR);
for ( vector = FIRST_DYNAMIC_VECTOR;
vector <= IRQ_MOVE_CLEANUP_VECTOR;
vector++ )
__set_bit(vector, used_vectors);
return 0;
}
@@ -734,10 +741,6 @@ void irq_move_cleanup_interrupt(struct cpu_user_regs *regs)
{
unsigned vector, me;
/* This interrupt should not nest inside others. */
BUILD_BUG_ON(APIC_PRIO_CLASS(IRQ_MOVE_CLEANUP_VECTOR) !=
APIC_PRIO_CLASS(FIRST_DYNAMIC_VECTOR));
ack_APIC_irq();
me = smp_processor_id();
@@ -781,6 +784,11 @@ void irq_move_cleanup_interrupt(struct cpu_user_regs *regs)
*/
if ( irr & (1u << (vector % 32)) )
{
if ( vector < IRQ_MOVE_CLEANUP_VECTOR )
{
ASSERT_UNREACHABLE();
goto unlock;
}
send_IPI_self(IRQ_MOVE_CLEANUP_VECTOR);
TRACE_3D(TRC_HW_IRQ_MOVE_CLEANUP_DELAY,
irq, vector, smp_processor_id());
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.