head 1.2; access; symbols pkgsrc-2022Q2:1.1.0.16 pkgsrc-2022Q2-base:1.1 pkgsrc-2022Q1:1.1.0.14 pkgsrc-2022Q1-base:1.1 pkgsrc-2021Q4:1.1.0.12 pkgsrc-2021Q4-base:1.1 pkgsrc-2021Q3:1.1.0.10 pkgsrc-2021Q3-base:1.1 pkgsrc-2021Q2:1.1.0.8 pkgsrc-2021Q2-base:1.1 pkgsrc-2021Q1:1.1.0.6 pkgsrc-2021Q1-base:1.1 pkgsrc-2020Q4:1.1.0.4 pkgsrc-2020Q4-base:1.1 pkgsrc-2020Q3:1.1.0.2; locks; strict; comment @# @; 1.2 date 2022.06.28.16.33.25; author bouyer; state dead; branches; next 1.1; commitid lkljtNX5KSYPgPJD; 1.1 date 2020.10.02.13.00.48; author bouyer; state Exp; branches 1.1.2.1; next ; commitid 2MY96Iq2OdtetkqC; 1.1.2.1 date 2020.10.02.13.00.48; author bsiegert; state dead; branches; next 1.1.2.2; commitid 6fMH1kUIpq0AYCqC; 1.1.2.2 date 2020.10.04.20.44.32; author bsiegert; state Exp; branches; next ; commitid 6fMH1kUIpq0AYCqC; desc @@ 1.2 log @Remove xenkernel411 and xenkernel411, they're EOL upstream @ text @$NetBSD: patch-XSA343,v 1.1 2020/10/02 13:00:48 bouyer Exp $ From: Jan Beulich Subject: evtchn: evtchn_reset() shouldn't succeed with still-open ports While the function closes all ports, it does so without holding any lock, and hence racing requests may be issued causing new ports to get opened. This would have been problematic in particular if such a newly opened port had a port number above the new implementation limit (i.e. when switching from FIFO to 2-level) after the reset, as prior to "evtchn: relax port_is_valid()" this could have led to e.g. evtchn_close()'s "BUG_ON(!port_is_valid(d2, port2))" to trigger. Introduce a counter of active ports and check that it's (still) no larger then the number of Xen internally used ones after obtaining the necessary lock in evtchn_reset(). As to the access model of the new {active,xen}_evtchns fields - while all writes get done using write_atomic(), reads ought to use read_atomic() only when outside of a suitably locked region. Note that as of now evtchn_bind_virq() and evtchn_bind_ipi() don't have a need to call check_free_port(). This is part of XSA-343. Signed-off-by: Jan Beulich Reviewed-by: Stefano Stabellini Reviewed-by: Julien Grall --- xen/common/event_channel.c.orig +++ xen/common/event_channel.c @@@@ -188,6 +188,8 @@@@ int evtchn_allocate_port(struct domain * write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); } + write_atomic(&d->active_evtchns, d->active_evtchns + 1); + return 0; } @@@@ -211,11 +213,26 @@@@ static int get_free_port(struct domain * return -ENOSPC; } +/* + * Check whether a port is still marked free, and if so update the domain + * counter accordingly. To be used on function exit paths. + */ +static void check_free_port(struct domain *d, evtchn_port_t port) +{ + if ( port_is_valid(d, port) && + evtchn_from_port(d, port)->state == ECS_FREE ) + write_atomic(&d->active_evtchns, d->active_evtchns - 1); +} + void evtchn_free(struct domain *d, struct evtchn *chn) { /* Clear pending event to avoid unexpected behavior on re-bind. */ evtchn_port_clear_pending(d, chn); + if ( consumer_is_xen(chn) ) + write_atomic(&d->xen_evtchns, d->xen_evtchns - 1); + write_atomic(&d->active_evtchns, d->active_evtchns - 1); + /* Reset binding to vcpu0 when the channel is freed. */ chn->state = ECS_FREE; chn->notify_vcpu_id = 0; @@@@ -258,6 +275,7 @@@@ static long evtchn_alloc_unbound(evtchn_ alloc->port = port; out: + check_free_port(d, port); spin_unlock(&d->event_lock); rcu_unlock_domain(d); @@@@ -351,6 +369,7 @@@@ static long evtchn_bind_interdomain(evtc bind->local_port = lport; out: + check_free_port(ld, lport); spin_unlock(&ld->event_lock); if ( ld != rd ) spin_unlock(&rd->event_lock); @@@@ -484,7 +503,7 @@@@ static long evtchn_bind_pirq(evtchn_bind struct domain *d = current->domain; struct vcpu *v = d->vcpu[0]; struct pirq *info; - int port, pirq = bind->pirq; + int port = 0, pirq = bind->pirq; long rc; if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) @@@@ -532,6 +551,7 @@@@ static long evtchn_bind_pirq(evtchn_bind arch_evtchn_bind_pirq(d, pirq); out: + check_free_port(d, port); spin_unlock(&d->event_lock); return rc; @@@@ -1005,10 +1025,10 @@@@ int evtchn_unmask(unsigned int port) return 0; } - int evtchn_reset(struct domain *d) { unsigned int i; + int rc = 0; if ( d != current->domain && !d->controller_pause_count ) return -EINVAL; @@@@ -1018,7 +1038,9 @@@@ int evtchn_reset(struct domain *d) spin_lock(&d->event_lock); - if ( d->evtchn_fifo ) + if ( d->active_evtchns > d->xen_evtchns ) + rc = -EAGAIN; + else if ( d->evtchn_fifo ) { /* Switching back to 2-level ABI. */ evtchn_fifo_destroy(d); @@@@ -1027,7 +1049,7 @@@@ int evtchn_reset(struct domain *d) spin_unlock(&d->event_lock); - return 0; + return rc; } static long evtchn_set_priority(const struct evtchn_set_priority *set_priority) @@@@ -1213,10 +1235,9 @@@@ int alloc_unbound_xen_event_channel( spin_lock(&ld->event_lock); - rc = get_free_port(ld); + port = rc = get_free_port(ld); if ( rc < 0 ) goto out; - port = rc; chn = evtchn_from_port(ld, port); rc = xsm_evtchn_unbound(XSM_TARGET, ld, chn, remote_domid); @@@@ -1232,7 +1253,10 @@@@ int alloc_unbound_xen_event_channel( spin_unlock(&chn->lock); + write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); + out: + check_free_port(ld, port); spin_unlock(&ld->event_lock); return rc < 0 ? rc : port; @@@@ -1308,6 +1332,7 @@@@ int evtchn_init(struct domain *d) return -EINVAL; } evtchn_from_port(d, 0)->state = ECS_RESERVED; + write_atomic(&d->active_evtchns, 0); #if MAX_VIRT_CPUS > BITS_PER_LONG d->poll_mask = xzalloc_array(unsigned long, @@@@ -1335,6 +1360,8 @@@@ void evtchn_destroy(struct domain *d) for ( i = 0; port_is_valid(d, i); i++ ) evtchn_close(d, i, 0); + ASSERT(!d->active_evtchns); + clear_global_virq_handlers(d); evtchn_fifo_destroy(d); --- xen/include/xen/sched.h.orig +++ xen/include/xen/sched.h @@@@ -345,6 +345,16 @@@@ struct domain struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ unsigned int max_evtchn_port; /* max permitted port number */ unsigned int valid_evtchns; /* number of allocated event channels */ + /* + * Number of in-use event channels. Writers should use write_atomic(). + * Readers need to use read_atomic() only when not holding event_lock. + */ + unsigned int active_evtchns; + /* + * Number of event channels used internally by Xen (not subject to + * EVTCHNOP_reset). Read/write access like for active_evtchns. + */ + unsigned int xen_evtchns; spinlock_t event_lock; const struct evtchn_port_ops *evtchn_port_ops; struct evtchn_fifo_domain *evtchn_fifo; From: Jan Beulich Subject: evtchn: convert per-channel lock to be IRQ-safe ... in order for send_guest_{global,vcpu}_virq() to be able to make use of it. This is part of XSA-343. Signed-off-by: Jan Beulich Acked-by: Julien Grall --- xen/common/event_channel.c.orig +++ xen/common/event_channel.c @@@@ -248,6 +248,7 @@@@ static long evtchn_alloc_unbound(evtchn_ int port; domid_t dom = alloc->dom; long rc; + unsigned long flags; d = rcu_lock_domain_by_any_id(dom); if ( d == NULL ) @@@@ -263,14 +264,14 @@@@ static long evtchn_alloc_unbound(evtchn_ if ( rc ) goto out; - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_UNBOUND; if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) chn->u.unbound.remote_domid = current->domain->domain_id; evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); alloc->port = port; @@@@ -283,26 +284,32 @@@@ static long evtchn_alloc_unbound(evtchn_ } -static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) +static unsigned long double_evtchn_lock(struct evtchn *lchn, + struct evtchn *rchn) { - if ( lchn < rchn ) + unsigned long flags; + + if ( lchn <= rchn ) { - spin_lock(&lchn->lock); - spin_lock(&rchn->lock); + spin_lock_irqsave(&lchn->lock, flags); + if ( lchn != rchn ) + spin_lock(&rchn->lock); } else { - if ( lchn != rchn ) - spin_lock(&rchn->lock); + spin_lock_irqsave(&rchn->lock, flags); spin_lock(&lchn->lock); } + + return flags; } -static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) +static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, + unsigned long flags) { - spin_unlock(&lchn->lock); if ( lchn != rchn ) - spin_unlock(&rchn->lock); + spin_unlock(&lchn->lock); + spin_unlock_irqrestore(&rchn->lock, flags); } static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) @@@@ -312,6 +319,7 @@@@ static long evtchn_bind_interdomain(evtc int lport, rport = bind->remote_port; domid_t rdom = bind->remote_dom; long rc; + unsigned long flags; if ( rdom == DOMID_SELF ) rdom = current->domain->domain_id; @@@@ -347,7 +355,7 @@@@ static long evtchn_bind_interdomain(evtc if ( rc ) goto out; - double_evtchn_lock(lchn, rchn); + flags = double_evtchn_lock(lchn, rchn); lchn->u.interdomain.remote_dom = rd; lchn->u.interdomain.remote_port = rport; @@@@ -364,7 +372,7 @@@@ static long evtchn_bind_interdomain(evtc */ evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); - double_evtchn_unlock(lchn, rchn); + double_evtchn_unlock(lchn, rchn, flags); bind->local_port = lport; @@@@ -387,6 +395,7 @@@@ int evtchn_bind_virq(evtchn_bind_virq_t struct domain *d = current->domain; int virq = bind->virq, vcpu = bind->vcpu; int rc = 0; + unsigned long flags; if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) return -EINVAL; @@@@ -419,14 +428,14 @@@@ int evtchn_bind_virq(evtchn_bind_virq_t chn = evtchn_from_port(d, port); - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_VIRQ; chn->notify_vcpu_id = vcpu; chn->u.virq = virq; evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); v->virq_to_evtchn[virq] = bind->port = port; @@@@ -443,6 +452,7 @@@@ static long evtchn_bind_ipi(evtchn_bind_ struct domain *d = current->domain; int port, vcpu = bind->vcpu; long rc = 0; + unsigned long flags; if ( (vcpu < 0) || (vcpu >= d->max_vcpus) || (d->vcpu[vcpu] == NULL) ) @@@@ -455,13 +465,13 @@@@ static long evtchn_bind_ipi(evtchn_bind_ chn = evtchn_from_port(d, port); - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_IPI; chn->notify_vcpu_id = vcpu; evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); bind->port = port; @@@@ -505,6 +515,7 @@@@ static long evtchn_bind_pirq(evtchn_bind struct pirq *info; int port = 0, pirq = bind->pirq; long rc; + unsigned long flags; if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) return -EINVAL; @@@@ -537,14 +548,14 @@@@ static long evtchn_bind_pirq(evtchn_bind goto out; } - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_PIRQ; chn->u.pirq.irq = pirq; link_pirq_port(port, chn, v); evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); bind->port = port; @@@@ -565,6 +576,7 @@@@ int evtchn_close(struct domain *d1, int struct evtchn *chn1, *chn2; int port2; long rc = 0; + unsigned long flags; again: spin_lock(&d1->event_lock); @@@@ -664,14 +676,14 @@@@ int evtchn_close(struct domain *d1, int BUG_ON(chn2->state != ECS_INTERDOMAIN); BUG_ON(chn2->u.interdomain.remote_dom != d1); - double_evtchn_lock(chn1, chn2); + flags = double_evtchn_lock(chn1, chn2); evtchn_free(d1, chn1); chn2->state = ECS_UNBOUND; chn2->u.unbound.remote_domid = d1->domain_id; - double_evtchn_unlock(chn1, chn2); + double_evtchn_unlock(chn1, chn2, flags); goto out; @@@@ -679,9 +691,9 @@@@ int evtchn_close(struct domain *d1, int BUG(); } - spin_lock(&chn1->lock); + spin_lock_irqsave(&chn1->lock, flags); evtchn_free(d1, chn1); - spin_unlock(&chn1->lock); + spin_unlock_irqrestore(&chn1->lock, flags); out: if ( d2 != NULL ) @@@@ -701,13 +713,14 @@@@ int evtchn_send(struct domain *ld, unsig struct evtchn *lchn, *rchn; struct domain *rd; int rport, ret = 0; + unsigned long flags; if ( !port_is_valid(ld, lport) ) return -EINVAL; lchn = evtchn_from_port(ld, lport); - spin_lock(&lchn->lock); + spin_lock_irqsave(&lchn->lock, flags); /* Guest cannot send via a Xen-attached event channel. */ if ( unlikely(consumer_is_xen(lchn)) ) @@@@ -742,7 +755,7 @@@@ int evtchn_send(struct domain *ld, unsig } out: - spin_unlock(&lchn->lock); + spin_unlock_irqrestore(&lchn->lock, flags); return ret; } @@@@ -1232,6 +1245,7 @@@@ int alloc_unbound_xen_event_channel( { struct evtchn *chn; int port, rc; + unsigned long flags; spin_lock(&ld->event_lock); @@@@ -1244,14 +1258,14 @@@@ int alloc_unbound_xen_event_channel( if ( rc ) goto out; - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_UNBOUND; chn->xen_consumer = get_xen_consumer(notification_fn); chn->notify_vcpu_id = lvcpu; chn->u.unbound.remote_domid = remote_domid; - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); @@@@ -1274,11 +1288,12 @@@@ void notify_via_xen_event_channel(struct { struct evtchn *lchn, *rchn; struct domain *rd; + unsigned long flags; ASSERT(port_is_valid(ld, lport)); lchn = evtchn_from_port(ld, lport); - spin_lock(&lchn->lock); + spin_lock_irqsave(&lchn->lock, flags); if ( likely(lchn->state == ECS_INTERDOMAIN) ) { @@@@ -1288,7 +1303,7 @@@@ void notify_via_xen_event_channel(struct evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); } - spin_unlock(&lchn->lock); + spin_unlock_irqrestore(&lchn->lock, flags); } void evtchn_check_pollers(struct domain *d, unsigned int port) From: Jan Beulich Subject: evtchn: address races with evtchn_reset() Neither d->evtchn_port_ops nor max_evtchns(d) may be used in an entirely lock-less manner, as both may change by a racing evtchn_reset(). In the common case, at least one of the domain's event lock or the per-channel lock needs to be held. In the specific case of the inter-domain sending by evtchn_send() and notify_via_xen_event_channel() holding the other side's per-channel lock is sufficient, as the channel can't change state without both per-channel locks held. Without such a channel changing state, evtchn_reset() can't complete successfully. Lock-free accesses continue to be permitted for the shim (calling some otherwise internal event channel functions), as this happens while the domain is in effectively single-threaded mode. Special care also needs taking for the shim's marking of in-use ports as ECS_RESERVED (allowing use of such ports in the shim case is okay because switching into and hence also out of FIFO mode is impossible there). As a side effect, certain operations on Xen bound event channels which were mistakenly permitted so far (e.g. unmask or poll) will be refused now. This is part of XSA-343. Reported-by: Julien Grall Signed-off-by: Jan Beulich Acked-by: Julien Grall --- xen/arch/x86/irq.c.orig +++ xen/arch/x86/irq.c @@@@ -2367,14 +2367,24 @@@@ static void dump_irqs(unsigned char key) for ( i = 0; i < action->nr_guests; i++ ) { + struct evtchn *evtchn; + unsigned int pending = 2, masked = 2; + d = action->guest[i]; pirq = domain_irq_to_pirq(d, irq); info = pirq_info(d, pirq); + evtchn = evtchn_from_port(d, info->evtchn); + local_irq_disable(); + if ( spin_trylock(&evtchn->lock) ) + { + pending = evtchn_is_pending(d, evtchn); + masked = evtchn_is_masked(d, evtchn); + spin_unlock(&evtchn->lock); + } + local_irq_enable(); printk("%u:%3d(%c%c%c)", - d->domain_id, pirq, - evtchn_port_is_pending(d, info->evtchn) ? 'P' : '-', - evtchn_port_is_masked(d, info->evtchn) ? 'M' : '-', - (info->masked ? 'M' : '-')); + d->domain_id, pirq, "-P?"[pending], + "-M?"[masked], info->masked ? 'M' : '-'); if ( i != action->nr_guests ) printk(","); } --- xen/arch/x86/pv/shim.c.orig +++ xen/arch/x86/pv/shim.c @@@@ -616,8 +616,11 @@@@ void pv_shim_inject_evtchn(unsigned int if ( port_is_valid(guest, port) ) { struct evtchn *chn = evtchn_from_port(guest, port); + unsigned long flags; + spin_lock_irqsave(&chn->lock, flags); evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); + spin_unlock_irqrestore(&chn->lock, flags); } } --- xen/common/event_2l.c.orig +++ xen/common/event_2l.c @@@@ -63,8 +63,10 @@@@ static void evtchn_2l_unmask(struct doma } } -static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) +static bool evtchn_2l_is_pending(const struct domain *d, + const struct evtchn *evtchn) { + evtchn_port_t port = evtchn->port; unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); ASSERT(port < max_ports); @@@@ -72,8 +74,10 @@@@ static bool evtchn_2l_is_pending(const s guest_test_bit(d, port, &shared_info(d, evtchn_pending))); } -static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) +static bool evtchn_2l_is_masked(const struct domain *d, + const struct evtchn *evtchn) { + evtchn_port_t port = evtchn->port; unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); ASSERT(port < max_ports); --- xen/common/event_channel.c.orig +++ xen/common/event_channel.c @@@@ -156,8 +156,9 @@@@ int evtchn_allocate_port(struct domain * if ( port_is_valid(d, port) ) { - if ( evtchn_from_port(d, port)->state != ECS_FREE || - evtchn_port_is_busy(d, port) ) + const struct evtchn *chn = evtchn_from_port(d, port); + + if ( chn->state != ECS_FREE || evtchn_is_busy(d, chn) ) return -EBUSY; } else @@@@ -770,6 +771,7 @@@@ void send_guest_vcpu_virq(struct vcpu *v unsigned long flags; int port; struct domain *d; + struct evtchn *chn; ASSERT(!virq_is_global(virq)); @@@@ -780,7 +782,10 @@@@ void send_guest_vcpu_virq(struct vcpu *v goto out; d = v->domain; - evtchn_port_set_pending(d, v->vcpu_id, evtchn_from_port(d, port)); + chn = evtchn_from_port(d, port); + spin_lock(&chn->lock); + evtchn_port_set_pending(d, v->vcpu_id, chn); + spin_unlock(&chn->lock); out: spin_unlock_irqrestore(&v->virq_lock, flags); @@@@ -809,7 +814,9 @@@@ static void send_guest_global_virq(struc goto out; chn = evtchn_from_port(d, port); + spin_lock(&chn->lock); evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); + spin_unlock(&chn->lock); out: spin_unlock_irqrestore(&v->virq_lock, flags); @@@@ -819,6 +826,7 @@@@ void send_guest_pirq(struct domain *d, c { int port; struct evtchn *chn; + unsigned long flags; /* * PV guests: It should not be possible to race with __evtchn_close(). The @@@@ -833,7 +841,9 @@@@ void send_guest_pirq(struct domain *d, c } chn = evtchn_from_port(d, port); + spin_lock_irqsave(&chn->lock, flags); evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); + spin_unlock_irqrestore(&chn->lock, flags); } static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; @@@@ -1028,12 +1038,15 @@@@ int evtchn_unmask(unsigned int port) { struct domain *d = current->domain; struct evtchn *evtchn; + unsigned long flags; if ( unlikely(!port_is_valid(d, port)) ) return -EINVAL; evtchn = evtchn_from_port(d, port); + spin_lock_irqsave(&evtchn->lock, flags); evtchn_port_unmask(d, evtchn); + spin_unlock_irqrestore(&evtchn->lock, flags); return 0; } @@@@ -1446,8 +1459,8 @@@@ static void domain_dump_evtchn_info(stru printk(" %4u [%d/%d/", port, - evtchn_port_is_pending(d, port), - evtchn_port_is_masked(d, port)); + evtchn_is_pending(d, chn), + evtchn_is_masked(d, chn)); evtchn_port_print_state(d, chn); printk("]: s=%d n=%d x=%d", chn->state, chn->notify_vcpu_id, chn->xen_consumer); --- xen/common/event_fifo.c.orig +++ xen/common/event_fifo.c @@@@ -295,23 +295,26 @@@@ static void evtchn_fifo_unmask(struct do evtchn_fifo_set_pending(v, evtchn); } -static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) +static bool evtchn_fifo_is_pending(const struct domain *d, + const struct evtchn *evtchn) { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); + const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); } -static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) +static bool_t evtchn_fifo_is_masked(const struct domain *d, + const struct evtchn *evtchn) { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); + const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); } -static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) +static bool_t evtchn_fifo_is_busy(const struct domain *d, + const struct evtchn *evtchn) { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); + const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); } --- xen/include/asm-x86/event.h.orig +++ xen/include/asm-x86/event.h @@@@ -47,4 +47,10 @@@@ static inline bool arch_virq_is_global(u return true; } +#ifdef CONFIG_PV_SHIM +# include +# define arch_evtchn_is_special(chn) \ + (pv_shim && (chn)->port && (chn)->state == ECS_RESERVED) +#endif + #endif --- xen/include/xen/event.h.orig +++ xen/include/xen/event.h @@@@ -125,6 +125,24 @@@@ static inline struct evtchn *evtchn_from return bucket_from_port(d, p) + (p % EVTCHNS_PER_BUCKET); } +/* + * "usable" as in "by a guest", i.e. Xen consumed channels are assumed to be + * taken care of separately where used for Xen's internal purposes. + */ +static bool evtchn_usable(const struct evtchn *evtchn) +{ + if ( evtchn->xen_consumer ) + return false; + +#ifdef arch_evtchn_is_special + if ( arch_evtchn_is_special(evtchn) ) + return true; +#endif + + BUILD_BUG_ON(ECS_FREE > ECS_RESERVED); + return evtchn->state > ECS_RESERVED; +} + /* Wait on a Xen-attached event channel. */ #define wait_on_xen_event_channel(port, condition) \ do { \ @@@@ -157,19 +175,24 @@@@ int evtchn_reset(struct domain *d); /* * Low-level event channel port ops. + * + * All hooks have to be called with a lock held which prevents the channel + * from changing state. This may be the domain event lock, the per-channel + * lock, or in the case of sending interdomain events also the other side's + * per-channel lock. Exceptions apply in certain cases for the PV shim. */ struct evtchn_port_ops { void (*init)(struct domain *d, struct evtchn *evtchn); void (*set_pending)(struct vcpu *v, struct evtchn *evtchn); void (*clear_pending)(struct domain *d, struct evtchn *evtchn); void (*unmask)(struct domain *d, struct evtchn *evtchn); - bool (*is_pending)(const struct domain *d, evtchn_port_t port); - bool (*is_masked)(const struct domain *d, evtchn_port_t port); + bool (*is_pending)(const struct domain *d, const struct evtchn *evtchn); + bool (*is_masked)(const struct domain *d, const struct evtchn *evtchn); /* * Is the port unavailable because it's still being cleaned up * after being closed? */ - bool (*is_busy)(const struct domain *d, evtchn_port_t port); + bool (*is_busy)(const struct domain *d, const struct evtchn *evtchn); int (*set_priority)(struct domain *d, struct evtchn *evtchn, unsigned int priority); void (*print_state)(struct domain *d, const struct evtchn *evtchn); @@@@ -185,38 +208,67 @@@@ static inline void evtchn_port_set_pendi unsigned int vcpu_id, struct evtchn *evtchn) { - d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); + if ( evtchn_usable(evtchn) ) + d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); } static inline void evtchn_port_clear_pending(struct domain *d, struct evtchn *evtchn) { - d->evtchn_port_ops->clear_pending(d, evtchn); + if ( evtchn_usable(evtchn) ) + d->evtchn_port_ops->clear_pending(d, evtchn); } static inline void evtchn_port_unmask(struct domain *d, struct evtchn *evtchn) { - d->evtchn_port_ops->unmask(d, evtchn); + if ( evtchn_usable(evtchn) ) + d->evtchn_port_ops->unmask(d, evtchn); } -static inline bool evtchn_port_is_pending(const struct domain *d, - evtchn_port_t port) +static inline bool evtchn_is_pending(const struct domain *d, + const struct evtchn *evtchn) { - return d->evtchn_port_ops->is_pending(d, port); + return evtchn_usable(evtchn) && d->evtchn_port_ops->is_pending(d, evtchn); } -static inline bool evtchn_port_is_masked(const struct domain *d, - evtchn_port_t port) +static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) { - return d->evtchn_port_ops->is_masked(d, port); + struct evtchn *evtchn = evtchn_from_port(d, port); + bool rc; + unsigned long flags; + + spin_lock_irqsave(&evtchn->lock, flags); + rc = evtchn_is_pending(d, evtchn); + spin_unlock_irqrestore(&evtchn->lock, flags); + + return rc; +} + +static inline bool evtchn_is_masked(const struct domain *d, + const struct evtchn *evtchn) +{ + return !evtchn_usable(evtchn) || d->evtchn_port_ops->is_masked(d, evtchn); +} + +static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) +{ + struct evtchn *evtchn = evtchn_from_port(d, port); + bool rc; + unsigned long flags; + + spin_lock_irqsave(&evtchn->lock, flags); + rc = evtchn_is_masked(d, evtchn); + spin_unlock_irqrestore(&evtchn->lock, flags); + + return rc; } -static inline bool evtchn_port_is_busy(const struct domain *d, - evtchn_port_t port) +static inline bool evtchn_is_busy(const struct domain *d, + const struct evtchn *evtchn) { return d->evtchn_port_ops->is_busy && - d->evtchn_port_ops->is_busy(d, port); + d->evtchn_port_ops->is_busy(d, evtchn); } static inline int evtchn_port_set_priority(struct domain *d, @@@@ -225,6 +277,8 @@@@ static inline int evtchn_port_set_priori { if ( !d->evtchn_port_ops->set_priority ) return -ENOSYS; + if ( !evtchn_usable(evtchn) ) + return -EACCES; return d->evtchn_port_ops->set_priority(d, evtchn, priority); } @ 1.1 log @dd uptream fixes for XSA333, XSA336, XSA337, XSA338, XSA339, XSA340, XSA342, XSA343, XSA344 bump PKGREVISION @ text @d1 1 a1 1 $NetBSD: $ @ 1.1.2.1 log @file patch-XSA343 was added on branch pkgsrc-2020Q3 on 2020-10-04 20:44:32 +0000 @ text @d1 863 @ 1.1.2.2 log @Pullup ticket #6332 - requested by bouyer sysutils/xenkernel411: security fix sysutils/xenkernel413: security fix Revisions pulled up: - sysutils/xenkernel411/Makefile 1.16 - sysutils/xenkernel411/distinfo 1.14 - sysutils/xenkernel411/patches/patch-XSA333 1.1 - sysutils/xenkernel411/patches/patch-XSA336 1.1 - sysutils/xenkernel411/patches/patch-XSA337 1.1 - sysutils/xenkernel411/patches/patch-XSA338 1.1 - sysutils/xenkernel411/patches/patch-XSA339 1.1 - sysutils/xenkernel411/patches/patch-XSA340 1.1 - sysutils/xenkernel411/patches/patch-XSA342 1.1 - sysutils/xenkernel411/patches/patch-XSA343 1.1 - sysutils/xenkernel411/patches/patch-XSA344 1.1 - sysutils/xenkernel413/Makefile 1.5 - sysutils/xenkernel413/distinfo 1.3 - sysutils/xenkernel413/patches/patch-XSA333 1.1 - sysutils/xenkernel413/patches/patch-XSA334 1.1 - sysutils/xenkernel413/patches/patch-XSA336 1.1 - sysutils/xenkernel413/patches/patch-XSA337 1.1 - sysutils/xenkernel413/patches/patch-XSA338 1.1 - sysutils/xenkernel413/patches/patch-XSA339 1.1 - sysutils/xenkernel413/patches/patch-XSA340 1.1 - sysutils/xenkernel413/patches/patch-XSA342 1.1 - sysutils/xenkernel413/patches/patch-XSA343 1.1 - sysutils/xenkernel413/patches/patch-XSA344 1.1 --- Module Name: pkgsrc Committed By: bouyer Date: Thu Oct 1 12:41:19 UTC 2020 Modified Files: pkgsrc/sysutils/xenkernel413: Makefile distinfo Added Files: pkgsrc/sysutils/xenkernel413/patches: patch-XSA333 patch-XSA334 patch-XSA336 patch-XSA337 patch-XSA338 patch-XSA339 patch-XSA340 patch-XSA342 patch-XSA343 patch-XSA344 Log Message: Add uptream fixes for XSA333, XSA334, XSA336, XSA337, XSA338, XSA339, XSA340, XSA342, XSA343, XSA344 bump PKGREVISION --- Module Name: pkgsrc Committed By: bouyer Date: Fri Oct 2 13:00:48 UTC 2020 Modified Files: pkgsrc/sysutils/xenkernel411: Makefile distinfo Added Files: pkgsrc/sysutils/xenkernel411/patches: patch-XSA333 patch-XSA336 patch-XSA337 patch-XSA338 patch-XSA339 patch-XSA340 patch-XSA342 patch-XSA343 patch-XSA344 Log Message: dd uptream fixes for XSA333, XSA336, XSA337, XSA338, XSA339, XSA340, XSA342, XSA343, XSA344 bump PKGREVISION @ text @a0 863 $NetBSD: patch-XSA343,v 1.1 2020/10/02 13:00:48 bouyer Exp $ From: Jan Beulich Subject: evtchn: evtchn_reset() shouldn't succeed with still-open ports While the function closes all ports, it does so without holding any lock, and hence racing requests may be issued causing new ports to get opened. This would have been problematic in particular if such a newly opened port had a port number above the new implementation limit (i.e. when switching from FIFO to 2-level) after the reset, as prior to "evtchn: relax port_is_valid()" this could have led to e.g. evtchn_close()'s "BUG_ON(!port_is_valid(d2, port2))" to trigger. Introduce a counter of active ports and check that it's (still) no larger then the number of Xen internally used ones after obtaining the necessary lock in evtchn_reset(). As to the access model of the new {active,xen}_evtchns fields - while all writes get done using write_atomic(), reads ought to use read_atomic() only when outside of a suitably locked region. Note that as of now evtchn_bind_virq() and evtchn_bind_ipi() don't have a need to call check_free_port(). This is part of XSA-343. Signed-off-by: Jan Beulich Reviewed-by: Stefano Stabellini Reviewed-by: Julien Grall --- xen/common/event_channel.c.orig +++ xen/common/event_channel.c @@@@ -188,6 +188,8 @@@@ int evtchn_allocate_port(struct domain * write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); } + write_atomic(&d->active_evtchns, d->active_evtchns + 1); + return 0; } @@@@ -211,11 +213,26 @@@@ static int get_free_port(struct domain * return -ENOSPC; } +/* + * Check whether a port is still marked free, and if so update the domain + * counter accordingly. To be used on function exit paths. + */ +static void check_free_port(struct domain *d, evtchn_port_t port) +{ + if ( port_is_valid(d, port) && + evtchn_from_port(d, port)->state == ECS_FREE ) + write_atomic(&d->active_evtchns, d->active_evtchns - 1); +} + void evtchn_free(struct domain *d, struct evtchn *chn) { /* Clear pending event to avoid unexpected behavior on re-bind. */ evtchn_port_clear_pending(d, chn); + if ( consumer_is_xen(chn) ) + write_atomic(&d->xen_evtchns, d->xen_evtchns - 1); + write_atomic(&d->active_evtchns, d->active_evtchns - 1); + /* Reset binding to vcpu0 when the channel is freed. */ chn->state = ECS_FREE; chn->notify_vcpu_id = 0; @@@@ -258,6 +275,7 @@@@ static long evtchn_alloc_unbound(evtchn_ alloc->port = port; out: + check_free_port(d, port); spin_unlock(&d->event_lock); rcu_unlock_domain(d); @@@@ -351,6 +369,7 @@@@ static long evtchn_bind_interdomain(evtc bind->local_port = lport; out: + check_free_port(ld, lport); spin_unlock(&ld->event_lock); if ( ld != rd ) spin_unlock(&rd->event_lock); @@@@ -484,7 +503,7 @@@@ static long evtchn_bind_pirq(evtchn_bind struct domain *d = current->domain; struct vcpu *v = d->vcpu[0]; struct pirq *info; - int port, pirq = bind->pirq; + int port = 0, pirq = bind->pirq; long rc; if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) @@@@ -532,6 +551,7 @@@@ static long evtchn_bind_pirq(evtchn_bind arch_evtchn_bind_pirq(d, pirq); out: + check_free_port(d, port); spin_unlock(&d->event_lock); return rc; @@@@ -1005,10 +1025,10 @@@@ int evtchn_unmask(unsigned int port) return 0; } - int evtchn_reset(struct domain *d) { unsigned int i; + int rc = 0; if ( d != current->domain && !d->controller_pause_count ) return -EINVAL; @@@@ -1018,7 +1038,9 @@@@ int evtchn_reset(struct domain *d) spin_lock(&d->event_lock); - if ( d->evtchn_fifo ) + if ( d->active_evtchns > d->xen_evtchns ) + rc = -EAGAIN; + else if ( d->evtchn_fifo ) { /* Switching back to 2-level ABI. */ evtchn_fifo_destroy(d); @@@@ -1027,7 +1049,7 @@@@ int evtchn_reset(struct domain *d) spin_unlock(&d->event_lock); - return 0; + return rc; } static long evtchn_set_priority(const struct evtchn_set_priority *set_priority) @@@@ -1213,10 +1235,9 @@@@ int alloc_unbound_xen_event_channel( spin_lock(&ld->event_lock); - rc = get_free_port(ld); + port = rc = get_free_port(ld); if ( rc < 0 ) goto out; - port = rc; chn = evtchn_from_port(ld, port); rc = xsm_evtchn_unbound(XSM_TARGET, ld, chn, remote_domid); @@@@ -1232,7 +1253,10 @@@@ int alloc_unbound_xen_event_channel( spin_unlock(&chn->lock); + write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); + out: + check_free_port(ld, port); spin_unlock(&ld->event_lock); return rc < 0 ? rc : port; @@@@ -1308,6 +1332,7 @@@@ int evtchn_init(struct domain *d) return -EINVAL; } evtchn_from_port(d, 0)->state = ECS_RESERVED; + write_atomic(&d->active_evtchns, 0); #if MAX_VIRT_CPUS > BITS_PER_LONG d->poll_mask = xzalloc_array(unsigned long, @@@@ -1335,6 +1360,8 @@@@ void evtchn_destroy(struct domain *d) for ( i = 0; port_is_valid(d, i); i++ ) evtchn_close(d, i, 0); + ASSERT(!d->active_evtchns); + clear_global_virq_handlers(d); evtchn_fifo_destroy(d); --- xen/include/xen/sched.h.orig +++ xen/include/xen/sched.h @@@@ -345,6 +345,16 @@@@ struct domain struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ unsigned int max_evtchn_port; /* max permitted port number */ unsigned int valid_evtchns; /* number of allocated event channels */ + /* + * Number of in-use event channels. Writers should use write_atomic(). + * Readers need to use read_atomic() only when not holding event_lock. + */ + unsigned int active_evtchns; + /* + * Number of event channels used internally by Xen (not subject to + * EVTCHNOP_reset). Read/write access like for active_evtchns. + */ + unsigned int xen_evtchns; spinlock_t event_lock; const struct evtchn_port_ops *evtchn_port_ops; struct evtchn_fifo_domain *evtchn_fifo; From: Jan Beulich Subject: evtchn: convert per-channel lock to be IRQ-safe ... in order for send_guest_{global,vcpu}_virq() to be able to make use of it. This is part of XSA-343. Signed-off-by: Jan Beulich Acked-by: Julien Grall --- xen/common/event_channel.c.orig +++ xen/common/event_channel.c @@@@ -248,6 +248,7 @@@@ static long evtchn_alloc_unbound(evtchn_ int port; domid_t dom = alloc->dom; long rc; + unsigned long flags; d = rcu_lock_domain_by_any_id(dom); if ( d == NULL ) @@@@ -263,14 +264,14 @@@@ static long evtchn_alloc_unbound(evtchn_ if ( rc ) goto out; - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_UNBOUND; if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) chn->u.unbound.remote_domid = current->domain->domain_id; evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); alloc->port = port; @@@@ -283,26 +284,32 @@@@ static long evtchn_alloc_unbound(evtchn_ } -static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) +static unsigned long double_evtchn_lock(struct evtchn *lchn, + struct evtchn *rchn) { - if ( lchn < rchn ) + unsigned long flags; + + if ( lchn <= rchn ) { - spin_lock(&lchn->lock); - spin_lock(&rchn->lock); + spin_lock_irqsave(&lchn->lock, flags); + if ( lchn != rchn ) + spin_lock(&rchn->lock); } else { - if ( lchn != rchn ) - spin_lock(&rchn->lock); + spin_lock_irqsave(&rchn->lock, flags); spin_lock(&lchn->lock); } + + return flags; } -static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) +static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, + unsigned long flags) { - spin_unlock(&lchn->lock); if ( lchn != rchn ) - spin_unlock(&rchn->lock); + spin_unlock(&lchn->lock); + spin_unlock_irqrestore(&rchn->lock, flags); } static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) @@@@ -312,6 +319,7 @@@@ static long evtchn_bind_interdomain(evtc int lport, rport = bind->remote_port; domid_t rdom = bind->remote_dom; long rc; + unsigned long flags; if ( rdom == DOMID_SELF ) rdom = current->domain->domain_id; @@@@ -347,7 +355,7 @@@@ static long evtchn_bind_interdomain(evtc if ( rc ) goto out; - double_evtchn_lock(lchn, rchn); + flags = double_evtchn_lock(lchn, rchn); lchn->u.interdomain.remote_dom = rd; lchn->u.interdomain.remote_port = rport; @@@@ -364,7 +372,7 @@@@ static long evtchn_bind_interdomain(evtc */ evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); - double_evtchn_unlock(lchn, rchn); + double_evtchn_unlock(lchn, rchn, flags); bind->local_port = lport; @@@@ -387,6 +395,7 @@@@ int evtchn_bind_virq(evtchn_bind_virq_t struct domain *d = current->domain; int virq = bind->virq, vcpu = bind->vcpu; int rc = 0; + unsigned long flags; if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) return -EINVAL; @@@@ -419,14 +428,14 @@@@ int evtchn_bind_virq(evtchn_bind_virq_t chn = evtchn_from_port(d, port); - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_VIRQ; chn->notify_vcpu_id = vcpu; chn->u.virq = virq; evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); v->virq_to_evtchn[virq] = bind->port = port; @@@@ -443,6 +452,7 @@@@ static long evtchn_bind_ipi(evtchn_bind_ struct domain *d = current->domain; int port, vcpu = bind->vcpu; long rc = 0; + unsigned long flags; if ( (vcpu < 0) || (vcpu >= d->max_vcpus) || (d->vcpu[vcpu] == NULL) ) @@@@ -455,13 +465,13 @@@@ static long evtchn_bind_ipi(evtchn_bind_ chn = evtchn_from_port(d, port); - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_IPI; chn->notify_vcpu_id = vcpu; evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); bind->port = port; @@@@ -505,6 +515,7 @@@@ static long evtchn_bind_pirq(evtchn_bind struct pirq *info; int port = 0, pirq = bind->pirq; long rc; + unsigned long flags; if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) return -EINVAL; @@@@ -537,14 +548,14 @@@@ static long evtchn_bind_pirq(evtchn_bind goto out; } - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_PIRQ; chn->u.pirq.irq = pirq; link_pirq_port(port, chn, v); evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); bind->port = port; @@@@ -565,6 +576,7 @@@@ int evtchn_close(struct domain *d1, int struct evtchn *chn1, *chn2; int port2; long rc = 0; + unsigned long flags; again: spin_lock(&d1->event_lock); @@@@ -664,14 +676,14 @@@@ int evtchn_close(struct domain *d1, int BUG_ON(chn2->state != ECS_INTERDOMAIN); BUG_ON(chn2->u.interdomain.remote_dom != d1); - double_evtchn_lock(chn1, chn2); + flags = double_evtchn_lock(chn1, chn2); evtchn_free(d1, chn1); chn2->state = ECS_UNBOUND; chn2->u.unbound.remote_domid = d1->domain_id; - double_evtchn_unlock(chn1, chn2); + double_evtchn_unlock(chn1, chn2, flags); goto out; @@@@ -679,9 +691,9 @@@@ int evtchn_close(struct domain *d1, int BUG(); } - spin_lock(&chn1->lock); + spin_lock_irqsave(&chn1->lock, flags); evtchn_free(d1, chn1); - spin_unlock(&chn1->lock); + spin_unlock_irqrestore(&chn1->lock, flags); out: if ( d2 != NULL ) @@@@ -701,13 +713,14 @@@@ int evtchn_send(struct domain *ld, unsig struct evtchn *lchn, *rchn; struct domain *rd; int rport, ret = 0; + unsigned long flags; if ( !port_is_valid(ld, lport) ) return -EINVAL; lchn = evtchn_from_port(ld, lport); - spin_lock(&lchn->lock); + spin_lock_irqsave(&lchn->lock, flags); /* Guest cannot send via a Xen-attached event channel. */ if ( unlikely(consumer_is_xen(lchn)) ) @@@@ -742,7 +755,7 @@@@ int evtchn_send(struct domain *ld, unsig } out: - spin_unlock(&lchn->lock); + spin_unlock_irqrestore(&lchn->lock, flags); return ret; } @@@@ -1232,6 +1245,7 @@@@ int alloc_unbound_xen_event_channel( { struct evtchn *chn; int port, rc; + unsigned long flags; spin_lock(&ld->event_lock); @@@@ -1244,14 +1258,14 @@@@ int alloc_unbound_xen_event_channel( if ( rc ) goto out; - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_UNBOUND; chn->xen_consumer = get_xen_consumer(notification_fn); chn->notify_vcpu_id = lvcpu; chn->u.unbound.remote_domid = remote_domid; - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); @@@@ -1274,11 +1288,12 @@@@ void notify_via_xen_event_channel(struct { struct evtchn *lchn, *rchn; struct domain *rd; + unsigned long flags; ASSERT(port_is_valid(ld, lport)); lchn = evtchn_from_port(ld, lport); - spin_lock(&lchn->lock); + spin_lock_irqsave(&lchn->lock, flags); if ( likely(lchn->state == ECS_INTERDOMAIN) ) { @@@@ -1288,7 +1303,7 @@@@ void notify_via_xen_event_channel(struct evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); } - spin_unlock(&lchn->lock); + spin_unlock_irqrestore(&lchn->lock, flags); } void evtchn_check_pollers(struct domain *d, unsigned int port) From: Jan Beulich Subject: evtchn: address races with evtchn_reset() Neither d->evtchn_port_ops nor max_evtchns(d) may be used in an entirely lock-less manner, as both may change by a racing evtchn_reset(). In the common case, at least one of the domain's event lock or the per-channel lock needs to be held. In the specific case of the inter-domain sending by evtchn_send() and notify_via_xen_event_channel() holding the other side's per-channel lock is sufficient, as the channel can't change state without both per-channel locks held. Without such a channel changing state, evtchn_reset() can't complete successfully. Lock-free accesses continue to be permitted for the shim (calling some otherwise internal event channel functions), as this happens while the domain is in effectively single-threaded mode. Special care also needs taking for the shim's marking of in-use ports as ECS_RESERVED (allowing use of such ports in the shim case is okay because switching into and hence also out of FIFO mode is impossible there). As a side effect, certain operations on Xen bound event channels which were mistakenly permitted so far (e.g. unmask or poll) will be refused now. This is part of XSA-343. Reported-by: Julien Grall Signed-off-by: Jan Beulich Acked-by: Julien Grall --- xen/arch/x86/irq.c.orig +++ xen/arch/x86/irq.c @@@@ -2367,14 +2367,24 @@@@ static void dump_irqs(unsigned char key) for ( i = 0; i < action->nr_guests; i++ ) { + struct evtchn *evtchn; + unsigned int pending = 2, masked = 2; + d = action->guest[i]; pirq = domain_irq_to_pirq(d, irq); info = pirq_info(d, pirq); + evtchn = evtchn_from_port(d, info->evtchn); + local_irq_disable(); + if ( spin_trylock(&evtchn->lock) ) + { + pending = evtchn_is_pending(d, evtchn); + masked = evtchn_is_masked(d, evtchn); + spin_unlock(&evtchn->lock); + } + local_irq_enable(); printk("%u:%3d(%c%c%c)", - d->domain_id, pirq, - evtchn_port_is_pending(d, info->evtchn) ? 'P' : '-', - evtchn_port_is_masked(d, info->evtchn) ? 'M' : '-', - (info->masked ? 'M' : '-')); + d->domain_id, pirq, "-P?"[pending], + "-M?"[masked], info->masked ? 'M' : '-'); if ( i != action->nr_guests ) printk(","); } --- xen/arch/x86/pv/shim.c.orig +++ xen/arch/x86/pv/shim.c @@@@ -616,8 +616,11 @@@@ void pv_shim_inject_evtchn(unsigned int if ( port_is_valid(guest, port) ) { struct evtchn *chn = evtchn_from_port(guest, port); + unsigned long flags; + spin_lock_irqsave(&chn->lock, flags); evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); + spin_unlock_irqrestore(&chn->lock, flags); } } --- xen/common/event_2l.c.orig +++ xen/common/event_2l.c @@@@ -63,8 +63,10 @@@@ static void evtchn_2l_unmask(struct doma } } -static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) +static bool evtchn_2l_is_pending(const struct domain *d, + const struct evtchn *evtchn) { + evtchn_port_t port = evtchn->port; unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); ASSERT(port < max_ports); @@@@ -72,8 +74,10 @@@@ static bool evtchn_2l_is_pending(const s guest_test_bit(d, port, &shared_info(d, evtchn_pending))); } -static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) +static bool evtchn_2l_is_masked(const struct domain *d, + const struct evtchn *evtchn) { + evtchn_port_t port = evtchn->port; unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); ASSERT(port < max_ports); --- xen/common/event_channel.c.orig +++ xen/common/event_channel.c @@@@ -156,8 +156,9 @@@@ int evtchn_allocate_port(struct domain * if ( port_is_valid(d, port) ) { - if ( evtchn_from_port(d, port)->state != ECS_FREE || - evtchn_port_is_busy(d, port) ) + const struct evtchn *chn = evtchn_from_port(d, port); + + if ( chn->state != ECS_FREE || evtchn_is_busy(d, chn) ) return -EBUSY; } else @@@@ -770,6 +771,7 @@@@ void send_guest_vcpu_virq(struct vcpu *v unsigned long flags; int port; struct domain *d; + struct evtchn *chn; ASSERT(!virq_is_global(virq)); @@@@ -780,7 +782,10 @@@@ void send_guest_vcpu_virq(struct vcpu *v goto out; d = v->domain; - evtchn_port_set_pending(d, v->vcpu_id, evtchn_from_port(d, port)); + chn = evtchn_from_port(d, port); + spin_lock(&chn->lock); + evtchn_port_set_pending(d, v->vcpu_id, chn); + spin_unlock(&chn->lock); out: spin_unlock_irqrestore(&v->virq_lock, flags); @@@@ -809,7 +814,9 @@@@ static void send_guest_global_virq(struc goto out; chn = evtchn_from_port(d, port); + spin_lock(&chn->lock); evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); + spin_unlock(&chn->lock); out: spin_unlock_irqrestore(&v->virq_lock, flags); @@@@ -819,6 +826,7 @@@@ void send_guest_pirq(struct domain *d, c { int port; struct evtchn *chn; + unsigned long flags; /* * PV guests: It should not be possible to race with __evtchn_close(). The @@@@ -833,7 +841,9 @@@@ void send_guest_pirq(struct domain *d, c } chn = evtchn_from_port(d, port); + spin_lock_irqsave(&chn->lock, flags); evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); + spin_unlock_irqrestore(&chn->lock, flags); } static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; @@@@ -1028,12 +1038,15 @@@@ int evtchn_unmask(unsigned int port) { struct domain *d = current->domain; struct evtchn *evtchn; + unsigned long flags; if ( unlikely(!port_is_valid(d, port)) ) return -EINVAL; evtchn = evtchn_from_port(d, port); + spin_lock_irqsave(&evtchn->lock, flags); evtchn_port_unmask(d, evtchn); + spin_unlock_irqrestore(&evtchn->lock, flags); return 0; } @@@@ -1446,8 +1459,8 @@@@ static void domain_dump_evtchn_info(stru printk(" %4u [%d/%d/", port, - evtchn_port_is_pending(d, port), - evtchn_port_is_masked(d, port)); + evtchn_is_pending(d, chn), + evtchn_is_masked(d, chn)); evtchn_port_print_state(d, chn); printk("]: s=%d n=%d x=%d", chn->state, chn->notify_vcpu_id, chn->xen_consumer); --- xen/common/event_fifo.c.orig +++ xen/common/event_fifo.c @@@@ -295,23 +295,26 @@@@ static void evtchn_fifo_unmask(struct do evtchn_fifo_set_pending(v, evtchn); } -static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) +static bool evtchn_fifo_is_pending(const struct domain *d, + const struct evtchn *evtchn) { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); + const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); } -static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) +static bool_t evtchn_fifo_is_masked(const struct domain *d, + const struct evtchn *evtchn) { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); + const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); } -static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) +static bool_t evtchn_fifo_is_busy(const struct domain *d, + const struct evtchn *evtchn) { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); + const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); } --- xen/include/asm-x86/event.h.orig +++ xen/include/asm-x86/event.h @@@@ -47,4 +47,10 @@@@ static inline bool arch_virq_is_global(u return true; } +#ifdef CONFIG_PV_SHIM +# include +# define arch_evtchn_is_special(chn) \ + (pv_shim && (chn)->port && (chn)->state == ECS_RESERVED) +#endif + #endif --- xen/include/xen/event.h.orig +++ xen/include/xen/event.h @@@@ -125,6 +125,24 @@@@ static inline struct evtchn *evtchn_from return bucket_from_port(d, p) + (p % EVTCHNS_PER_BUCKET); } +/* + * "usable" as in "by a guest", i.e. Xen consumed channels are assumed to be + * taken care of separately where used for Xen's internal purposes. + */ +static bool evtchn_usable(const struct evtchn *evtchn) +{ + if ( evtchn->xen_consumer ) + return false; + +#ifdef arch_evtchn_is_special + if ( arch_evtchn_is_special(evtchn) ) + return true; +#endif + + BUILD_BUG_ON(ECS_FREE > ECS_RESERVED); + return evtchn->state > ECS_RESERVED; +} + /* Wait on a Xen-attached event channel. */ #define wait_on_xen_event_channel(port, condition) \ do { \ @@@@ -157,19 +175,24 @@@@ int evtchn_reset(struct domain *d); /* * Low-level event channel port ops. + * + * All hooks have to be called with a lock held which prevents the channel + * from changing state. This may be the domain event lock, the per-channel + * lock, or in the case of sending interdomain events also the other side's + * per-channel lock. Exceptions apply in certain cases for the PV shim. */ struct evtchn_port_ops { void (*init)(struct domain *d, struct evtchn *evtchn); void (*set_pending)(struct vcpu *v, struct evtchn *evtchn); void (*clear_pending)(struct domain *d, struct evtchn *evtchn); void (*unmask)(struct domain *d, struct evtchn *evtchn); - bool (*is_pending)(const struct domain *d, evtchn_port_t port); - bool (*is_masked)(const struct domain *d, evtchn_port_t port); + bool (*is_pending)(const struct domain *d, const struct evtchn *evtchn); + bool (*is_masked)(const struct domain *d, const struct evtchn *evtchn); /* * Is the port unavailable because it's still being cleaned up * after being closed? */ - bool (*is_busy)(const struct domain *d, evtchn_port_t port); + bool (*is_busy)(const struct domain *d, const struct evtchn *evtchn); int (*set_priority)(struct domain *d, struct evtchn *evtchn, unsigned int priority); void (*print_state)(struct domain *d, const struct evtchn *evtchn); @@@@ -185,38 +208,67 @@@@ static inline void evtchn_port_set_pendi unsigned int vcpu_id, struct evtchn *evtchn) { - d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); + if ( evtchn_usable(evtchn) ) + d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); } static inline void evtchn_port_clear_pending(struct domain *d, struct evtchn *evtchn) { - d->evtchn_port_ops->clear_pending(d, evtchn); + if ( evtchn_usable(evtchn) ) + d->evtchn_port_ops->clear_pending(d, evtchn); } static inline void evtchn_port_unmask(struct domain *d, struct evtchn *evtchn) { - d->evtchn_port_ops->unmask(d, evtchn); + if ( evtchn_usable(evtchn) ) + d->evtchn_port_ops->unmask(d, evtchn); } -static inline bool evtchn_port_is_pending(const struct domain *d, - evtchn_port_t port) +static inline bool evtchn_is_pending(const struct domain *d, + const struct evtchn *evtchn) { - return d->evtchn_port_ops->is_pending(d, port); + return evtchn_usable(evtchn) && d->evtchn_port_ops->is_pending(d, evtchn); } -static inline bool evtchn_port_is_masked(const struct domain *d, - evtchn_port_t port) +static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) { - return d->evtchn_port_ops->is_masked(d, port); + struct evtchn *evtchn = evtchn_from_port(d, port); + bool rc; + unsigned long flags; + + spin_lock_irqsave(&evtchn->lock, flags); + rc = evtchn_is_pending(d, evtchn); + spin_unlock_irqrestore(&evtchn->lock, flags); + + return rc; +} + +static inline bool evtchn_is_masked(const struct domain *d, + const struct evtchn *evtchn) +{ + return !evtchn_usable(evtchn) || d->evtchn_port_ops->is_masked(d, evtchn); +} + +static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) +{ + struct evtchn *evtchn = evtchn_from_port(d, port); + bool rc; + unsigned long flags; + + spin_lock_irqsave(&evtchn->lock, flags); + rc = evtchn_is_masked(d, evtchn); + spin_unlock_irqrestore(&evtchn->lock, flags); + + return rc; } -static inline bool evtchn_port_is_busy(const struct domain *d, - evtchn_port_t port) +static inline bool evtchn_is_busy(const struct domain *d, + const struct evtchn *evtchn) { return d->evtchn_port_ops->is_busy && - d->evtchn_port_ops->is_busy(d, port); + d->evtchn_port_ops->is_busy(d, evtchn); } static inline int evtchn_port_set_priority(struct domain *d, @@@@ -225,6 +277,8 @@@@ static inline int evtchn_port_set_priori { if ( !d->evtchn_port_ops->set_priority ) return -ENOSYS; + if ( !evtchn_usable(evtchn) ) + return -EACCES; return d->evtchn_port_ops->set_priority(d, evtchn, priority); } @