Virtuozzo QEMU development (svt-core@virtuozzo.com)
 help / color / mirror / Atom feed
From: no-reply@sashiko.virtuozzo.com
To: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>,
	 svt-core@virtuozzo.com
Subject: Re: [PATCH QEMU HCI-8.1 v4 20/31] kvm: tolerate ioctls after kvm_close()
Date: Tue, 20 Oct 2026 16:52:05 +0000	[thread overview]
Message-ID: <010701a15a3a31ac-44f48c59-f930-4e24-a55a-ec817090149e-000000@eu-central-1.amazonses.com> (raw)
In-Reply-To: <20261020-vfio_v1-v4-20-83a5668230e5@virtuozzo.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] kvm: fake 0 return from kvm_vm_ioctl() after kvm_close() is taken as a valid fd
- [Low] kvm: kvm_vm_ioctl() fake success leaves kvm_set_irq() reading uninit event.status
- [Low] kvm: kvm_close() sentinel checks in ioctl wrappers are not synchronized with non-BQL callers
- [Low] kvm: ioctl wrappers return 0 for every ioctl after kvm_close(), faking success
--

commit 09dbc4fd8a681d8276f3d8eda8630fee15bf5809
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

kvm: tolerate ioctls after kvm_close()

This patch keeps kvm_state allocated after kvm_close() and makes
kvm_ioctl(), kvm_vm_ioctl() and kvm_vcpu_ioctl() return 0 once the
descriptors have been closed. The aim is to stop a retired cpr-transfer
source from crashing on a late interrupt, such as the MSI-X delivery
triggered by net_cleanup().

> diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
> --- a/accel/kvm/kvm-all.c
> +++ b/accel/kvm/kvm-all.c

[ ... ]

> @@ -644,7 +644,6 @@ void kvm_close(void)
>          close(kvm_state->fd);
>          kvm_state->fd = -1;
>      }
> -    kvm_state = NULL;
>  }

[Severity: Low]
Is kvm_close() synchronized with non-BQL callers of the ioctl wrappers?

kvm_close() is called from vfio_cpr_kvm_close_notifier() on
MIG_EVENT_PRECOPY_DONE, through vfio_kvm_device_close(). It does not call
accel_ioctl_inhibit_begin(). The fds are closed with plain stores and no
barrier:

    close(kvm_state->vmfd);
    kvm_state->vmfd = -1;
    close(kvm_state->fd);
    kvm_state->fd = -1;

The later patch "kvm: fix coalesced MMIO flush racing with kvm_close()"
only clears coalesced_mmio_ring and calls drain_call_rcu(). It does not
add any synchronization to the fd close or to the new checks.

> @@ -3371,6 +3370,11 @@ int kvm_ioctl(KVMState *s, unsigned long type, ...)
>      arg = va_arg(ap, void *);
>      va_end(ap);
>  
> +    /* kvm_close() has been called: there is no KVM to talk to any more */
> +    if (s->fd == -1) {
> +        return 0;
> +    }
> +

[Severity: Low]
Can returning 0 for every system-level ioctl cause callers to act on a
success that never happened?

After kvm_close(), kvm_ioctl() fills in no output. For example,
kvm_check_extension() sees 0 for KVM_CHECK_EXTENSION and so reports every
capability as unsupported.

>      trace_kvm_ioctl(type, arg);
>      ret = ioctl(s->fd, type, arg);

[ ... ]

> @@ -3389,6 +3393,11 @@ int kvm_vm_ioctl(KVMState *s, unsigned long type, ...)
>      arg = va_arg(ap, void *);
>      va_end(ap);
>  
> +    /* kvm_close() has been called: there is no VM to talk to any more */
> +    if (s->vmfd == -1) {
> +        return 0;
> +    }
> +

[Severity: Medium]
Can this fake 0 be mistaken for a valid file descriptor?

Some kvm_vm_ioctl() requests return a new fd on success, and 0 is
indistinguishable from fd 0 (normally stdin). Two paths are affected after
kvm_close().

query_stats_cb() and query_stats_schemas_cb() only test for -1:

    stats_fd = kvm_vm_ioctl(s, KVM_GET_STATS_FD, NULL);
    if (stats_fd == -1) {
        ...
    }
    query_stats(result, target, names, stats_fd, NULL, errp);
    close(stats_fd);

Both callbacks are still registered after kvm_close(), so a QMP query-stats
or query-stats-schemas would pass fd 0 to query_stats() and then call
close(0).

