[PATCH 2/3] KVM: arm64: ptdump: Store both mmu and kvm pointers in kvm_ptdump_guest_state

From: Wei-Lin Chang

Date: Tue Jun 23 2026 - 10:25:55 EST


In the nested case, the nested mmu could be freed when .release() is
called, e.g. another process closes the ptdump debugfs file after the VM
is destroyed. This causes a UAF when the nested mmu is accessed to reach
kvm for kvm_put_kvm(). Store the kvm pointer in kvm_ptdump_guest_state
so that it can be reached without going through the nested mmu.

Fixes: 204f7c018d76 ("KVM: arm64: ptdump: Make KVM ptdump code s2 mmu aware")
Signed-off-by: Wei-Lin Chang <weilin.chang@xxxxxxx>
---
arch/arm64/kvm/ptdump.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kvm/ptdump.c b/arch/arm64/kvm/ptdump.c
index 2a6afe67646a..a089e87ea366 100644
--- a/arch/arm64/kvm/ptdump.c
+++ b/arch/arm64/kvm/ptdump.c
@@ -19,7 +19,12 @@
#define KVM_PGTABLE_MAX_LEVELS (KVM_PGTABLE_LAST_LEVEL + 1)
#define S2FNAMESZ sizeof("0x0123456789abcdef-0x0123456789abcdef-s2-disabled")

+/*
+ * Nested mmus could be freed when .release() is called, so also keep the kvm
+ * pointer for kvm_put_kvm().
+ */
struct kvm_ptdump_guest_state {
+ struct kvm *kvm;
struct kvm_s2_mmu *mmu;
struct ptdump_pg_state parser_state;
struct addr_marker ipa_marker[MARKERS_LEN];
@@ -133,6 +138,7 @@ static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm_s2_mmu
st->ipa_marker[1].start_address = BIT(pgtable->ia_bits);

st->mmu = mmu;
+ st->kvm = kvm_s2_mmu_to_kvm(mmu);
return st;
}

@@ -197,11 +203,10 @@ static int kvm_ptdump_guest_open(struct inode *m, struct file *file)

static int kvm_ptdump_guest_close(struct inode *m, struct file *file)
{
- struct kvm *kvm = kvm_s2_mmu_to_kvm(m->i_private);
void *st = ((struct seq_file *)file->private_data)->private;

+ kvm_put_kvm(((struct kvm_ptdump_guest_state *)st)->kvm);
kfree(st);
- kvm_put_kvm(kvm);

return single_release(m, file);
}
--
2.43.0