DragonFlyBSD Kernel Audit
DF-1113 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/platform/pc64/acpica/acpi_sdt.c b/sys/platform/pc64/acpica/acpi_sdt.c
--- a/sys/platform/pc64/acpica/acpi_sdt.c
+++ b/sys/platform/pc64/acpica/acpi_sdt.c
@@ -49,6 +49,14 @@
 		kprintf("ACPI SDT: " fmt , ##arg); \
 } while (0)
 
+/*
+ * Sanity upper bound on the size of a single ACPI table.  Legitimate
+ * firmware tables are well under 1 MB; this guard rejects malicious or
+ * buggy Length values near UINT32_MAX that would otherwise drive a
+ * multi-GiB kmem_alloc_nofault in pmap_mapdev_attr and panic the boot.
+ */
+#define SDT_MAX_TABLE_SIZE	(1U * 1024 * 1024)
+
 typedef	vm_paddr_t		(*sdt_search_t)(vm_paddr_t, const uint8_t *);
 
 static const ACPI_TABLE_RSDP	*sdt_rsdp_search(const uint8_t *, int);
@@ -147,6 +155,18 @@
 	if (mapsz < sizeof(*sdth))
 		return NULL;
 
+	/*
+	 * Reject obviously-malformed / malicious firmware Length values.
+	 * The largest legitimate ACPI table is well under 1 MB (the DSDT
+	 * on this guest is 0x1AF8 bytes); a Length near UINT32_MAX makes
+	 * pmap_mapdev_attr attempt a multi-GiB kmem_alloc_nofault that
+	 * fails and panics ("pmap_mapdev: Couldn't alloc kernel virtual
+	 * memory", pmap.c:6203).  Cap the mapping at a generous upper
+	 * bound so buggy / malicious firmware can't panic the boot.
+	 */
+	if (mapsz > SDT_MAX_TABLE_SIZE)
+		return NULL;
+
 	return pmap_mapdev(paddr, mapsz);
 }