Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Tao Gong" <gongtao@google.com>, kvm@vger.kernel.org
Subject: Re: [RFC PATCH v3 2/5] KVM: guest_memfd: Add ioctl KVM_LINK_GUEST_MEMFD
Date: Mon, 26 Oct 2026 07:19:26 +0000	[thread overview]
Message-ID: <sashiko-outbox-184655@kernel.org> (raw)
In-Reply-To: <20261026064424.3003536-3-gongtao@google.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] KVM: guest_memfd: multiple same-VM links to one inode in kvm_gmem_link()
- [High] KVM: x86: kvm_arch_gmem_link_allowed() allows unsafe TDX and SEV guest_memfd linking
--

commit f69df48ff894e872fc6ccbcb9ce7d8cfde5a15f3
Author: Ackerley Tng <ackerleytng@google.com>

KVM: guest_memfd: Add ioctl KVM_LINK_GUEST_MEMFD

This commit introduces the KVM_LINK_GUEST_MEMFD ioctl to link an existing
guest_memfd's underlying inode to a new file descriptor bound to a target VM,
allowing compatible VMs to share the same underlying guest_memfd memory.

> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index c081e4b9b25de..4427d32290c0f 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -10648,6 +10648,16 @@ bool kvm_arch_supports_gmem_init_shared(struct kvm *kvm)
>  	return gmem_in_place_conversion || !kvm_arch_has_private_mem(kvm);
>  }
>  
> +bool kvm_arch_gmem_link_allowed(struct kvm *dst, struct kvm *src)
> +{
> +	/*
> +	 * Different VM types have different requirements for, and different
> +	 * (vendor) state associated with, guest_memfd memory, e.g. memory that
> +	 * is private to a TDX guest must never be mapped into a non-TDX VM.
> +	 */
> +	return dst->arch.vm_type == src->arch.vm_type;
> +}

[Severity: High]
Is checking dst->arch.vm_type == src->arch.vm_type in
kvm_arch_gmem_link_allowed() sufficient for confidential VM types on x86?

On Intel TDX (KVM_X86_TDX_VM), each physical page in a guest_memfd is
exclusively owned in the TDX Module PAMT by a single TD's TDR and encrypted
with that TD's private HKID. If userspace links a guest_memfd between two TDX
VMs and both VMs populate or fault in the same private page via
tdx_mem_page_add() or tdx_mem_page_aug(), the second TD's SEAMCALL will fail
with TDX_PAGE_NOT_FREE and trigger a host WARN_ON_ONCE via TDX_BUG_ON_2():

arch/x86/kvm/vmx/tdx.c:tdx_mem_page_aug() {
	err = tdh_mem_page_aug(&kvm_tdx->td, gpa, level, pfn, &entry, &level_state);
	if (unlikely(tdx_operand_busy(err)))
		return -EBUSY;

	if (TDX_BUG_ON_2(err, TDH_MEM_PAGE_AUG, entry, level_state, kvm))
		return -EIO;
}

On AMD SEV-SNP (KVM_X86_SNP_VM), each physical page's hardware RMP entry
records a single (ASID, GPA) owner. If two SNP VMs link the same guest_memfd:

1. When dst faults in a private page already assigned in the RMP to src,
   sev_gmem_make_private() checks snp_lookup_rmpentry() without verifying the
   owning ASID or GPA, and returns 0, installing an NPT mapping in dst for a
   PFN owned by src's ASID:

arch/x86/kvm/svm/sev.c:sev_gmem_make_private() {
	rc = snp_lookup_rmpentry(pfn, &assigned, &level);
	...
	if (assigned) {
		pr_debug("%s: already assigned: gfn %llx pfn %llx nr_pages %llx level %d\n",
			 __func__, gfn, pfn, nr_pages, level);
		return 0;
	}
}

2. If src closes its guest_memfd and is destroyed while dst keeps the linked
   guest_memfd open, the shared inode's folios are not freed, so
   kvm_gmem_free_folio() -> sev_gmem_make_shared() does not run.
   sev_vm_destroy() then decommissions src's SNP context and frees src's ASID
   via sev_asid_free() while physical pages in the guest_memfd remain
   Assigned=1 to that freed/recycled ASID in the RMP.

3. If dst converts pages to shared or punches a hole while src has pages
   marked Immutable=1 in the RMP via KVM_SEV_SNP_LAUNCH_UPDATE,
   sev_gmem_make_shared() -> rmp_make_shared() fails and triggers WARN_ONCE().

In addition, for legacy SEV and SEV-ES VMs initialized via KVM_SEV_INIT or
KVM_SEV_ES_INIT, sev_guest_init() leaves kvm->arch.vm_type set to
KVM_X86_DEFAULT_VM, so kvm_arch_gmem_link_allowed() treats legacy SEV/SEV-ES
VMs and non-SEV default VMs as having the same vm_type.

Should kvm_arch_gmem_link_allowed() disallow linking when either VM is a
confidential VM (or only allow KVM_X86_DEFAULT_VM without SEV enabled)?

> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> index 3ca6f64d48040..54b11747a8c48 100644
> --- a/virt/kvm/guest_memfd.c
> +++ b/virt/kvm/guest_memfd.c
[ ... ]
> @@ -970,7 +979,10 @@ static struct file *kvm_gmem_alloc_file(struct kvm *kvm, struct inode *inode,
>  	kvm_get_kvm(kvm);
>  	f->kvm = kvm;
>  	xa_init(&f->bindings);
> +
> +	filemap_invalidate_lock(inode->i_mapping);
>  	list_add(&f->entry, &GMEM_I(inode)->gmem_file_list);
> +	filemap_invalidate_unlock(inode->i_mapping);
>  
>  	return file;
>  }
> @@ -1026,6 +1038,77 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args)
>  	return __kvm_gmem_create(kvm, size, flags);
>  }
>  
> +int kvm_gmem_link(struct kvm *kvm, struct kvm_link_guest_memfd *args)
> +{
[ ... ]
> +	ret = -EINVAL;
> +	if (src_file->f_op != &kvm_gmem_fops)
> +		goto out;
> +
> +	/* Cannot link a gmem file with the same VM again. */
> +	gmem = src_file->private_data;
> +	if (gmem->kvm == kvm)
> +		goto out;
> +
> +	if (!kvm_arch_gmem_link_allowed(kvm, gmem->kvm))
> +		goto out;
> +
> +	fd = get_unused_fd_flags(0);
> +	if (fd < 0) {
> +		ret = fd;
> +		goto out;
> +	}
> +
> +	inode = file_inode(src_file);
> +	ihold(inode);
> +
> +	dst_file = kvm_gmem_alloc_file(kvm, inode, name);
> +	if (IS_ERR(dst_file)) {
> +		iput(inode);
> +		ret = PTR_ERR(dst_file);
> +		goto out_fd;
> +	}

[Severity: High]
Can kvm_gmem_link() still link the same guest_memfd inode to the same VM
multiple times?

In kvm_gmem_link(), the check only verifies whether src_file's immediate
gmem_file->kvm matches the target kvm, and does so without holding
filemap_invalidate_lock(inode->i_mapping).

If userspace creates fd1 on VM1 and links fd1 to VM2 (producing fd2), it can
then:
1. Call KVM_LINK_GUEST_MEMFD(fd1) on VM2 a second time,
2. Call KVM_LINK_GUEST_MEMFD(fd2) back onto VM1, or
3. Call KVM_LINK_GUEST_MEMFD(fd1) concurrently from two threads on VM2.

In all three cases, gmem->kvm == kvm is false, so kvm_gmem_alloc_file()
inserts a second struct gmem_file for the same struct kvm onto
GMEM_I(inode)->gmem_file_list.

Having multiple struct gmem_file instances for the same VM on the same inode
breaks two assumptions elsewhere in guest_memfd.c:

First, kvm_gmem_commit_memory_region() only checks f->bindings on the single
gmem_file being bound:

virt/kvm/guest_memfd.c:kvm_gmem_commit_memory_region() {
	if (!xa_empty(&f->bindings) &&
	    xa_find(&f->bindings, &start, end - 1, XA_PRESENT)) {
		filemap_invalidate_unlock(inode->i_mapping);
		return -EEXIST;
	}
}

With two gmem_file fds for the same VM and inode, a single VM can bind two
memslots at different GPAs to the exact same pgoff range of the same
guest_memfd inode.

Second, kvm_gmem_invalidate_start() iterates over gmem_file_list and calls
__kvm_gmem_invalidate_start() per gmem_file, which acquires and releases
KVM_MMU_LOCK(kvm) per gmem_file while mmu_invalidate_in_progress is active.

Should GMEM_I(inode)->gmem_file_list be walked while holding
filemap_invalidate_lock(inode->i_mapping) to reject any link where an entry
with f->kvm == kvm already exists on the inode?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261026064424.3003536-1-gongtao@google.com?part=2

  reply	other threads:[~2026-10-26  7:19 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-26  6:44 [RFC PATCH v3 0/5] KVM: guest_memfd: Add KVM_LINK_GUEST_MEMFD ioctl Tao Gong
2026-10-26  6:44 ` [RFC PATCH v3 1/5] KVM: guest_memfd: Refactor out kvm_gmem_alloc_file() Tao Gong
2026-10-26  6:44 ` [RFC PATCH v3 2/5] KVM: guest_memfd: Add ioctl KVM_LINK_GUEST_MEMFD Tao Gong
2026-10-26  7:19   ` sashiko-bot [this message]
2026-10-26  6:44 ` [RFC PATCH v3 3/5] KVM: guest_memfd: Advertise support for KVM_LINK_GUEST_MEMFD Tao Gong
2026-10-26  7:35   ` sashiko-bot
2026-10-26  6:44 ` [RFC PATCH v3 4/5] KVM: selftests: Add tests for KVM_LINK_GUEST_MEMFD ioctl Tao Gong
2026-10-26  7:42   ` sashiko-bot
2026-10-26  6:44 ` [RFC PATCH v3 5/5] KVM: selftests: Test transferring private memory to another VM Tao Gong

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=sashiko-outbox-184655@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=gongtao@google.com \
    --cc=kvm@vger.kernel.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