x86/HVM: add locking to I/O port translation list traversal

XEN_DOMCTL_ioport_mapping is usable by DM stubdoms, and hence we can't
assume the list to be left unaltered while the guest (really: the
hypervisor on behalf of the guest) is accessing it.

This is XSA-491 / CVE-2026-42487.

Fixes: 192c4dabc3 ("domctl and p2m changes for PCI passthru")
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
(cherry picked from commit 7787f8e939)
This commit is contained in:
Jan Beulich authored and Andrew Cooper committed 2026-06-04 21:45:56 +01:00
1 parent caf651fdbc
commit 4c11789f6e
6 files changed
+61 -24

No files matched your search

+8
View File
@@ -620,6 +620,7 @@ long arch_do_domctl(
"ioport_map:add: dom%d gport=%x mport=%x nr=%x\n",
d->domain_id, fgp, fmp, np);
write_lock(&hvm->g2m_ioport_lock);
list_for_each_entry(g2m_ioport, &hvm->g2m_ioport_list, list)
if (g2m_ioport->mport == fmp )
{
@@ -641,11 +642,14 @@ long arch_do_domctl(
g2m_ioport->np = np;
list_add_tail(&g2m_ioport->list, &hvm->g2m_ioport_list);
}
write_unlock(&hvm->g2m_ioport_lock);
if ( !ret )
ret = ioports_permit_access(d, fmp, fmp + np - 1);
if ( ret && !found && g2m_ioport )
{
write_lock(&hvm->g2m_ioport_lock);
list_del(&g2m_ioport->list);
write_unlock(&hvm->g2m_ioport_lock);
xfree(g2m_ioport);
}
}
@@ -654,6 +658,8 @@ long arch_do_domctl(
printk(XENLOG_G_INFO
"ioport_map:remove: dom%d gport=%x mport=%x nr=%x\n",
d->domain_id, fgp, fmp, np);
write_lock(&hvm->g2m_ioport_lock);
list_for_each_entry(g2m_ioport, &hvm->g2m_ioport_list, list)
if ( g2m_ioport->mport == fmp )
{
@@ -661,6 +667,8 @@ long arch_do_domctl(
xfree(g2m_ioport);
break;
}
write_unlock(&hvm->g2m_ioport_lock);
ret = ioports_deny_access(d, fmp, fmp + np - 1);
if ( ret && is_hardware_domain(currd) )
printk(XENLOG_ERR
-1
View File
@@ -145,7 +145,6 @@ void hvmemul_cancel(struct vcpu *v)
hvio->mmio_insn_bytes = 0;
hvio->mmio_access = (struct npfec){};
hvio->mmio_retry = false;
hvio->g2m_ioport = NULL;
hvmemul_cache_disable(v);
}
+1
View File
@@ -588,6 +588,7 @@ int hvm_domain_initialise(struct domain *d,
spin_lock_init(&d->arch.hvm.irq_lock);
spin_lock_init(&d->arch.hvm.uc_lock);
spin_lock_init(&d->arch.hvm.write_map.lock);
rwlock_init(&d->arch.hvm.g2m_ioport_lock);
rwlock_init(&d->arch.hvm.mmcfg_lock);
INIT_LIST_HEAD(&d->arch.hvm.write_map.list);
INIT_LIST_HEAD(&d->arch.hvm.g2m_ioport_list);
+51 -21
View File
@@ -157,36 +157,56 @@ bool handle_pio(uint16_t port, unsigned int size, int dir)
return true;
}
static bool cf_check g2m_portio_accept(
const struct hvm_io_handler *handler, const ioreq_t *p)
/* NB: Returns with the lock held in the success case. */
static const struct g2m_ioport *g2m_portio_find_and_lock(struct hvm_domain *hvm,
uint64_t addr,
uint32_t size)
{
struct vcpu *curr = current;
const struct hvm_domain *hvm = &curr->domain->arch.hvm;
struct hvm_vcpu_io *hvio = &curr->arch.hvm.hvm_io;
struct g2m_ioport *g2m_ioport;
unsigned int start, end;
const struct g2m_ioport *g2m_ioport;
read_lock(&hvm->g2m_ioport_lock);
list_for_each_entry( g2m_ioport, &hvm->g2m_ioport_list, list )
{
start = g2m_ioport->gport;
end = start + g2m_ioport->np;
if ( (p->addr >= start) && (p->addr + p->size <= end) )
{
hvio->g2m_ioport = g2m_ioport;
return 1;
}
unsigned int start = g2m_ioport->gport;
if ( addr >= start && addr + size <= start + g2m_ioport->np )
return g2m_ioport;
}
return 0;
read_unlock(&hvm->g2m_ioport_lock);
return NULL;
}
static bool cf_check g2m_portio_accept(
const struct hvm_io_handler *handler, const ioreq_t *p)
{
struct hvm_domain *hvm = &current->domain->arch.hvm;
const struct g2m_ioport *g2m_ioport =
g2m_portio_find_and_lock(hvm, p->addr, p->size);
if ( !g2m_ioport )
return false;
read_unlock(&hvm->g2m_ioport_lock);
return true;
}
static int cf_check g2m_portio_read(
const struct hvm_io_handler *handler, uint64_t addr, uint32_t size,
uint64_t *data)
{
struct hvm_vcpu_io *hvio = &current->arch.hvm.hvm_io;
const struct g2m_ioport *g2m_ioport = hvio->g2m_ioport;
unsigned int mport = (addr - g2m_ioport->gport) + g2m_ioport->mport;
struct hvm_domain *hvm = &current->domain->arch.hvm;
const struct g2m_ioport *g2m_ioport =
g2m_portio_find_and_lock(hvm, addr, size);
unsigned int mport;
if ( !g2m_ioport )
return X86EMUL_RETRY;
mport = addr - g2m_ioport->gport + g2m_ioport->mport;
switch ( size )
{
@@ -203,6 +223,8 @@ static int cf_check g2m_portio_read(
BUG();
}
read_unlock(&hvm->g2m_ioport_lock);
return X86EMUL_OKAY;
}
@@ -210,9 +232,15 @@ static int cf_check g2m_portio_write(
const struct hvm_io_handler *handler, uint64_t addr, uint32_t size,
uint64_t data)
{
struct hvm_vcpu_io *hvio = &current->arch.hvm.hvm_io;
const struct g2m_ioport *g2m_ioport = hvio->g2m_ioport;
unsigned int mport = (addr - g2m_ioport->gport) + g2m_ioport->mport;
struct hvm_domain *hvm = &current->domain->arch.hvm;
const struct g2m_ioport *g2m_ioport =
g2m_portio_find_and_lock(hvm, addr, size);
unsigned int mport;
if ( !g2m_ioport )
return X86EMUL_RETRY;
mport = addr - g2m_ioport->gport + g2m_ioport->mport;
switch ( size )
{
@@ -229,6 +257,8 @@ static int cf_check g2m_portio_write(
BUG();
}
read_unlock(&hvm->g2m_ioport_lock);
return X86EMUL_OKAY;
}
+1
View File
@@ -136,6 +136,7 @@ struct hvm_domain {
/* List of guest to machine IO ports mapping. */
struct list_head g2m_ioport_list;
rwlock_t g2m_ioport_lock;
/* List of MMCFG regions trapped by Xen. */
struct list_head mmcfg_regions;
-2
View File
@@ -76,8 +76,6 @@ struct hvm_vcpu_io {
unsigned long msix_unmask_address;
unsigned long msix_snoop_address;
unsigned long msix_snoop_gpa;
const struct g2m_ioport *g2m_ioport;
};
struct nestedvcpu {
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.