Skip to content

Commit c000a26

Browse files
renzhengeekMarc Zyngier
authored andcommitted
KVM: arm64: vgic: Fix exit condition in scan_its_table()
With some PCIe topologies, restoring a guest fails while parsing the ITS device tables. Reproducer hints: 1. Create ARM virt VM with pxb-pcie bus which adds extra host bridges, with qemu command like: ``` -device pxb-pcie,bus_nr=8,id=pci.x,numa_node=0,bus=pcie.0 \ -device pcie-root-port,..,bus=pci.x \ ... -device pxb-pcie,bus_nr=37,id=pci.y,numa_node=1,bus=pcie.0 \ -device pcie-root-port,..,bus=pci.y \ ... ``` 2. Ensure the guest uses 2-level device table 3. Perform VM migration which calls save/restore device tables In that setup, we get a big "offset" between 2 device_ids, which makes unsigned "len" round up a big positive number, causing the scan loop to continue with a bad GPA. For example: 1. L1 table has 2 entries; 2. and we are now scanning at L2 table entry index 2075 (pointed to by L1 first entry) 3. if next device id is 9472, we will get a big offset: 7397; 4. with unsigned 'len', 'len -= offset * esz', len will underflow to a positive number, mistakenly into next iteration with a bad GPA; (It should break out of the current L2 table scanning, and jump into the next L1 table entry) 5. that bad GPA fails the guest read. Fix it by stopping the L2 table scan when the next device id is outside of the current table, allowing the scan to continue from the next L1 table entry. Thanks to Eric Auger for the fix suggestion. Fixes: 920a7a8 ("KVM: arm64: vgic-its: Add infrastructure for tableookup") Suggested-by: Eric Auger <[email protected]> Signed-off-by: Eric Ren <[email protected]> [maz: commit message tidy-up] Signed-off-by: Marc Zyngier <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/r/d9c3a564af9e2c5bf63f48a7dcbf08cd593c5c0b.1665802985.git.renzhengeek@gmail.com
1 parent bde971a commit c000a26

File tree

1 file changed

+4
-1
lines changed

1 file changed

+4
-1
lines changed

arch/arm64/kvm/vgic/vgic-its.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2149,7 +2149,7 @@ static int scan_its_table(struct vgic_its *its, gpa_t base, int size, u32 esz,
21492149

21502150
memset(entry, 0, esz);
21512151

2152-
while (len > 0) {
2152+
while (true) {
21532153
int next_offset;
21542154
size_t byte_offset;
21552155

@@ -2162,6 +2162,9 @@ static int scan_its_table(struct vgic_its *its, gpa_t base, int size, u32 esz,
21622162
return next_offset;
21632163

21642164
byte_offset = next_offset * esz;
2165+
if (byte_offset >= len)
2166+
break;
2167+
21652168
id += next_offset;
21662169
gpa += byte_offset;
21672170
len -= byte_offset;

0 commit comments

Comments
 (0)