Skip to content

Commit a9f0e3d

Browse files
Fuad TabbaMarc Zyngier
authored andcommitted
KVM: arm64: Reload PTE after invoking walker callback on preorder traversal
The preorder callback on the kvm_pgtable_stage2_map() path can replace a table with a block, then recursively free the detached table. The higher-level walking logic stashes the old page table entry and then walks the freed table, invoking the leaf callback and potentially freeing pgtable pages prematurely. In normal operation, the call to tear down the detached stage-2 is indirected and uses an RCU callback to trigger the freeing. RCU is not available to pKVM, which is where this bug is triggered. Change the behavior of the walker to reload the page table entry after invoking the walker callback on preorder traversal, as it does for leaf entries. Tested on Pixel 6. Fixes: 5c359cc ("KVM: arm64: Tear down unlinked stage-2 subtree after break-before-make") Suggested-by: Oliver Upton <oliver.upton@linux.dev> Signed-off-by: Fuad Tabba <tabba@google.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20230522103258.402272-1-tabba@google.com
1 parent d282fa3 commit a9f0e3d

File tree

2 files changed

+16
-4
lines changed

2 files changed

+16
-4
lines changed

arch/arm64/include/asm/kvm_pgtable.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -632,9 +632,9 @@ int kvm_pgtable_stage2_flush(struct kvm_pgtable *pgt, u64 addr, u64 size);
632632
*
633633
* The walker will walk the page-table entries corresponding to the input
634634
* address range specified, visiting entries according to the walker flags.
635-
* Invalid entries are treated as leaf entries. Leaf entries are reloaded
636-
* after invoking the walker callback, allowing the walker to descend into
637-
* a newly installed table.
635+
* Invalid entries are treated as leaf entries. The visited page table entry is
636+
* reloaded after invoking the walker callback, allowing the walker to descend
637+
* into a newly installed table.
638638
*
639639
* Returning a negative error code from the walker callback function will
640640
* terminate the walk immediately with the same error code.

arch/arm64/kvm/hyp/pgtable.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,14 +209,26 @@ static inline int __kvm_pgtable_visit(struct kvm_pgtable_walk_data *data,
209209
.flags = flags,
210210
};
211211
int ret = 0;
212+
bool reload = false;
212213
kvm_pteref_t childp;
213214
bool table = kvm_pte_table(ctx.old, level);
214215

215-
if (table && (ctx.flags & KVM_PGTABLE_WALK_TABLE_PRE))
216+
if (table && (ctx.flags & KVM_PGTABLE_WALK_TABLE_PRE)) {
216217
ret = kvm_pgtable_visitor_cb(data, &ctx, KVM_PGTABLE_WALK_TABLE_PRE);
218+
reload = true;
219+
}
217220

218221
if (!table && (ctx.flags & KVM_PGTABLE_WALK_LEAF)) {
219222
ret = kvm_pgtable_visitor_cb(data, &ctx, KVM_PGTABLE_WALK_LEAF);
223+
reload = true;
224+
}
225+
226+
/*
227+
* Reload the page table after invoking the walker callback for leaf
228+
* entries or after pre-order traversal, to allow the walker to descend
229+
* into a newly installed or replaced table.
230+
*/
231+
if (reload) {
220232
ctx.old = READ_ONCE(*ptep);
221233
table = kvm_pte_table(ctx.old, level);
222234
}

0 commit comments

Comments
 (0)