diff options
| author | Fuad Tabba <fuad.tabba@linux.dev> | 2026-08-07 11:41:01 +0100 |
|---|---|---|
| committer | Oliver Upton <oupton@kernel.org> | 2026-08-07 09:06:27 -0700 |
| commit | 9b10fb74e4b661543d188701bd4d024fc5c18f58 (patch) | |
| tree | a662b420496304ef94347fbcf861e2b47d28b02b | |
| parent | 52d044d6e76fcd48ee384f0628a39d56b04de95b (diff) | |
| download | linux-stable-9b10fb74e4b661543d188701bd4d024fc5c18f58.tar.gz linux-stable-9b10fb74e4b661543d188701bd4d024fc5c18f58.zip | |
KVM: arm64: vgic-its: Don't save collections the table cannot hold
A guest that disables the ITS and rewrites GITS_BASER with fewer pages,
VALID still set, keeps every collection it mapped against the larger
table: KVM stores the new BASER unconditionally and frees the list only
when VALID is cleared. vgic_its_save_collection_table() then walks the
whole list, writing up to 448K past the end of the table, and saves
collection IDs that vgic_its_restore_cte() rejects, so the save succeeds
and the restore fails with -EINVAL on the destination. The overrun stays
in guest memory, as vgic_write_guest_lock() validates every gfn.
Validate each collection against the current table with
vgic_its_check_id() and return -EINVAL, as vgic_its_save_device_tables()
does for devices. Collection IDs are unique and the collection table is
never indirect, so the check also bounds the walk.
Fixes: ea1ad53e1e31a ("KVM: arm64: vgic-its: Collection table save/restore")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
Link: https://patch.msgid.link/20260807104102.2410744-4-fuad.tabba@linux.dev
Signed-off-by: Oliver Upton <oupton@kernel.org>
| -rw-r--r-- | arch/arm64/kvm/vgic/vgic-its.c | 3 |
1 files changed, 3 insertions, 0 deletions
diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 6f3ea75b4c23..d8b784f69abc 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -2529,6 +2529,9 @@ static int vgic_its_save_collection_table(struct vgic_its *its) max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K; list_for_each_entry(collection, &its->collection_list, coll_list) { + if (!vgic_its_check_id(its, baser, collection->collection_id, NULL)) + return -EINVAL; + ret = vgic_its_save_cte(its, collection, gpa); if (ret) return ret; |
