DF-0990 / fix.diff
diff --git a/sys/bus/pci/pci.c b/sys/bus/pci/pci.c index 0000000..1111111 100644 --- a/sys/bus/pci/pci.c +++ b/sys/bus/pci/pci.c @@ -873,21 +873,39 @@ /* * Read capability entries. + * + * Defensive bound: PCI capability pointers are 1-byte offsets into + * config space, must be >= 0x40 and 4-byte aligned. The maximum + * number of distinct valid pointers is (256 - 64) / 4 = 48. Cap the + * loop iteration count to detect malicious or buggy devices with a + * cyclic cap list (e.g. a self-loop cap@0x80 next=0x80) that would + * otherwise spin here forever doing config reads at full CPU speed. */ - while (pci_fixup_nextptr(&nextptr)) { - const struct pci_read_cap *rc; - int ptr = nextptr; + { + int cap_iter = 0; + while (pci_fixup_nextptr(&nextptr)) { + const struct pci_read_cap *rc; + int ptr = nextptr; - /* Find the next entry */ - nextptr = REG(ptr + PCICAP_NEXTPTR, 1); - - /* Process this entry */ - val = REG(ptr + PCICAP_ID, 1); - for (rc = pci_read_caps; rc->read_cap != NULL; ++rc) { - if (rc->cap == val) { - rc->read_cap(pcib, ptr, nextptr, cfg); + if (++cap_iter > 64) { + kprintf("pci%d:%d:%d:%d: capability list walk " + "exceeded %d entries (cyclic list?), aborting\n", + cfg->domain, cfg->bus, cfg->slot, cfg->func, + cap_iter - 1); break; } + + /* Find the next entry */ + nextptr = REG(ptr + PCICAP_NEXTPTR, 1); + + /* Process this entry */ + val = REG(ptr + PCICAP_ID, 1); + for (rc = pci_read_caps; rc->read_cap != NULL; ++rc) { + if (rc->cap == val) { + rc->read_cap(pcib, ptr, nextptr, cfg); + break; + } + } } } |