Re: [PATCH v3 12/13] KVM: pfncache: use a dedicated invalidation sequence for cache refresh
From: Paul Durrant
Date: Wed Sep 02 2026 - 08:22:12 EST
On 31/08/2026 22:26, David Woodhouse wrote:
From: David Woodhouse <dwmw@xxxxxxxxxxxx>Reviewed-by: Paul Durrant <paul@xxxxxxx>
The gfn_to_pfn_cache refresh path guards against mmu notifier
invalidations which complete while it has dropped gpc->lock for the
HVA->PFN lookup: hva_to_pfn_retry() samples kvm->mmu_invalidate_seq
and retries if it changed, or if mn_active_invalidate_count is still
elevated.
That is insufficient for HVA-based caches. mmu_invalidate_seq is only
advanced by kvm_mmu_invalidate_end() when the invalidated range
overlaps a memslot, and an HVA-based cache (e.g. the Xen shared_info
page mapped with KVM_XEN_ATTR_TYPE_SHARED_INFO_HVA) need not be backed
by any memslot at all. An invalidation of the cached HVA which starts
and ends entirely within the lookup window is thus invisible to the
retry check: mn_active_invalidate_count is back to zero and the
sequence never moved. The refresh then publishes a mapping of a page
which has already been freed, and the next reader dereferences it:
BUG: KASAN: use-after-free in kvm_xen_shared_info_init+0x3c6/0x440
Read of size 4 at addr ffff8880599c2900 by task syz.2.383/7257
Since gfn_to_pfn_cache_invalidate_start() deliberately skips caches
which are not currently valid (including one whose refresh is in
progress, as the refresh clears the valid flag before dropping the
lock), the retry check is the only line of defence, and it must fire
for *any* invalidation, not just those hitting a memslot.
Add a dedicated kvm->gpc_invalidate_seq, incremented by every
kvm_mmu_notifier_invalidate_range_end() under mn_invalidate_lock
before mn_active_invalidate_count is decremented, and check it in
hva_to_pfn_retry() instead of mmu_invalidate_seq. Incrementing in
range_end() in the same critical section as the in-progress count
also closes the variant where the cache is activated with the
contested HVA only after invalidate_range_start() has run.
The same bug is also reachable through the per-vCPU vcpu_info cache
(KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA), where the stale mapping is
then dereferenced by kvm_setup_guest_pvclock() on the next KVM_RUN:
BUG: KASAN: use-after-free in kvm_setup_guest_pvclock+0x5bf/0x660
This intentionally makes refresh retry on *unrelated* mmu notifier
events; restoring precision (and reworking the GPC locking more
generally) is left for a subsequent series.
Reproducers: https://david.woodhou.se/xen_shinfo_race.c
https://david.woodhou.se/vcpu_info_race.c
Suggested-by: Sean Christopherson <seanjc@xxxxxxxxxx>
Reported-by: syzbot+0948c82180d475ad24e2@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://lore.kernel.org/all/6a0c5f2c.a00a0220.2c7954.0000.GAE@xxxxxxxxxx/
Tested-by: syzbot+0948c82180d475ad24e2@xxxxxxxxxxxxxxxxxxxxxxxxx
Reported-by: syzbot+fb7c2dd166d3ea63df2a@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://lore.kernel.org/all/6a426dd2.854d4ab9.360e1d.0008.GAE@xxxxxxxxxx/
Fixes: b9220d32799a ("KVM: x86/xen: allow shared_info to be mapped by fixed HVA")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
Assisted-by: Claude:claude-mythos-5
---
include/linux/kvm_host.h | 2 ++
virt/kvm/kvm_main.c | 10 ++++++++++
virt/kvm/pfncache.c | 18 +++++++++---------
3 files changed, 21 insertions(+), 9 deletions(-)