All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Paolo Bonzini" <pbonzini@redhat.com>, kvm@vger.kernel.org
Subject: Re: [PATCH 24/45] KVM: Introduce a generation number for memory attributes
Date: Mon, 02 Nov 2026 18:20:29 +0000	[thread overview]
Message-ID: <sashiko-outbox-193927@kernel.org> (raw)
In-Reply-To: <20261102174512.8903-8-pbonzini@redhat.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] KVM: RCU stall in kvm_range_has_any_vm_memory_attributes() via xas_next()
- [Medium] KVM: unconditional generation bump and error-path sync in kvm_set_vm_mem_attributes()
- [Low] KVM: loongarch: missing closing parenthesis in kvm_check_gen() conditions
- [Low] KVM: stale attrs_generation on error in __kvm_gfn_to_hva_cache_init()
--

commit c500cbe0be12ea140c87b086c8905238fbefe1ea
Author: Nicolas Saenz Julienne <nsaenz@amazon.com>

KVM: Introduce a generation number for memory attributes

This commit introduces a generation number for VM memory attributes so that
KVM components using gfn_to_hva_cache and gfn_to_pfn_cache can detect memory
attribute updates and synchronize via KVM SRCU when attributes requiring
synchronization are modified.

Link: https://patch.msgid.link/20260918135030.171564-22-pbonzini@redhat.com

> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
> index c3e71ef983bcb..6301e726a536f 100644
> --- a/arch/loongarch/kvm/vcpu.c
> +++ b/arch/loongarch/kvm/vcpu.c
> @@ -163,7 +163,8 @@ static void kvm_update_stolen_time(struct kvm_vcpu *vcpu)
>  		return;
>  
>  	gpa &= KVM_STEAL_PHYS_MASK;
> -	if (unlikely(!kvm_check_gen(vcpu->kvm, ghc->generation) || gpa != ghc->gpa ||
> +	if (unlikely(!kvm_check_gen(vcpu->kvm, ghc->slots_generation, ghc->attrs_generation) ||
> +		     gpa != ghc->gpa ||
>  		     kvm_is_error_hva(ghc->hva) || !ghc->memslot) {
>  		if (kvm_gfn_to_hva_cache_init(vcpu->kvm, ghc, gpa, sizeof(*st))) {
>  			ghc->gpa = INVALID_GPA;
> @@ -1817,7 +1818,8 @@ static void kvm_vcpu_set_pv_preempted(struct kvm_vcpu *vcpu)
>  
>  	gpa &= KVM_STEAL_PHYS_MASK;
>  	ghc = &vcpu->arch.st.cache;
> -	if (unlikely(!kvm_check_gen(vcpu->kvm, ghc->generation) || gpa != ghc->gpa ||
> +	if (unlikely(!kvm_check_gen(vcpu->kvm, ghc->slots_generation, ghc->attrs_generation) ||
> +		     gpa != ghc->gpa ||
>  		     kvm_is_error_hva(ghc->hva) || !ghc->memslot) {

[Severity: Low]
As reported by Sashiko on v2 [1], does this fail to build on LoongArch due to
a missing closing parenthesis in both kvm_update_stolen_time() and
kvm_vcpu_set_pv_preempted()?

Both conditions open two parentheses at if (unlikely( before calling
!kvm_check_gen(vcpu->kvm, ghc->slots_generation, ghc->attrs_generation), but
only close one parenthesis after !ghc->memslot before the opening brace.

[ ... ]

> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index da12f4d43c911..6aa6a0a55b070 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c

[ ... ]

> @@ -2558,6 +2559,53 @@ bool kvm_range_has_vm_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
>  	return true;
>  }
>  
> +/*
> + * Returns true if _any_ gfns in the range [@start, @end) have attributes that
> + * match _any_ bit in @mask.
> + */
> +bool kvm_range_has_any_vm_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
> +					unsigned long mask)
> +{
> +	XA_STATE(xas, &kvm->mem_attrs.array, start);
> +	void *entry;
> +
> +	mask &= kvm_supported_vm_mem_attributes(kvm);
> +	if (!mask)
> +		return false;
> +
> +	if (end == start + 1)
> +		return !!(kvm_get_vm_memory_attributes(kvm, start) & mask);
> +
> +	guard(rcu)();
> +	for (;;) {
> +		do {
> +			entry = xas_next(&xas);
> +		} while (xas_retry(&xas, entry));
> +
> +		if (xas.xa_node == XAS_BOUNDS || xas.xa_index >= end)
> +			break;
> +
> +		if (xa_to_value(entry) & mask)
> +			return true;
> +	}

[Severity: High]
Can kvm_range_has_any_vm_memory_attributes() trigger an RCU CPU stall or soft
lockup when userspace invokes KVM_SET_MEMORY_ATTRIBUTES over a large sparse or
unassigned GFN range?

Unassigned GFNs are represented as NULL entries in kvm->mem_attrs.array, where
xa_to_value(NULL) is 0, so NULL entries do not match mask and do not terminate
the loop. However, the loop uses xas_next(&xas), which increments xas.xa_index
by 1 on every call across NULL slots and empty xa_node subtrees whenever the
XArray has an entry at an index >= end keeping xas.xa_node from reaching
XAS_BOUNDS.

Because the entire loop runs inside guard(rcu)() without rescheduling,
scanning a large sparse range will step through every empty GFN index one by
one while holding the RCU read lock. Should this use xas_next_entry(&xas,
end - 1) or xas_for_each(&xas, entry, end - 1) to skip empty slots?

> +
> +	return false;
> +}
> +
> +#define KVM_MEMORY_ATTRIBUTE_NEEDS_SYNC_MASK KVM_MEMORY_ATTRIBUTE_PROT
> +
> +static bool kvm_range_memory_attributes_need_sync(struct kvm *kvm,
> +						  gfn_t start, gfn_t end,
> +						  unsigned long attributes)
> +{
> +	u64 mask = KVM_MEMORY_ATTRIBUTE_NEEDS_SYNC_MASK;
> +
> +	if (attributes & mask)
> +		return true;
> +
> +	return kvm_range_has_any_vm_memory_attributes(kvm, start, end, mask);
> +}
> +
>  static __always_inline void kvm_handle_gfn_range(struct kvm *kvm,
>  						 struct kvm_mmu_notifier_range *range)
>  {
> @@ -2648,20 +2696,25 @@ static int kvm_set_vm_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
>  		.on_lock = kvm_mmu_invalidate_end,
>  		.may_block = true,
>  	};
> +	bool sync = false;
>  	unsigned long i;
>  	void *entry;
>  	int r = 0;
>  
>  	entry = attributes ? xa_mk_value(attributes) : NULL;
>  
> -	trace_kvm_set_vm_mem_attributes(start, end, attributes);
> -
>  	mutex_lock(&kvm->slots_lock);
>  
>  	/* Nothing to do if the entire range has the desired attributes. */
>  	if (kvm_range_has_vm_memory_attributes(kvm, start, end, ~0, attributes))
>  		goto out_unlock;
>  
> +	sync = kvm_range_memory_attributes_need_sync(kvm, start, end,
> +						     attributes);
> +
> +	trace_kvm_set_vm_mem_attributes(start, end, attributes, sync,
> +					kvm->mem_attrs.generation + 1);
> +
>  	/*
>  	 * Reserve memory ahead of time to avoid having to deal with failures
>  	 * partway through setting the new attributes.  Storing NULL never
> @@ -2684,10 +2737,16 @@ static int kvm_set_vm_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
>  		cond_resched();
>  	}
>  
> +	/* Pairs with acquire in kvm_mem_attributes_generation() */
> +	smp_store_release(&kvm->mem_attrs.generation,
> +			  kvm->mem_attrs.generation + 1);
> +
>  	kvm_handle_gfn_range(kvm, &post_set_range);
>  
>  out_unlock:
>  	mutex_unlock(&kvm->slots_lock);
> +	if (sync)
> +		synchronize_srcu_expedited(&kvm->srcu);

[Severity: Medium]
Should kvm->mem_attrs.generation only be incremented when sync is true, and
should synchronize_srcu_expedited() be skipped if xa_reserve() fails?

The commit message notes that for memory attributes that do not require
synchronization, the attributes generation will always remain unchanged.
However, kvm_set_vm_mem_attributes() unconditionally increments
kvm->mem_attrs.generation via smp_store_release() even when sync is false,
which invalidates all gfn_to_hva_cache and gfn_to_pfn_cache entries across the
VM (and causes non-refreshing cache checks such as
kvm_steal_time_set_preempted() to bail out) when toggling attributes like
KVM_MEMORY_ATTRIBUTE_PRIVATE.

In addition, because sync is computed and trace_kvm_set_vm_mem_attributes() is
called with kvm->mem_attrs.generation + 1 before the xa_reserve() loop:

	for (i = start; entry && i < end; i++) {
		r = xa_reserve(&kvm->mem_attrs.array, i, GFP_KERNEL_ACCOUNT);
		if (r)
			goto out_unlock;

		cond_resched();
	}

if xa_reserve() fails and jumps to out_unlock while sync is true,
kvm_set_vm_mem_attributes() still runs synchronize_srcu_expedited(&kvm->srcu)
even though no attributes or generation numbers were updated.

[ ... ]

> @@ -3582,7 +3641,8 @@ int kvm_vcpu_write_guest(struct kvm_vcpu *vcpu, gpa_t gpa, const void *data,
>  }
>  EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_vcpu_write_guest);
>  
> -static int __kvm_gfn_to_hva_cache_init(struct kvm_memslots *slots,
> +static int __kvm_gfn_to_hva_cache_init(struct kvm *kvm,
> +				       struct kvm_memslots *slots,
>  				       struct gfn_to_hva_cache *ghc,
>  				       gpa_t gpa, unsigned long len)
>  {
> @@ -3592,8 +3652,8 @@ static int __kvm_gfn_to_hva_cache_init(struct kvm_memslots *slots,
>  	gfn_t nr_pages_needed = end_gfn - start_gfn + 1;
>  	gfn_t nr_pages_avail;
>  
> -	/* Update ghc->generation before performing any error checks. */
> -	ghc->generation = slots->generation;
> +	/* Update ghc->slots_generation before performing any error checks. */
> +	ghc->slots_generation = slots->generation;
>  
>  	if (start_gfn > end_gfn) {
>  		ghc->hva = KVM_HVA_ERR_BAD;
> @@ -3612,6 +3672,8 @@ static int __kvm_gfn_to_hva_cache_init(struct kvm_memslots *slots,
>  			return -EFAULT;
>  	}
>  
> +	ghc->attrs_generation = kvm_mem_attributes_generation(kvm);

[Severity: Low]
Should ghc->attrs_generation be updated alongside ghc->slots_generation before
the error checks in __kvm_gfn_to_hva_cache_init()?

ghc->slots_generation is updated before checking start_gfn > end_gfn or
running the gfn_to_hva_many() loop so that when an invalid GPA sets ghc->hva
to KVM_HVA_ERR_BAD, subsequent calls to kvm_read_guest_offset_cached() or
kvm_write_guest_offset_cached() pass kvm_memslots_check_gen() and immediately
hit the fast-path kvm_is_error_hva(ghc->hva) check.

Because ghc->attrs_generation is only updated after those error returns (which
remains the case after the follow-up commit "KVM: Take memory protections into
account for accesses with cached gfn->hva"), if a cached range fails in the
memslot lookup loop after kvm->mem_attrs.generation has advanced,
ghc->attrs_generation remains stale and every subsequent cached access will
repeatedly fail kvm_memslots_check_gen() and re-run
__kvm_gfn_to_hva_cache_init().

[1] https://lore.kernel.org/r/20260918081543.139871-19-pbonzini@redhat.com

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261102174130.8779-1-pbonzini@redhat.com?part=24

  reply	other threads:[~2026-11-02 18:20 UTC|newest]

Thread overview: 71+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-02 17:40 [PATCH v4 00/45] KVM: x86: Introduce memory protection attributes Paolo Bonzini
2026-11-02 17:40 ` [PATCH 01/45] KVM: nVMX/nSVM: check unconditionally whether CR3 is valid Paolo Bonzini
2026-11-02 18:05   ` sashiko-bot
2026-11-02 17:40 ` [PATCH 02/45] KVM: x86/hyperv: do not overwrite hc->ingpa for SIGNAL_EVENT hypercall Paolo Bonzini
2026-11-02 17:40 ` [PATCH 03/45] KVM: x86: apply nGPA->GPA translation to KVM_HC_CLOCK_PAIRING Paolo Bonzini
2026-11-02 17:40 ` [PATCH 04/45] KVM: x86/emulate: Check TSS busy-bit writes during task switches Paolo Bonzini
2026-11-02 18:22   ` sashiko-bot
2026-11-02 17:40 ` [PATCH 05/45] KVM: loongarch: do full validity check on the gfn-to-hva cache Paolo Bonzini
2026-11-02 17:40 ` [PATCH 06/45] KVM: selftests: Test address translation for Hyper-V direct L2 hypercalls Paolo Bonzini
2026-11-02 17:40 ` [PATCH 07/45] KVM: selftests: Take into account mixed memory fault flags Paolo Bonzini
2026-11-02 17:50   ` sashiko-bot
2026-11-02 17:40 ` [PATCH 08/45] selftests: kvm: drop TEST_ASSERT_SUPPORTED_ATTRIBUTES Paolo Bonzini
2026-11-02 17:40 ` [PATCH 09/45] KVM: Define and communicate KVM_EXIT_MEMORY_FAULT RWX flags to userspace Paolo Bonzini
2026-11-02 17:55   ` sashiko-bot
2026-11-02 17:40 ` [PATCH 10/45] KVM: x86: Introduce memory fault on invalid hypercalls reads/writes Paolo Bonzini
2026-11-02 17:40 ` [PATCH 11/45] KVM: selftests: test hypercall memory fault exits Paolo Bonzini
2026-11-02 17:58   ` sashiko-bot
2026-11-02 17:40 ` [PATCH 12/45] KVM: x86/mmu: intersect writability from __kvm_faultin_pfn with fault->map_writable Paolo Bonzini
2026-11-02 17:40 ` [PATCH 13/45] KVM: x86/mmu: Extend map_writable to a full ACC_* mask Paolo Bonzini
2026-11-02 17:40 ` [PATCH 14/45] KVM: x86/mmu: Init memslot hugepage information for non-private_mem VMs too Paolo Bonzini
2026-11-02 17:40 ` [PATCH 15/45] KVM: VMX: return error from kvm_set_cr* Paolo Bonzini
2026-11-02 17:41 ` [PATCH 16/45] KVM: adjust for presence of more than one attribute Paolo Bonzini
2026-11-02 18:03   ` sashiko-bot
2026-11-02 17:44 ` [PATCH 17/45] KVM: Introduce NR/NW/NX memory attributes Paolo Bonzini
2026-11-02 18:04   ` sashiko-bot
2026-11-03  9:25   ` kernel test robot
2026-11-02 17:44 ` [PATCH 18/45] KVM: Include memory protections in result of gfn->hva conversion Paolo Bonzini
2026-11-02 17:44 ` [PATCH 19/45] KVM: Introduce kvm_fetch_guest_page() and use it for x86 Paolo Bonzini
2026-11-02 17:44 ` [PATCH 20/45] KVM: Take memory protections into account for memory read/write/fetch Paolo Bonzini
2026-11-02 17:44 ` [PATCH 21/45] KVM: Take memory protections into account for __kvm_vcpu_map Paolo Bonzini
2026-11-02 17:44 ` [PATCH 22/45] KVM: Encapsulate memattrs array into anonymous struct Paolo Bonzini
2026-11-02 17:44 ` [PATCH 23/45] KVM: Introduce kvm_check_gen()/kvm_memslots_check_gen() Paolo Bonzini
2026-11-02 18:04   ` sashiko-bot
2026-11-03 18:07   ` kernel test robot
2026-11-02 17:44 ` [PATCH 24/45] KVM: Introduce a generation number for memory attributes Paolo Bonzini
2026-11-02 18:20   ` sashiko-bot [this message]
2026-11-03 14:11   ` kernel test robot
2026-11-02 17:44 ` [PATCH 25/45] KVM: Take memory protections into account for accesses with cached gfn->hva Paolo Bonzini
2026-11-02 18:14   ` sashiko-bot
2026-11-02 17:44 ` [PATCH 26/45] KVM: pfncache: Fail to refresh if it contains memory protections Paolo Bonzini
2026-11-02 18:09   ` sashiko-bot
2026-11-02 17:44 ` [PATCH 27/45] KVM: x86/mmu: Do not prefetch sptes on gfns backed by memory attributes Paolo Bonzini
2026-11-02 17:44 ` [PATCH 28/45] KVM: x86/mmu: Take memory protection attributes into account during faults Paolo Bonzini
2026-11-02 18:26   ` sashiko-bot
2026-11-02 17:44 ` [PATCH 29/45] KVM: x86/emulate: change return value of emulator_io_permitted to int Paolo Bonzini
2026-11-02 17:44 ` [PATCH 30/45] KVM: x86/emulate: Propagate memory faults to userspace Paolo Bonzini
2026-11-02 18:32   ` sashiko-bot
2026-11-02 17:44 ` [PATCH 31/45] KVM: x86: "Handle" failed decode when skipping instructions Paolo Bonzini
2026-11-02 18:18   ` sashiko-bot
2026-11-02 17:44 ` [PATCH 32/45] KVM: vmx/sgx: make return value of sgx_gva_to_gpa consistent with its callers Paolo Bonzini
2026-11-02 17:45 ` [PATCH 33/45] KVM: x86: Allow page fault fixup to return errors Paolo Bonzini
2026-11-02 17:45 ` [PATCH 34/45] KVM: x86/mmu: Adjust gva_to_gpa to allow returning an error Paolo Bonzini
2026-11-02 18:19   ` sashiko-bot
2026-11-02 17:45 ` [PATCH 35/45] KVM: x86/mmu: Propagate errors from nested GPA translation Paolo Bonzini
2026-11-02 17:45 ` [PATCH 36/45] KVM: x86: Propagate memory faults through load_pdptrs() Paolo Bonzini
2026-11-02 18:29   ` sashiko-bot
2026-11-02 17:45 ` [PATCH 37/45] KVM: x86/mmu: Issue memory fault exit if walk failed due to memory attribute Paolo Bonzini
2026-11-02 18:32   ` sashiko-bot
2026-11-02 17:45 ` [PATCH 38/45] KVM: x86: mark VM as dead after KVM_SET_SREGS fails to load PDPTRs Paolo Bonzini
2026-11-02 18:28   ` sashiko-bot
2026-11-02 17:45 ` [PATCH 39/45] KVM: VMX: mark VM dead on shadow VMCS or eVMCS access failures Paolo Bonzini
2026-11-02 18:40   ` sashiko-bot
2026-11-02 17:45 ` [PATCH 40/45] KVM: let kvm_arch_post_set_memory_attributes drop mmu_lock Paolo Bonzini
2026-11-02 17:45 ` [PATCH 41/45] KVM: x86/mmu: Obsolete all roots if memattr contains gPTEs Paolo Bonzini
2026-11-02 17:45 ` [PATCH 42/45] KVM: x86: selftests: Introduce memory protection attributes test Paolo Bonzini
2026-11-02 18:43   ` sashiko-bot
2026-11-02 17:45 ` [PATCH 43/45] KVM: x86: selftests: Introduce memory attributes PTE test Paolo Bonzini
2026-11-02 17:45 ` [PATCH 44/45] KVM: x86: selftests: Introduce memory attributes side-channel tests Paolo Bonzini
2026-11-02 18:41   ` sashiko-bot
2026-11-02 17:45 ` [PATCH 45/45] KVM: x86: split out parts of load_pdptrs Paolo Bonzini
2026-11-03 14:40 ` [syzbot ci] Re: KVM: x86: Introduce memory protection attributes syzbot ci

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-193927@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=kvm@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --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 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.