All Virtuozzo development lists (kernel + QEMU)
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.