mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Hang Nan <2122295973@qq.com>
To: Paolo Bonzini <pbonzini@redhat.com>, kvm@vger.kernel.org
Cc: Sean Christopherson <seanjc@google.com>,
	Carlos Llamas <cmllamas@google.com>,
	linux-kernel@vger.kernel.org
Subject: [PATCH v2] KVM: coalesced_mmio: reject zone sizes that don't fit in an int
Date: Wed, 28 Oct 2026 09:59:56 +0800	[thread overview]
Message-ID: <tencent_6EE8901CBE1ED363CBBA2E8CEC6D88D19F09@qq.com> (raw)
In-Reply-To: <1792859391.770388.v1@qq.com>

kvm_vm_ioctl_register_coalesced_mmio() validates @zone->pio but not
@zone->size.  The u32 size is handed to kvm_io_bus_register_dev() as an
"int len", so any size >= 0x80000000 silently becomes negative.

coalesced_mmio_in_range() bails out immediately for len < 0, so such a
zone never matches its own range:

  - coalesced MMIO writes for the zone are no longer coalesced and are
    forwarded to userspace instead;
  - KVM_UNREGISTER_COALESCED_MMIO returns 0 but does not remove the zone,
    because the unregister path uses coalesced_mmio_in_range() for matching
    too, leaving the kvm_io_device permanently registered;
  - the leaked device occupies a slot on the MMIO/PIO bus.  After
    NR_IOBUS_DEVS - 1 (998) such zones every further
    kvm_io_bus_register_dev() on that bus fails with -ENOSPC, including
    ioeventfds and valid coalesced zones, effectively killing the VM's
    device model until the VM is destroyed.

A negative len additionally breaks the sorted-interval invariant used by
kvm_io_bus_cmp(), which computes the end of an interval as "addr + len".

ioeventfd rejects lengths outside {0, 1, 2, 4, 8}, so the same ioctl
family cannot be abused this way; coalesced MMIO has no equivalent check.

Validate the zone in kvm_vm_ioctl_unregister_coalesced_mmio() as well: it
has the same gap today and is only saved by coalesced_mmio_in_range()
rejecting negative lengths (commit 1a214246cbb4).  Factor both checks into
kvm_is_valid_io_zone() and reject with -EINVAL.

Note that this makes KVM_UNREGISTER_COALESCED_MMIO return -EINVAL for a
zone that could never have been registered, instead of the old "0 but the
zone stays" behaviour.  QEMU never registers a zone larger than the
coalesced ring window, so no in-tree user is affected.

Fixes: 743eeb0b01d2 ("KVM: Intelligent device lookup on I/O bus")
Cc: stable@vger.kernel.org
Signed-off-by: Hang Nan <2122295973@qq.com>
---
v2:
 - recipients taken from scripts/get_maintainer.pl (Paolo and the list in To:,
   you and Carlos in Cc:), sorry about that
 - dropped the comment
 - folded the validation into kvm_is_valid_io_zone() and used it from
   kvm_vm_ioctl_unregister_coalesced_mmio() as well

On the unregister side: agreed, it only survives today because
coalesced_mmio_in_range() rejects negative lengths (1a214246cbb4).  Validating
there does change the return value for a zone that can no longer be registered
at all, from 0 to -EINVAL, so I called it out in the changelog -- it is an
ABI-visible difference for anyone who relied on the old behaviour.

Verified before and after the patch on v7.3-11778-g5b0764a53235, same .config,
unprivileged /dev/kvm, same initrd:

  before: REGISTER {addr=0x1000, size=0x80000000}          -> 0
          UNREGISTER {same}                                -> 0 (zone stays)
          998 bad zones accepted, then
          REGISTER {addr=0x900000, size=0x1000}            -> -ENOSPC
  after:  REGISTER {addr=0x1000, size=0x80000000}          -> -EINVAL
          UNREGISTER {same}                                -> -EINVAL
          1200 attempts, 0 accepted
          REGISTER {addr=0x900000, size=0x1000}            -> 0
          UNREGISTER {same}                                -> 0

The reproducer is ~150 lines of static C that only opens /dev/kvm; happy to
post it if it is useful.

Hang


virt/kvm/coalesced_mmio.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/virt/kvm/coalesced_mmio.c b/virt/kvm/coalesced_mmio.c
index 3b5430036cae..4afa86b72cf6 100644
--- a/virt/kvm/coalesced_mmio.c
+++ b/virt/kvm/coalesced_mmio.c
@@ -121,13 +121,24 @@ void kvm_coalesced_mmio_free(struct kvm *kvm)
 		free_page((unsigned long)kvm->coalesced_mmio_ring);
 }
 
+static bool kvm_is_valid_io_zone(struct kvm_coalesced_mmio_zone *zone)
+{
+	if (zone->pio != 1 && zone->pio != 0)
+		return false;
+
+	if (zone->size > INT_MAX)
+		return false;
+
+	return true;
+}
+
 int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
 					 struct kvm_coalesced_mmio_zone *zone)
 {
 	int ret;
 	struct kvm_coalesced_mmio_dev *dev;
 
-	if (zone->pio != 1 && zone->pio != 0)
+	if (!kvm_is_valid_io_zone(zone))
 		return -EINVAL;
 
 	dev = kzalloc_obj(struct kvm_coalesced_mmio_dev, GFP_KERNEL_ACCOUNT);
@@ -162,7 +173,7 @@ int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
 	struct kvm_coalesced_mmio_dev *dev, *tmp;
 	int r;
 
-	if (zone->pio != 1 && zone->pio != 0)
+	if (!kvm_is_valid_io_zone(zone))
 		return -EINVAL;
 
 	mutex_lock(&kvm->slots_lock);


           reply	other threads:[~2026-10-28  2:00 UTC|newest]

Thread overview: expand[flat|nested]  mbox.gz  Atom feed
 [parent not found: <1792859391.770388.v1@qq.com>]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=tencent_6EE8901CBE1ED363CBBA2E8CEC6D88D19F09@qq.com \
    --to=2122295973@qq.com \
    --cc=cmllamas@google.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=seanjc@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®