From: sashiko-bot@kernel.org
To: "David Ballesteros" <davimaba.v@proton.me>
Cc: kvm@vger.kernel.org
Subject: Re: [PATCH v2 1/2] KVM: Bound per-VM GFN materialization in KVM_SET_MEMORY_ATTRIBUTES
Date: Fri, 11 Sep 2026 20:45:31 +0000 [thread overview]
Message-ID: <20260911204531.E91311F00893@smtp.kernel.org> (raw)
In-Reply-To: <20260911203238.30088-2-davimaba.v@proton.me>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Sleepable functions called inside an RCU read-side critical section established by `guard(rcu)()`.
- [High] Asymmetric accounting of `mem_attr_gfn_count` allows permanent Denial of Service via budget exhaustion.
- [High] XArray iterator corruption by modifying the array with `xa_erase()` during lockless `xas_for_each()` iteration.
--
commit 3f5796ac8ce025c5b7da5cd6adbb4331967a81b4
Author: David Ballesteros <davimaba.v@proton.me>
KVM: Bound per-VM GFN materialization in KVM_SET_MEMORY_ATTRIBUTES
This commit bounds the number of GFNs materialized per VM in
KVM_SET_MEMORY_ATTRIBUTES to prevent kernel memory exhaustion. It enforces a
limit under kvm->slots_lock and reworks clearing requests to avoid allocations
by erasing entries directly. The per-VM counter is maintained incrementally to
improve performance on non-failing paths.
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index 108d42c5c1d6a..f623c5f07344e 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
[ ... ]
> @@ -2544,6 +2545,34 @@ static bool kvm_pre_set_memory_attributes(struct kvm *kvm,
> return kvm_arch_pre_set_memory_attributes(kvm, range);
> }
>
> +/*
> + * Return the number of GFNs with a materialized entry in mem_attr_array.
> + * Serialization against modifications is provided by kvm->slots_lock.
> + */
> +static unsigned long kvm_count_mem_attr_entries(struct kvm *kvm)
> +{
> + XA_STATE(xas, &kvm->mem_attr_array, 0);
> + unsigned long count = 0;
> + void *entry;
> +
> + guard(rcu)();
> + xas_for_each(&xas, entry, ULONG_MAX) {
> + if (xa_is_retry(entry)) {
> + xas_pause(&xas);
> + continue;
> + }
> + if (entry)
> + count++;
> +
> + if (need_resched()) {
> + xas_pause(&xas);
> + cond_resched();
[Severity: High]
Does this code call cond_resched() while holding the RCU read lock?
Since guard(rcu)() establishes a function-scoped RCU read-side critical section,
the lock is held until the function returns. Calling cond_resched() inside
this loop could lead to scheduling while atomic panics or RCU grace period
stalls.
> + }
> + }
> +
> + return count;
> +}
> +
[ ... ]
> @@ -2579,6 +2609,49 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
> if (kvm_range_has_memory_attributes(kvm, start, end, ~0, attributes))
> goto out_unlock;
>
> + /*
> + * Clearing never materializes entries: erasing cannot allocate, so
> + * there is no reservation phase, no bound to enforce and no way to
> + * fail partway through. Iterate the present entries only (absent
> + * spans are skipped by the xarray walk) and erase them; xa_erase()
> + * takes the xa_lock per entry and node frees are RCU-deferred, so
> + * the iterator is safe under the RCU lock while kvm->slots_lock
> + * excludes other writers.
> + */
> + if (!attributes) {
> + XA_STATE(xas, &kvm->mem_attr_array, start);
> +
> + kvm_handle_gfn_range(kvm, &pre_set_range);
> +
> + guard(rcu)();
> + xas_for_each(&xas, entry, end - 1) {
> + if (xa_is_retry(entry)) {
> + xas_pause(&xas);
> + continue;
> + }
> + xa_erase(&kvm->mem_attr_array, xas.xa_index);
[Severity: High]
Can modifying the array with xa_erase() during a lockless xas_for_each()
iteration corrupt the iterator state?
xa_erase() acquires its own lock and mutates the tree structure, but it does
not update the local xas iterator state. Modifying the tree without updating
the iterator might lead to skipped entries or infinite loops.
Would using xas_store(&xas, NULL) under xas_lock(&xas) be safer here?
> + freed++;
> + cond_resched();
[Severity: High]
Does this sleep while the RCU read lock is held?
The guard(rcu)() macro above establishes a block-scoped RCU critical section
for this if-block. cond_resched() is called unconditionally on every iteration
while the RCU read lock is actively held.
> + }
> +
> + kvm_handle_gfn_range(kvm, &post_set_range);
[Severity: High]
Is it safe to call kvm_handle_gfn_range() before the block-scoped guard(rcu)()
closes?
kvm_handle_gfn_range() acquires MMU locks and may block, but the RCU read lock
acquired by guard(rcu)() is still held at this point, since the block hasn't
ended yet.
> +
> + /* Exactly @freed entries left the array; no resync needed. */
> + kvm->mem_attr_gfn_count -= freed;
> + mutex_unlock(&kvm->slots_lock);
> +
> + return 0;
> + }
> +
> + /*
> + * Bound the number of materialized GFNs per VM. See the comment on
> + * KVM_MEM_ATTR_MAX_GFNS.
> + */
> + if (kvm->mem_attr_gfn_count + (end - start) > KVM_MEM_ATTR_MAX_GFNS) {
> + r = -ENOSPC;
> + goto out_unlock;
[Severity: High]
Can asymmetric accounting of mem_attr_gfn_count allow permanent denial of
service via budget exhaustion?
During a SET operation, the counter is incremented by the full logical range
size (end - start). This overcharges the count if entries are already populated.
When clearing, the counter is only decremented by the exact number of entries
physically erased (freed). This means alternating overlapping requests could
cause the counter to drift upward monotonically.
Once it hits KVM_MEM_ATTR_MAX_GFNS, future requests return -ENOSPC. Since the
-ENOSPC path jumps directly to out_unlock without recalculating the true count
(unlike the -ENOMEM path), the budget appears to be permanently exhausted.
> + }
> +
> /*
> * Reserve memory ahead of time to avoid having to deal with failures
> * partway through setting the new attributes.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260911184819.101123-1-davimaba.v@proton.me?part=1
next prev parent reply other threads:[~2026-09-11 20:45 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-11 18:48 [PATCH 0/2] KVM: unbounded per-VM kernel memory growth via KVM_SET_MEMORY_ATTRIBUTES David Ballesteros
2026-09-11 18:48 ` [PATCH 1/2] KVM: Bound per-VM GFN materialization in KVM_SET_MEMORY_ATTRIBUTES David Ballesteros
2026-09-11 19:05 ` sashiko-bot
2026-09-11 18:48 ` [PATCH 2/2] KVM: Account mem_attr_array nodes to the caller's memcg David Ballesteros
2026-09-11 19:02 ` sashiko-bot
2026-09-11 20:32 ` [PATCH v2 0/2] KVM: unbounded per-VM kernel memory growth via KVM_SET_MEMORY_ATTRIBUTES David Ballesteros
2026-09-11 21:19 ` David Ballesteros
2026-09-11 22:13 ` [PATCH v3 " David Ballesteros
2026-09-11 22:13 ` [PATCH v3 1/2] KVM: Bound per-VM GFN materialization in KVM_SET_MEMORY_ATTRIBUTES David Ballesteros
2026-09-11 22:32 ` sashiko-bot
2026-09-11 22:13 ` [PATCH v2 2/2] KVM: Account mem_attr_array nodes to the caller's memcg David Ballesteros
2026-09-11 20:32 ` [PATCH v2 1/2] KVM: Bound per-VM GFN materialization in KVM_SET_MEMORY_ATTRIBUTES David Ballesteros
2026-09-11 20:45 ` sashiko-bot [this message]
2026-09-11 20:32 ` [PATCH v2 2/2] KVM: Account mem_attr_array nodes to the caller's memcg David Ballesteros
2026-09-11 20:44 ` sashiko-bot
2026-09-12 8:48 ` [syzbot ci] Re: KVM: unbounded per-VM kernel memory growth via KVM_SET_MEMORY_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=20260911204531.E91311F00893@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=davimaba.v@proton.me \
--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 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.