[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v3 2/3] pirq_cleanup_check() leaks
On Thu, Jul 27, 2023 at 09:38:29AM +0200, Jan Beulich wrote: > Its original introduction had two issues: For one the "common" part of > the checks (carried out in the macro) was inverted. Is the current logic in evtchn_close() really malfunctioning? pirq->evtchn = 0; pirq_cleanup_check(pirq, d1); <- cleanup for PV domains if ( is_hvm_domain(d1) && domain_pirq_to_irq(d1, pirq->pirq) > 0 ) unmap_domain_pirq_emuirq(d1, pirq->pirq); <- cleanup for HVM domains It would seem to me the pirq_cleanup_check() call just after setting evtchn = 0 was done to account for PV domains, while the second (hidden) pirq_cleanup_check() call in unmap_domain_pirq_emuirq() would do the cleanup for HVM domains. Maybe there's something that I'm missing, I have to admit the PIRQ logic is awfully complicated, even more when we mix the HVM PIRQ stuff. > And then after > removal from the radix tree the structure wasn't scheduled for freeing. > (All structures still left in the radix tree would be freed upon domain > destruction, though.) So if my understanding is correct, we didn't have a leak due to the missing free_pirq_struct() because the inverted check in pirq_cleanup_check() macro prevented the removal from the radix tree, and so stale entries would be left there and freed at domain destruction? > For the freeing to be safe even if it didn't use RCU (i.e. to avoid use- > after-free), re-arrange checks/operations in evtchn_close(), such that > the pointer wouldn't be used anymore after calling pirq_cleanup_check() > (noting that unmap_domain_pirq_emuirq() itself calls the function in the > success case). > > Fixes: c24536b636f2 ("replace d->nr_pirqs sized arrays with radix tree") > Fixes: 79858fee307c ("xen: fix hvm_domain_use_pirq's behavior") See the comment above about the call to pirq_cleanup_check() in unmap_domain_pirq_emuirq(), which might imply 79858fee307c is not wrong, just confusing. > Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx> > --- > This is fallout from me looking into whether the function and macro of > the same name could be suitably split, to please Misra rule 5.5. The > idea was apparently that the check done in the macro is the "common" > part, and the actual function would be per-architecture. Pretty clearly > this, if need be, could also be achieved by naming the actual function > e.g. arch_pirq_cleanup_check(). > > Despite my testing of this (to a certain degree), I'm wary of the > change, since the code has been the way it was for about 12 years. It > feels like I'm overlooking something crucial ... > > The wrong check is also what explains why Arm got away without > implementing the function (prior to "restrict concept of pIRQ to x86"): > The compiler simply eliminated the two calls from event_channel.c. > --- > v3: New. > > --- a/xen/arch/x86/irq.c > +++ b/xen/arch/x86/irq.c > @@ -1349,6 +1349,7 @@ void (pirq_cleanup_check)(struct pirq *p > > if ( radix_tree_delete(&d->pirq_tree, pirq->pirq) != pirq ) > BUG(); > + free_pirq_struct(pirq); > } > > /* Flush all ready EOIs from the top of this CPU's pending-EOI stack. */ > --- a/xen/common/event_channel.c > +++ b/xen/common/event_channel.c > @@ -711,9 +711,10 @@ int evtchn_close(struct domain *d1, int > if ( !is_hvm_domain(d1) ) > pirq_guest_unbind(d1, pirq); > pirq->evtchn = 0; > - pirq_cleanup_check(pirq, d1); > - if ( is_hvm_domain(d1) && domain_pirq_to_irq(d1, pirq->pirq) > 0 > ) > - unmap_domain_pirq_emuirq(d1, pirq->pirq); > + if ( !is_hvm_domain(d1) || > + domain_pirq_to_irq(d1, pirq->pirq) <= 0 || > + unmap_domain_pirq_emuirq(d1, pirq->pirq) < 0 ) pirq_cleanup_check() already calls pirq_cleanup_check() itself. Could you please add a comment to note that unmap_domain_pirq_emuirq() succeeding implies the call to pirq_cleanup_check() has already been done? Otherwise the logic here looks unbalanced by skipping the pirq_cleanup_check() when unmap_domain_pirq_emuirq() succeeds. > + pirq_cleanup_check(pirq, d1); > } > unlink_pirq_port(chn1, d1->vcpu[chn1->notify_vcpu_id]); > break; > --- a/xen/include/xen/irq.h > +++ b/xen/include/xen/irq.h > @@ -158,7 +158,7 @@ extern struct pirq *pirq_get_info(struct > void pirq_cleanup_check(struct pirq *, struct domain *); > > #define pirq_cleanup_check(pirq, d) \ > - ((pirq)->evtchn ? pirq_cleanup_check(pirq, d) : (void)0) > + (!(pirq)->evtchn ? pirq_cleanup_check(pirq, d) : (void)0) Not that you need to fix it here, but why not place this check in pirq_cleanup_check() itself? Thanks, Roger.
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |