Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "David Woodhouse" <dwmw2@infradead.org>
Cc: kvm@vger.kernel.org, media-ci@linuxtv.org,
	dri-devel@lists.freedesktop.org
Subject: Re: [RFC PATCH 04/11] KVM: guest_memfd: Introduce guest memory ops and route native gmem through them
Date: Thu, 16 Jul 2026 15:30:04 +0000	[thread overview]
Message-ID: <20260716153004.CBC3B1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <1256966e1b1b112ad92bf1b248c439ac23d61535.1784194290.git.dwmw@amazon.co.uk>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Dropped page reference in `kvm_gmem_populate_one` causes a TOCTOU Use-After-Free on physical pages.
- [High] Use-After-Free (UAF) in `kvm_gmem_unbind` due to lockless dereference of unrefcounted `slot->gmem.file`.
- [High] Missing module reference count in `kvm_gmem_ops` allows foreign backend unload, causing kernel panic.
- [High] `kvm_gmem_bind` API contract fails to initialize `slot->gmem.file`, breaking KVM core state tracking.
--

--- Patch [4]: [RFC PATCH 04/11] KVM: guest_memfd: Introduce guest memory ops and route native gmem through them ---
commit 4ae3212093e20a291a948708162e27b1bb7009da
Author: David Woodhouse <dwmw@amazon.co.uk>

KVM: guest_memfd: Introduce guest memory ops and route native gmem through them

This commit introduces a kvm_gmem_ops vtable to allow other subsystems to
supply guest memory backing. It routes the built-in guest_memfd operations
through this vtable and refactors the generic bind, unbind, and populate
flows.

> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> index ab8cfaec82d31..b33d2f4750426 100644
> --- a/include/linux/kvm_host.h
> +++ b/include/linux/kvm_host.h
[ ... ]
> @@ -621,6 +621,88 @@ static inline bool kvm_slot_has_gmem(const struct kvm_memory_slot *slot)
>  	return slot && (slot->flags & KVM_MEM_GUEST_MEMFD);
>  }
>  
> +#ifdef CONFIG_KVM_GUEST_MEMFD
> +/*
> + * struct kvm_gmem_ops - vtable a guest_memfd backing implementation exposes.
> + *
> + * Every file that can back KVM guest memory -- KVM's own guest_memfd (see
> + * kvm_gmem_create()) and any other subsystem opting in -- is created with
> + * the shared kvm_gmem_fops (see is_kvm_gmem_file()).  file->private_data on
> + * such a file points at a struct kvm_gmem_backing whose ->ops field is the
> + * per-instance vtable below.  KVM never looks any further; the rest of the
> + * per-file state (allocator, refcount, private xarray, ...) is up to the
> + * implementation and is reached via container_of() from the backing.
> + *
> + * The memory-side callbacks (bind, unbind, get_pfn, populate, invalidate)
> + * are the reason the interface exists.  The file-level callbacks (release,
> + * mmap, fallocate, ...) are dispatched by kvm_gmem_fops so that every
> + * implementation gets a private hook -- see the dispatchers in
> + * virt/kvm/guest_memfd.c.
> + *
> + * get_pfn() contract:
> + *  - *pfn is the host PFN backing @gfn.  It need not have a struct page.
> + *  - *max_order is the largest order KVM may use to map starting at @gfn.
> + *    The implementation MUST clamp it to the physical contiguity and the
> + *    HPA/GPA alignment of the backing, so KVM never builds a huge SPTE that
> + *    spans discontiguous physical memory.
> + *  - If *page is non-NULL on return the caller owns that page reference and
> + *    is responsible for put_page() after use.  If *page is left NULL the PFN
> + *    is treated as non-refcounted, and its lifetime is owned by the
> + *    implementation across bind()/unbind().
> + *
> + * Memory intended to back guest RAM MUST be reported as E820_TYPE_RAM by the
> + * host so KVM maps it write-back (and applies the memory-encryption bit on
> + * encrypted hosts) rather than treating it as MMIO.
> + */
> +struct kvm_gmem_ops {
> +	/* Memory operations. */

[Severity: High]
Is there a missing module reference count in struct kvm_gmem_ops? 

The operations table lacks an owner field (like struct module *owner) to pin
the backend module.

Since the shared kvm_gmem_fops routes file operations to a foreign backend's
vtable, could a user open the guest_memfd, unload the backend module, and
then close the file? The release dispatcher would then call into the unloaded
module's release hook, potentially causing a kernel panic.

> +	int (*bind)(struct file *file, struct kvm *kvm,
> +		    struct kvm_memory_slot *slot, loff_t offset);
[ ... ]
> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> index db57c5766ab60..2b277468a12f0 100644
> --- a/virt/kvm/guest_memfd.c
> +++ b/virt/kvm/guest_memfd.c
[ ... ]
> @@ -692,15 +782,51 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
[ ... ]
> +int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
> +		  unsigned int fd, uoff_t offset)
> +{
> +	const struct kvm_gmem_ops *ops;
> +	struct file *file;
> +	int r;
> +
> +	BUILD_BUG_ON(sizeof(gpa_t) != sizeof(offset));
> +	BUILD_BUG_ON(sizeof(gfn_t) != sizeof(slot->gmem.pgoff));
> +
> +	file = fget(fd);
> +	if (!file)
> +		return -EBADF;
> +
> +	ops = kvm_gmem_get_ops(file);
> +	if (!ops || !ops->bind || !ops->get_pfn) {
> +		r = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (!PAGE_ALIGNED(offset)) {
> +		r = -EINVAL;
> +		goto out;
> +	}
>  
>  	/*
> -	 * Drop the reference to the file, even on success.  The file pins KVM,
> -	 * not the other way 'round.  Active bindings are invalidated if the
> -	 * file is closed before memslots are destroyed.
> +	 * On success .bind() associates the fd with @kvm and (if the
> +	 * implementation needs it) records the reference that makes the fd
> +	 * pin the VM.  A .bind() that returns success is trusted to have
> +	 * done so; there is no separate ownership check here.
> +	 */
> +	r = ops->bind(file, kvm, slot, offset);
> +out:
> +	/*
> +	 * Drop the reference to the file even on success.  The file pins
> +	 * KVM, not the other way 'round: it is the implementation's job to
> +	 * invalidate its bindings if the fd is closed before the memslots
> +	 * are destroyed.
>  	 */
> -	r = 0;
> -err:
>  	fput(file);
>  	return r;
>  }

[Severity: High]
Does this function miss initializing slot->gmem.file on a successful bind?

KVM core code relies heavily on slot->gmem.file being populated to later call
unbind. If a foreign backend succeeds in ops->bind but doesn't intentionally
mutate KVM's internal slot->gmem.file state, KVM might never call unbind
when the memslot is subsequently freed.

Could this leak backend resources or leave dangling memslot pointers in the
backend?

> @@ -741,15 +861,41 @@ void kvm_gmem_unbind(struct kvm_memory_slot *slot)
[ ... ]
> +void kvm_gmem_unbind(struct kvm *kvm, struct kvm_memory_slot *slot)
> +{
> +	const struct kvm_gmem_ops *ops;
> +	struct file *file;
> +
> +	/*
> +	 * Nothing to do if the underlying file was _already_ closed, as
> +	 * kvm_gmem_release() invalidates and nullifies all bindings.
> +	 */
> +	file = READ_ONCE(slot->gmem.file);
> +	if (!file)
> +		return;
> +
> +	ops = kvm_gmem_get_ops(file);

[Severity: High]
Is it safe to dereference the file locklessly here?

The generic code previously used get_file_active() to safely obtain a
reference. Because kvm_gmem_bind() intentionally drops its reference to the
file on success (via fput(file)), userspace can close the guest_memfd
concurrently while the memslot is being destroyed.

Could this result in a use-after-free when kvm_gmem_get_ops() attempts to
dereference the unrefcounted file pointer?

> +	if (ops && ops->unbind)
> +		ops->unbind(file, kvm, slot);
[ ... ]
> @@ -860,11 +1020,50 @@ static long __kvm_gmem_populate(struct kvm *kvm, struct kvm_memory_slot *slot,
>  	return ret;
>  }
>  
> +static int kvm_gmem_populate_one(const struct kvm_gmem_ops *ops,
> +				      struct file *file, struct kvm *kvm,
> +				      struct kvm_memory_slot *slot, gfn_t gfn,
> +				      struct page *src_page,
> +				      kvm_gmem_populate_cb post_populate,
> +				      void *opaque)
> +{
> +	kvm_pfn_t pfn;
> +	int ret;
> +
> +	/*
> +	 * Resolve the PFN via the ops.  A populate() op may also copy
> +	 * @src_page into the backing before the arch post_populate() step
> +	 * (e.g. SNP LAUNCH_UPDATE); otherwise fall back to get_pfn().
> +	 */
> +	if (ops->populate)
> +		ret = ops->populate(file, kvm, slot, gfn, &pfn,
> +				    src_page, 0);
> +	else {
> +		struct page *ignored_page = NULL;
> +
> +		ret = ops->get_pfn(file, kvm, slot, gfn, &pfn,
> +				   &ignored_page, NULL);
> +		/*
> +		 * Populate does not consume a refcount on the destination page.
> +		 * If get_pfn() handed a refcount back through @page, drop it.
> +		 * Implementations whose PFNs aren't refcounted per-call leave
> +		 * @page NULL and this is a no-op.
> +		 */
> +		if (!ret && ignored_page)
> +			put_page(ignored_page);
> +	}
> +	if (ret)
> +		return ret;
> +
> +	return post_populate(kvm, gfn, pfn, src_page, opaque);
> +}

[Severity: Critical]
Does dropping the page reference before calling post_populate() introduce a
time-of-check to time-of-use race?

If the backend provides a physical page via get_pfn() and we drop the pin
with put_page(ignored_page) early, the page could be concurrently freed and
reallocated to another process.

Could this cause the hardware post_populate callback (such as SNP RMPUPDATE)
to operate on a physical page that now belongs to an innocent host process,
corrupting host memory?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/f09647f427565c1789a6979470045aab78661563.1784194290.git.dwmw@amazon.co.uk?part=4

  reply	other threads:[~2026-07-16 15:30 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-16  9:31 [RFC PATCH 00/11] KVM: Allow alternative providers of guest_memfd backed by PFNMAP memory David Woodhouse
2026-07-16  9:31 ` [RFC PATCH 01/11] KVM: selftests: sev_smoke_test: Only run VM types the host offers David Woodhouse
2026-07-16  9:31   ` [RFC PATCH 02/11] KVM: selftests: sev_init2_tests: Derive SEV availability from KVM David Woodhouse
2026-07-16  9:31   ` [RFC PATCH 03/11] KVM: SEV: Remove struct page dependency from SNP gmem paths David Woodhouse
2026-07-16 15:30     ` sashiko-bot
2026-07-16  9:31   ` [RFC PATCH 04/11] KVM: guest_memfd: Introduce guest memory ops and route native gmem through them David Woodhouse
2026-07-16 15:30     ` sashiko-bot [this message]
2026-07-16  9:31   ` [RFC PATCH 05/11] iommufd: Look up private-interconnect phys via exporter symbols David Woodhouse
2026-07-16 15:33     ` sashiko-bot
2026-07-16  9:31   ` [RFC PATCH 06/11] iommufd: Plumb dma-buf memory-type (RAM vs MMIO) through the phys map David Woodhouse
2026-07-16 15:41     ` sashiko-bot
2026-07-16  9:31   ` [RFC PATCH 07/11] KVM: guest_memfd: Add ops-driven page revocation David Woodhouse
2026-07-16 15:42     ` sashiko-bot
2026-07-16  9:31   ` [RFC PATCH 08/11] samples/kvm: Add guest_memfd backing sample David Woodhouse
2026-07-16 15:53     ` sashiko-bot
2026-07-16  9:31   ` [RFC PATCH 09/11] selftests/kvm: gmem_provider KVM-only tests David Woodhouse
2026-07-16 15:46     ` sashiko-bot
2026-07-16  9:31   ` [RFC PATCH 10/11] selftests/kvm: gmem_provider iommufd tests David Woodhouse
2026-07-16 15:48     ` sashiko-bot
2026-07-16  9:31   ` [RFC PATCH 11/11] samples/kvm, selftests/kvm: Allow the gmem_provider NVMe DMA test on arm64 David Woodhouse
2026-07-16 15:54     ` sashiko-bot
2026-07-16 15:31   ` [RFC PATCH 01/11] KVM: selftests: sev_smoke_test: Only run VM types the host offers sashiko-bot

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=20260716153004.CBC3B1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=dwmw2@infradead.org \
    --cc=kvm@vger.kernel.org \
    --cc=media-ci@linuxtv.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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