vfio_kvm_device_add_fd() in hw/vfio/helpers.c has the same problem:

    if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) {
        ...
    }

    vfio_kvm_device_fd = cd.fd;

cd is initialized with only .type, so cd.fd stays 0. If a VFIO device is
attached after kvm_close(), vfio_kvm_device_fd becomes 0, the next
ioctl(0, KVM_SET_DEVICE_ATTR) fails with ENOTTY, and vfio_kvm_device_close()
would later call close(0).

kvm_create_guest_memfd() and kvm_create_vcpu() check only fd < 0, so they
would also accept 0. I did not establish that either of them can run after
kvm_close() on the source.

The commit message justifies returning 0 only for interrupt and ioeventfd
callers. Were the ioctls that return an fd audited?

[Severity: Low]
Can kvm_set_irq() return an uninitialized value after kvm_close()?

kvm_set_irq() declares struct kvm_irq_level event; without an initializer
and writes only level and irq:

    event.level = level;
    event.irq = irq;
    ret = kvm_vm_ioctl(s, s->irq_set_ioctl, &event);
    if (ret < 0) {
        perror("kvm_set_irq");
        abort();
    }
    return (s->irq_set_ioctl == KVM_IRQ_LINE) ? 1 : event.status;

With the new guard, kvm_vm_ioctl() returns 0 without running the ioctl, so
the abort() check is skipped. On x86, irq_set_ioctl is KVM_IRQ_LINE_STATUS
when KVM_CAP_IRQ_INJECT_STATUS is present, so event.status is returned
uninitialized.

kvm_ioapic_set_irq() reaches this path after kvm_close(), because kvm_state
is no longer NULL:

    delivered = kvm_set_irq(kvm_state, s->kvm_gsi_base + irq, level);
    kvm_report_irq_delivered(delivered);

The legacy PIC path, kvm_pic_set_irq(), does the same. Should event be
zero-initialized, or should kvm_set_irq() return early when the VM is
closed?

The same shape applies to other getters such as KVM_GET_CLOCK,
KVM_GET_IRQCHIP and KVM_GET_DIRTY_LOG, which would report success without
filling the caller's buffer. I could not establish that any of them runs
after kvm_close().

[Severity: Low]
Is there a window between the new check and the ioctl() call?

s->vmfd is read once for the check and again for ioctl(), and the check sits
outside the accel_ioctl_begin()/accel_ioctl_end() region:

    if (s->vmfd == -1) {
        return 0;
    }

    trace_kvm_vm_ioctl(type, arg);
    accel_ioctl_begin();
    ret = ioctl(s->vmfd, type, arg);

If a non-BQL thread passes the check just as kvm_close() closes the fds, the
ioctl() can fail with EBADF. Callers such as kvm_set_irq() and
kvm_irqchip_commit_routes() treat that as fatal. If the fd number has been
reused, the ioctl lands on an unrelated fd.

vCPU threads are stopped by cpu_remove_sync(), and the dirty-ring reaper
takes the BQL. I could not show that every other non-BQL caller is
excluded. This is speculative, and the window is narrow.

>      trace_kvm_vm_ioctl(type, arg);
>      accel_ioctl_begin();
>      ret = ioctl(s->vmfd, type, arg);

[ ... ]

> @@ -3409,6 +3418,11 @@ int kvm_vcpu_ioctl(CPUState *cpu, unsigned long type, ...)
>      arg = va_arg(ap, void *);
>      va_end(ap);
>  
> +    /* kvm_close() has been called: the vCPU fds are gone */
> +    if (cpu->kvm_fd == -1) {
> +        return 0;
> +    }
> +

[Severity: Low]
Is cpu->kvm_fd == -1 a reliable marker for the closed state?

The guard returns success for any vCPU whose kvm_fd is -1, whether it was
closed or never created. kvm_fd is set to -1 only in kvm_close(), so the
guard is not tied to a dedicated closed state. Vector getters on this path
would also report success without filling the caller's buffer.

I did not establish that this path runs after kvm_close().

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261020-vfio_v1-v4-0-83a5668230e5@virtuozzo.com?part=20

  reply	other threads:[~2026-10-20 16:52 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-20 16:30 [PATCH QEMU HCI-8.1 v4 00/31] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 01/31] VZ: migration/cpr-exec: fix Error leak in preserve_ram() Andrey Drobyshev
