[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [xen staging] evtchn/x86: enforce correct upper limit for 32-bit guests
commit 62bcdc4edbf6d8c6e8a25544d48de22ccf75310d Author: Jan Beulich <jbeulich@xxxxxxxx> AuthorDate: Tue Sep 22 15:50:09 2020 +0200 Commit: Jan Beulich <jbeulich@xxxxxxxx> CommitDate: Tue Sep 22 15:50:09 2020 +0200 evtchn/x86: enforce correct upper limit for 32-bit guests The recording of d->max_evtchns in evtchn_2l_init(), in particular with the limited set of callers of the function, is insufficient. Neither for PV nor for HVM guests the bitness is known at domain_create() time, yet the upper bound in 2-level mode depends upon guest bitness. Recording too high a limit "allows" x86 32-bit domains to open not properly usable event channels, management of which (inside Xen) would then result in corruption of the shared info and vCPU info structures. Keep the upper limit dynamic for the 2-level case, introducing a helper function to retrieve the effective limit. This helper is now supposed to be private to the event channel code. The used in do_poll() and domain_dump_evtchn_info() weren't consistent with port uses elsewhere and hence get switched to port_is_valid(). Furthermore FIFO mode's setup_ports() gets adjusted to loop only up to the prior ABI limit, rather than all the way up to the new one. Finally a word on the change to do_poll(): Accessing ->max_evtchns without holding a suitable lock was never safe, as it as well as ->evtchn_port_ops may change behind do_poll()'s back. Using port_is_valid() instead widens some the window for potential abuse, until we've dealt with the race altogether (see XSA-343). This is XSA-342. Reported-by: Julien Grall <jgrall@xxxxxxxxxx> Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx> Reviewed-by: Stefano Stabellini <sstabellini@xxxxxxxxxx> Reviewed-by: Julien Grall <jgrall@xxxxxxxxxx> --- xen/common/event_2l.c | 1 - xen/common/event_channel.c | 6 ++---- xen/common/event_fifo.c | 9 +++++---- xen/common/sched/core.c | 2 +- xen/include/xen/event.h | 6 ++++++ xen/include/xen/sched.h | 1 - 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/xen/common/event_2l.c b/xen/common/event_2l.c index e1dbb860f4..a229d35271 100644 --- a/xen/common/event_2l.c +++ b/xen/common/event_2l.c @@ -103,7 +103,6 @@ static const struct evtchn_port_ops evtchn_port_ops_2l = void evtchn_2l_init(struct domain *d) { d->evtchn_port_ops = &evtchn_port_ops_2l; - d->max_evtchns = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); } /* diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c index 53c17bd354..08ffe0f063 100644 --- a/xen/common/event_channel.c +++ b/xen/common/event_channel.c @@ -151,7 +151,7 @@ static void free_evtchn_bucket(struct domain *d, struct evtchn *bucket) int evtchn_allocate_port(struct domain *d, evtchn_port_t port) { - if ( port > d->max_evtchn_port || port >= d->max_evtchns ) + if ( port > d->max_evtchn_port || port >= max_evtchns(d) ) return -ENOSPC; if ( port_is_valid(d, port) ) @@ -1396,13 +1396,11 @@ static void domain_dump_evtchn_info(struct domain *d) spin_lock(&d->event_lock); - for ( port = 1; port < d->max_evtchns; ++port ) + for ( port = 1; port_is_valid(d, port); ++port ) { const struct evtchn *chn; char *ssid; - if ( !port_is_valid(d, port) ) - continue; chn = evtchn_from_port(d, port); if ( chn->state == ECS_FREE ) continue; diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c index 230f440f14..2f13d92128 100644 --- a/xen/common/event_fifo.c +++ b/xen/common/event_fifo.c @@ -478,7 +478,7 @@ static void cleanup_event_array(struct domain *d) d->evtchn_fifo = NULL; } -static void setup_ports(struct domain *d) +static void setup_ports(struct domain *d, unsigned int prev_evtchns) { unsigned int port; @@ -488,7 +488,7 @@ static void setup_ports(struct domain *d) * - save its pending state. * - set default priority. */ - for ( port = 1; port < d->max_evtchns; port++ ) + for ( port = 1; port < prev_evtchns; port++ ) { struct evtchn *evtchn; @@ -546,6 +546,8 @@ int evtchn_fifo_init_control(struct evtchn_init_control *init_control) if ( !d->evtchn_fifo ) { struct vcpu *vcb; + /* Latch the value before it changes during setup_event_array(). */ + unsigned int prev_evtchns = max_evtchns(d); for_each_vcpu ( d, vcb ) { rc = setup_control_block(vcb); @@ -562,8 +564,7 @@ int evtchn_fifo_init_control(struct evtchn_init_control *init_control) goto error; d->evtchn_port_ops = &evtchn_port_ops_fifo; - d->max_evtchns = EVTCHN_FIFO_NR_CHANNELS; - setup_ports(d); + setup_ports(d, prev_evtchns); } else rc = map_control_block(v, gfn, offset); diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c index cb49a8bc02..ab94d2ec3a 100644 --- a/xen/common/sched/core.c +++ b/xen/common/sched/core.c @@ -1428,7 +1428,7 @@ static long do_poll(struct sched_poll *sched_poll) goto out; rc = -EINVAL; - if ( port >= d->max_evtchns ) + if ( !port_is_valid(d, port) ) goto out; rc = 0; diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h index c35f4b23b6..e1b299e8df 100644 --- a/xen/include/xen/event.h +++ b/xen/include/xen/event.h @@ -105,6 +105,12 @@ void notify_via_xen_event_channel(struct domain *ld, int lport); #define bucket_from_port(d, p) \ ((group_from_port(d, p))[((p) % EVTCHNS_PER_GROUP) / EVTCHNS_PER_BUCKET]) +static inline unsigned int max_evtchns(const struct domain *d) +{ + return d->evtchn_fifo ? EVTCHN_FIFO_NR_CHANNELS + : BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); +} + static inline bool_t port_is_valid(struct domain *d, unsigned int p) { if ( p >= read_atomic(&d->valid_evtchns) ) diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h index e9ce43cc9a..33d716ef77 100644 --- a/xen/include/xen/sched.h +++ b/xen/include/xen/sched.h @@ -359,7 +359,6 @@ struct domain /* Event channel information. */ struct evtchn *evtchn; /* first bucket only */ struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ - unsigned int max_evtchns; /* number supported by ABI */ unsigned int max_evtchn_port; /* max permitted port number */ unsigned int valid_evtchns; /* number of allocated event channels */ spinlock_t event_lock; -- generated by git-patchbot for /home/xen/git/xen.git#staging
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |