Re: [PATCH] KVM: pfncache: track all MMU notifier invalidations
From: David Lee
Date: Tue Aug 11 2026 - 13:18:30 EST
Hi Sean,
Please refer to the attached reproducer.
Required configuration:
* CONFIG_KVM
* CONFIG_KVM_XEN
* An x86 KVM host
CONFIG_KASAN is not required for the vulnerability, but it is used to
produce the attached diagnostic output.
========== Reproduction ==========
Step 1: Build and boot an affected x86 kernel with CONFIG_KVM,
CONFIG_KVM_XEN, and CONFIG_KASAN enabled.
The confirmed kernel source revision is:
~
a13c140cc289c0b7b3770bce5b3ad42ab35074aa
~
Step 2: Compile the attached `poc.c`:
~
gcc -static -O2 -Wall -Wextra -pthread -o poc poc.c
~
Step 3: Run the PoC as a non-root user that has permission to open
`/dev/kvm`:
~
./poc
~
The PoC creates a KVM VM without any memslots, maps one anonymous page,
and runs two threads on separate CPUs. One thread repeatedly activates
and deactivates the Xen shared-info HVA cache. The other repeatedly
discards the backing page with `MADV_DONTNEED`.
The race is timing-dependent. The supplied PoC runs for 90 seconds. On
the confirmed test system it produced the attached KASAN report in
`splash.txt`.
Best regards,
David
Please refer to the attached reproducer.
Required configuration:
* CONFIG_KVM
* CONFIG_KVM_XEN
* An x86 KVM host
CONFIG_KASAN is not required for the vulnerability, but it is used to
produce the attached diagnostic output.
========== Reproduction ==========
Step 1: Build and boot an affected x86 kernel with CONFIG_KVM,
CONFIG_KVM_XEN, and CONFIG_KASAN enabled.
The confirmed kernel source revision is:
~
a13c140cc289c0b7b3770bce5b3ad42ab35074aa
~
Step 2: Compile the attached `poc.c`:
~
gcc -static -O2 -Wall -Wextra -pthread -o poc poc.c
~
Step 3: Run the PoC as a non-root user that has permission to open
`/dev/kvm`:
~
./poc
~
The PoC creates a KVM VM without any memslots, maps one anonymous page,
and runs two threads on separate CPUs. One thread repeatedly activates
and deactivates the Xen shared-info HVA cache. The other repeatedly
discards the backing page with `MADV_DONTNEED`.
The race is timing-dependent. The supplied PoC runs for 90 seconds. On
the confirmed test system it produced the attached KASAN report in
`splash.txt`.
Best regards,
David
On Tue, Aug 11, 2026 at 10:40 AM Sean Christopherson <seanjc@xxxxxxxxxx> wrote:
+David W
On Mon, Aug 10, 2026, David Lee wrote:
> From: Kyle Zeng <kylebot@xxxxxxxxxx>
>
> There is a race condition in KVM's gfn-to-pfn cache refresh and MMU
> notifier handling. An HVA-backed cache can publish a stale PFN and
> kernel virtual address after the corresponding userspace mapping has
> been invalidated. The Xen shared-info HVA interface immediately reads
> and writes through that stale address, resulting in a host-kernel
> use-after-free.
>
> The cache refresh path in virt/kvm/pfncache.c drops gpc->lock while
> resolving and mapping an HVA. It uses mn_active_invalidate_count and
> mmu_invalidate_seq to detect an MMU notifier interval that overlaps
> this unlocked window. However, mmu_invalidate_seq is advanced only
> when the invalidated HVA overlaps a KVM memslot. HVA-backed caches are
> explicitly allowed to refer to memory outside all memslots. If such an
> invalidation starts and finishes while gpc->valid is false, the active
> count returns to zero without a sequence change and the refresh accepts
> a stale PFN.
>
> An unprivileged process with access to /dev/kvm can reach this path with
> KVM_XEN_ATTR_TYPE_SHARED_INFO_HVA. KASAN-detected use-after-free in
> kvm_xen_shared_info_init(). The affected function reads and writes Xen
> wall-clock fields through the stale mapping, so the issue can cause a
> host-kernel crash and memory corruption.
>
> The attached KASAN output confirms:
>
> BUG: KASAN: use-after-free in kvm_xen_shared_info_init+0x344/0x3d0 [kvm]
> Read of size 4 at addr ffff888046000900 by task poc/1266
>
> Add a notifier-specific sequence that advances for every completed
> invalidate interval before mn_active_invalidate_count is decremented,
> and use that sequence for pfncache retry. The existing barrier pairing
> then guarantees refresh observes either an active invalidation or a
> sequence change.
>
> Fixes: 721f5b0dda78 ("KVM: pfncache: allow a cache to be activated with a fixed (userspace) HVA")
> Cc: stable@xxxxxxxxxxxxxxx # 6.9+
> Assisted-by: Codex:gpt-5.6-sol Codex:gpt-5.5-cyber
> Signed-off-by: Kyle Zeng <kylebot@xxxxxxxxxx>
> Co-developed-by: David Lee <david.lee@xxxxxxxxxxxxxxx>
> Signed-off-by: David Lee <david.lee@xxxxxxxxxxxxxxx>
> ---
> Bug found and triaged by OpenAI Security Research and
> validated by Trail of Bits.
>
> Trail of Bits has a reproducer for this bug that triggers a
> KASAN use-after-free and can share if needed.
Go ahead and share, syzbot has been reporting this race for some time, i.e. how
to repro this isn't exactly a secret, and the more testcases the better. This is
also like the fourth or fifth proposed fix for the bug (bugs?). I think the most
recent version of the fix we are pursuing is here:
https://lore.kernel.org/all/20260811094829.98794-12-dwmw2@xxxxxxxxxxxxx
Thanks!
[ 232.327844] BUG: KASAN: use-after-free in kvm_xen_shared_info_init+0x344/0x3d0 [kvm]
[ 232.327844] Read of size 4 at addr ffff888046000900 by task poc/1266
[ 232.327844]
[ 232.327844] CPU: 0 UID: 1000 PID: 1266 Comm: poc Not tainted 7.2.0-rc3-kasan #1 PREEMPT(lazy)
[ 232.327844] Hardware name: QEMU Ubuntu 26.04 PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1ubuntu1 04/01/2014
[ 232.327844] Call Trace:
[ 232.327844] <TASK>
[ 232.327844] dump_stack_lvl+0x5f/0x90
[ 232.327844] print_report+0x15b/0x4ec
[ 232.327844] ? __pfx__raw_spin_lock_irqsave+0x10/0x10
[ 232.327844] ? kasan_addr_to_slab+0xd/0x80
[ 232.327844] kasan_report+0xf2/0x130
[ 232.327844] ? kvm_xen_shared_info_init+0x344/0x3d0 [kvm]
[ 232.327844] ? kvm_xen_shared_info_init+0x344/0x3d0 [kvm]
[ 232.327844] __asan_report_load_n_noabort+0xf/0x20
[ 232.327844] kvm_xen_shared_info_init+0x344/0x3d0 [kvm]
[ 232.327844] kvm_xen_hvm_set_attr+0xbe0/0x1a40 [kvm]
[ 232.327844] ? __pfx_kvm_xen_hvm_set_attr+0x10/0x10 [kvm]
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] kvm_arch_vm_ioctl+0x283/0x1760 [kvm]
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? __pfx_kvm_arch_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? __pfx_mutex_unlock+0x10/0x10
[ 232.327844] ? __pfx__raw_spin_lock+0x10/0x10
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? mutex_unlock+0x83/0xe0
[ 232.327844] ? __pfx_mutex_unlock+0x10/0x10
[ 232.327844] ? kvm_gpc_deactivate+0x8d/0x530 [kvm]
[ 232.327844] ? kvm_xen_hvm_set_attr+0x377/0x1a40 [kvm]
[ 232.327844] ? kvm_arch_vm_ioctl+0x283/0x1760 [kvm]
[ 232.327844] ? __pfx_kvm_xen_hvm_set_attr+0x10/0x10 [kvm]
[ 232.327844] ? __pfx_kvm_arch_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? kvm_arch_vm_ioctl+0x283/0x1760 [kvm]
[ 232.327844] ? kvm_gpc_deactivate+0x8d/0x530 [kvm]
[ 232.327844] ? __pfx_kvm_xen_hvm_set_attr+0x10/0x10 [kvm]
[ 232.327844] ? __pfx_kvm_xen_hvm_set_attr+0x10/0x10 [kvm]
[ 232.327844] ? kvm_arch_vm_ioctl+0x283/0x1760 [kvm]
[ 232.327844] ? kvm_arch_vm_ioctl+0x283/0x1760 [kvm]
[ 232.327844] kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? __pfx_kvm_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? __pfx_kvm_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? __pfx_kvm_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? __pfx_kvm_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? __pfx_kvm_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? __pfx_kvm_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? kvm_vm_ioctl+0x12f4/0x3fe0 [kvm]
[ 232.327844] ? __pfx_do_vfs_ioctl+0x10/0x10
[ 232.327844] ? __pfx_do_vfs_ioctl+0x10/0x10
[ 232.327844] ? __pfx_kvm_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? __pfx_kvm_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? __pfx_do_vfs_ioctl+0x10/0x10
[ 232.327844] ? __pfx_kvm_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? __pfx_kvm_vm_ioctl+0x10/0x10 [kvm]
[ 232.327844] ? __pfx_do_vfs_ioctl+0x10/0x10
[ 232.327844] ? __pfx_do_vfs_ioctl+0x10/0x10
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? fdget+0x30d/0x4b0
[ 232.327844] __x64_sys_ioctl+0x147/0x1e0
[ 232.327844] x64_sys_call+0x103b/0x2390
[ 232.327844] do_syscall_64+0xdd/0x640
[ 232.327844] ? __pfx_do_vfs_ioctl+0x10/0x10
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? fput+0x27/0xa0
[ 232.327844] ? __kasan_check_read+0x11/0x20
[ 232.327844] ? fpregs_assert_state_consistent+0x5c/0x100
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? fput+0x27/0xa0
[ 232.327844] ? __kasan_check_read+0x11/0x20
[ 232.327844] ? fpregs_assert_state_consistent+0x5c/0x100
[ 232.327844] ? do_syscall_64+0x11a/0x640
[ 232.327844] ? __kasan_check_read+0x11/0x20
[ 232.327844] ? fpregs_assert_state_consistent+0x5c/0x100
[ 232.327844] ? do_syscall_64+0x11a/0x640
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? fput+0x27/0xa0
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? __kasan_check_write+0x14/0x30
[ 232.327844] ? fput+0x27/0xa0
[ 232.327844] ? __kasan_check_read+0x11/0x20
[ 232.327844] ? fpregs_assert_state_consistent+0x5c/0x100
[ 232.327844] ? do_syscall_64+0x11a/0x640
[ 232.327844] ? do_syscall_64+0x11a/0x640
[ 232.327844] ? do_syscall_64+0x11a/0x640
[ 232.327844] ? do_syscall_64+0x94/0x640
[ 232.327844] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 232.327844] RIP: 0033:0x41665d
[ 232.327844] Code: 04 25 28 00 00 00 48 89 45 c8 31 c0 48 8d 45 10 c7 45 b0 10 00 00 00 48 89 45 b8 48 8d 45 d0 48 89 45 c0 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1a 48 8b 45 c8 64 48 2b 04 25 28 00 00 00
[ 232.327844] RSP: 002b:0000778a148a4040 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
[ 232.327844] RAX: ffffffffffffffda RBX: 0000000000000004 RCX: 000000000041665d
[ 232.327844] RDX: 0000778a148a40a0 RSI: 000000004048aec9 RDI: 0000000000000004
[ 232.327844] RBP: 0000778a148a4090 R08: 0000000000000000 R09: 0000000000000001
[ 232.327844] R10: 0000000000000008 R11: 0000000000000246 R12: 0000778a148a46c0
[ 232.327844] R13: 00007ffeee4a0ee0 R14: 0000778a148a4ce4 R15: 00007ffeee4a0fd7
[ 232.327844] </TASK>
[ 232.327844]
[ 232.327844] The buggy address belongs to the physical page:
[ 232.327844] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x778a150a6 pfn:0x46000
[ 232.327844] flags: 0xfffffc0000000(node=0|zone=1|lastcpupid=0x1fffff)
[ 232.327844] raw: 000fffffc0000000 ffffea00010e1fc8 ffff88811aecb670 0000000000000000
[ 232.327844] raw: 0000000778a150a6 0000000000000000 00000000ffffffff 0000000000000000
[ 232.327844] page dumped because: kasan: bad access detected
[ 232.327844]
[ 232.327844] Memory state around the buggy address:
[ 232.327844] ffff888046000800: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[ 232.327844] ffff888046000880: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[ 232.327844] >ffff888046000900: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[ 232.327844] ^
[ 232.327844] ffff888046000980: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[ 232.327844] ffff888046000a00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[ 232.327844] ==================================================================
[ 232.327844] Disabling lock debugging due to kernel taint
Attachment:
poc.c
Description: Binary data