2026-10-20 16:34   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 02/31] migration/cpr-exec: restore RAM write access if exec fails Andrey Drobyshev
2026-10-20 16:34   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 03/31] vfio/region: create BAR dma-bufs with O_CLOEXEC Andrey Drobyshev
2026-10-20 16:35   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 04/31] migration/cpr-exec: require a file or exec migration URI Andrey Drobyshev
2026-10-20 16:37   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 05/31] migration: close the CPR state if the outgoing channel fails to open Andrey Drobyshev
2026-10-20 16:38   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 06/31] migration: Plug memory leaks after migrate_set_error() Andrey Drobyshev
2026-10-20 16:38   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 07/31] hw/vfio: Fix liveness check in vfio_connect_kvm_msi_virq() Andrey Drobyshev
2026-10-20 16:38   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 08/31] vfio/pci: Reject invalid MSI-X Table and PBA BIR values Andrey Drobyshev
2026-10-20 16:38   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 09/31] vfio/pci: Fix information leak in vfio_rom_read() Andrey Drobyshev
2026-10-20 16:39   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 10/31] vfio/pci: Initialize rom_read_failed in vfio_pci_load_rom() Andrey Drobyshev
2026-10-20 16:40   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 11/31] vfio/pci: Grow buffer in vfio_pci_host_match() Andrey Drobyshev
2026-10-20 16:39   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 12/31] q35: Fix migration of SMRAM state Andrey Drobyshev
2026-10-20 16:41   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 13/31] migration: ensure APIC is loaded prior to VFIO PCI devices Andrey Drobyshev
2026-10-20 16:40   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 14/31] migration: Fix regression of passing error_fatal into vmstate_load_state() Andrey Drobyshev
2026-10-20 16:43   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 15/31] migration: vmstate_save_state_v: fix double error_setg Andrey Drobyshev
2026-10-20 16:45   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 16/31] migration: yank: Move register instance earlier Andrey Drobyshev
2026-10-20 16:47   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 17/31] migration: Free cpr-transfer MigrationAddress along with gsource Andrey Drobyshev
2026-10-20 16:47   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 18/31] migration: Move CPR HUP watch to cpr-transfer.c Andrey Drobyshev
2026-10-20 16:48   ` no-reply
2026-10-20 16:30 ` [PATCH QEMU HCI-8.1 v4 19/31] migration: Fix crash on second migration when cancel early Andrey Drobyshev
2026-10-20 16:52   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 20/31] kvm: tolerate ioctls after kvm_close() Andrey Drobyshev
2026-10-20 16:52   ` no-reply [this message]
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 21/31] kvm: fix coalesced MMIO flush racing with kvm_close() Andrey Drobyshev
2026-10-20 16:54   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 22/31] migration/cpr: add cpr_is_in_progress() Andrey Drobyshev
2026-10-20 16:54   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 23/31] vfio/migration: don't migrate device state during CPR Andrey Drobyshev
2026-10-20 17:00   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 24/31] vfio/listener: skip dirty page tracking " Andrey Drobyshev
2026-10-20 16:59   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 25/31] VZ: vfio/cpr: don't close KVM on cpr-exec Andrey Drobyshev
2026-10-20 17:01   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 26/31] VZ: migration/cpr-exec: abort cpr-exec if memory can't be preserved Andrey Drobyshev
2026-10-20 17:02   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 27/31] VZ: migration/cpr-exec: take the vhost-net devices back if exec fails Andrey Drobyshev
2026-10-20 17:03   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 28/31] VZ: migration/cpr-exec: don't exec if a vhost-net device can't be released Andrey Drobyshev
2026-10-20 17:03   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 29/31] VZ: vfio/region: preserve BAR mappings across cpr-exec #VSTOR-103860 Andrey Drobyshev
2026-10-20 17:08   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 30/31] VZ: migration/cpr-exec: allow cpr-exec with VFIO devices #VSTOR-103860 Andrey Drobyshev
2026-10-20 17:04   ` no-reply
2026-10-20 16:31 ` [PATCH QEMU HCI-8.1 v4 31/31] VZ: vfio/container: lift the CPR blocker for mdev " Andrey Drobyshev
2026-10-20 17:06   ` no-reply

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=010701a15a3a31ac-44f48c59-f930-4e24-a55a-ec817090149e-000000@eu-central-1.amazonses.com \
    --to=no-reply@sashiko.virtuozzo.com \
    --cc=andrey.drobyshev@virtuozzo.com \
    --cc=svt-core@virtuozzo.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