ACK/Cmnt: [PATCH] xen/events: avoid removing an event channel while handling it

Tim Gardner tim.gardner at canonical.com
Tue Feb 23 14:58:46 UTC 2021



On 2/23/21 7:55 AM, Stefan Bader wrote:
> On 23.02.21 14:23, Tim Gardner wrote:
>> From: Juergen Gross <jgross at suse.com>
>>
>> Today it can happen that an event channel is being removed from the
>> system while the event handling loop is active. This can lead to a
>> race resulting in crashes or WARN() splats when trying to access the
>> irq_info structure related to the event channel.
>>
>> Fix this problem by using a rwlock taken as reader in the event
>> handling loop and as writer when deallocating the irq_info structure.
>>
>> As the observed problem was a NULL dereference in evtchn_from_irq()
>> make this function more robust against races by testing the irq_info
>> pointer to be not NULL before dereferencing it.
>>
>> And finally make all accesses to evtchn_to_irq[row][col] atomic ones
>> in order to avoid seeing partial updates of an array element in irq
>> handling. Note that irq handling can be entered only for event channels
>> which have been valid before, so any not populated row isn't a problem
>> in this regard, as rows are only ever added and never removed.
>>
>> This is XSA-331.
>>
>> Cc: stable at vger.kernel.org
>> Reported-by: Marek Marczykowski-Górecki <marmarek at invisiblethingslab.com>
>> Reported-by: Jinoh Kang <luke1337 at theori.io>
>> Signed-off-by: Juergen Gross <jgross at suse.com>
>> Reviewed-by: Stefano Stabellini <sstabellini at kernel.org>
>> Reviewed-by: Wei Liu <wl at xen.org>
>> (backported from commit 073d0552ead5bfc7a3a9c01de590e924f11b5dd2)
> [rtg: Minor context adjustment in xen_free_irq()]
>> Signed-off-by: Tim Gardner <tim.gardner at canonical.com>
> Acked-by: Stefan Bader <stefan.bader at canonical.com>
>>
>> Conflicts:
>> 	drivers/xen/events/events_base.c
>> ---
> 
> I give up. Why above is not possible, I cannot understand.
> 

I have no idea what you want here. As I pointed out above, the conflict 
was a minor context adjustment in xen_free_irq(). How can I better 
describe what I did ?

> -Stefan
> 
>>   drivers/xen/events/events_base.c | 41 ++++++++++++++++++++++++++++----
>>   1 file changed, 36 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
>> index 499eff7d3f65..c56fc81a409f 100644
>> --- a/drivers/xen/events/events_base.c
>> +++ b/drivers/xen/events/events_base.c
>> @@ -33,6 +33,7 @@
>>   #include <linux/slab.h>
>>   #include <linux/irqnr.h>
>>   #include <linux/pci.h>
>> +#include <linux/spinlock.h>
>>   
>>   #ifdef CONFIG_X86
>>   #include <asm/desc.h>
>> @@ -70,6 +71,23 @@ const struct evtchn_ops *evtchn_ops;
>>    */
>>   static DEFINE_MUTEX(irq_mapping_update_lock);
>>   
>> +/*
>> + * Lock protecting event handling loop against removing event channels.
>> + * Adding of event channels is no issue as the associated IRQ becomes active
>> + * only after everything is setup (before request_[threaded_]irq() the handler
>> + * can't be entered for an event, as the event channel will be unmasked only
>> + * then).
>> + */
>> +static DEFINE_RWLOCK(evtchn_rwlock);
>> +
>> +/*
>> + * Lock hierarchy:
>> + *
>> + * irq_mapping_update_lock
>> + *   evtchn_rwlock
>> + *     IRQ-desc lock
>> + */
>> +
>>   static LIST_HEAD(xen_irq_list_head);
>>   
>>   /* IRQ <-> VIRQ mapping. */
>> @@ -102,7 +120,7 @@ static void clear_evtchn_to_irq_row(unsigned row)
>>   	unsigned col;
>>   
>>   	for (col = 0; col < EVTCHN_PER_ROW; col++)
>> -		evtchn_to_irq[row][col] = -1;
>> +		WRITE_ONCE(evtchn_to_irq[row][col], -1);
>>   }
>>   
>>   static void clear_evtchn_to_irq_all(void)
>> @@ -139,7 +157,7 @@ static int set_evtchn_to_irq(unsigned evtchn, unsigned irq)
>>   		clear_evtchn_to_irq_row(row);
>>   	}
>>   
>> -	evtchn_to_irq[row][col] = irq;
>> +	WRITE_ONCE(evtchn_to_irq[row][col], irq);
>>   	return 0;
>>   }
>>   
>> @@ -149,7 +167,7 @@ int get_evtchn_to_irq(unsigned evtchn)
>>   		return -1;
>>   	if (evtchn_to_irq[EVTCHN_ROW(evtchn)] == NULL)
>>   		return -1;
>> -	return evtchn_to_irq[EVTCHN_ROW(evtchn)][EVTCHN_COL(evtchn)];
>> +	return READ_ONCE(evtchn_to_irq[EVTCHN_ROW(evtchn)][EVTCHN_COL(evtchn)]);
>>   }
>>   
>>   /* Get info for IRQ */
>> @@ -247,10 +265,14 @@ static void xen_irq_info_cleanup(struct irq_info *info)
>>    */
>>   unsigned int evtchn_from_irq(unsigned irq)
>>   {
>> -	if (WARN(irq >= nr_irqs, "Invalid irq %d!\n", irq))
>> +	const struct irq_info *info = NULL;
>> +
>> +	if (likely(irq < nr_irqs))
>> +		info = info_for_irq(irq);
>> +	if (!info)
>>   		return 0;
>>   
>> -	return info_for_irq(irq)->evtchn;
>> +	return info->evtchn;
>>   }
>>   
>>   unsigned irq_from_evtchn(unsigned int evtchn)
>> @@ -426,16 +448,21 @@ static int __must_check xen_allocate_irq_gsi(unsigned gsi)
>>   static void xen_free_irq(unsigned irq)
>>   {
>>   	struct irq_info *info = irq_get_handler_data(irq);
>> +	unsigned long flags;
>>   
>>   	if (WARN_ON(!info))
>>   		return;
>>   
>> +	write_lock_irqsave(&evtchn_rwlock, flags);
>> +
>>   	list_del(&info->list);
>>   
>>   	irq_set_handler_data(irq, NULL);
>>   
>>   	WARN_ON(info->refcnt > 0);
>>   
>> +	write_unlock_irqrestore(&evtchn_rwlock, flags);
>> +
>>   	kfree(info);
>>   
>>   	/* Legacy IRQ descriptors are managed by the arch. */
>> @@ -1218,6 +1245,8 @@ static void __xen_evtchn_do_upcall(void)
>>   	struct vcpu_info *vcpu_info = __this_cpu_read(xen_vcpu);
>>   	int cpu = smp_processor_id();
>>   
>> +	read_lock(&evtchn_rwlock);
>> +
>>   	do {
>>   		vcpu_info->evtchn_upcall_pending = 0;
>>   
>> @@ -1228,6 +1257,8 @@ static void __xen_evtchn_do_upcall(void)
>>   		virt_rmb(); /* Hypervisor can set upcall pending. */
>>   
>>   	} while (vcpu_info->evtchn_upcall_pending);
>> +
>> +	read_unlock(&evtchn_rwlock);
>>   }
>>   
>>   void xen_evtchn_do_upcall(struct pt_regs *regs)
>>
> 
> 

-- 
-----------
Tim Gardner
Canonical, Inc



More information about the kernel-team mailing list