⬢ DragonFlyBSD Kernel Audit
DF-5477 / fix.diff
← back to finding ↓ download raw
--- a/sys/platform/pc64/x86_64/efirt.c
+++ b/sys/platform/pc64/x86_64/efirt.c
@@ -466,6 +466,14 @@
 	lockuninit(&resettodr_lock);
 }
 
+/*
+ * Sanity cap for the firmware-fed system-table config entry count.  The
+ * EFI specification has no architected maximum, but real firmware tables
+ * carry a handful of entries; anything past this is a lie that would
+ * otherwise drive an unbounded walk over kernel memory.
+ */
+#define	EFI_CFGTBL_MAX	256
+
 int
 efi_get_table(struct uuid *uuid, void **ptr)
 {
@@ -474,10 +482,23 @@
 
 	if (efi_cfgtbl == NULL)
 		return (ENXIO);
+	/*
+	 * efi_cfgtbl holds the firmware-fed st_cfgtbl value, which is a
+	 * PHYSICAL address: this kernel never calls SetVirtualAddressMap
+	 * (see the 1:1 mapping design comment above efi_enter()), so table
+	 * pointers stay physical for the lifetime of the system.  They must
+	 * be translated into the direct map before they can be dereferenced
+	 * as kernel virtual addresses.
+	 */
 	count = efi_systbl->st_entries;
-	ct = efi_cfgtbl;
+	if (count > EFI_CFGTBL_MAX)
+		return (EOVERFLOW);
+	ct = (struct efi_cfgtbl *)PHYS_TO_DMAP(
+	    (vm_paddr_t)(uintptr_t)efi_systbl->st_cfgtbl);
 	while (count--) {
 		if (!bcmp(&ct->ct_uuid, uuid, sizeof(*uuid))) {
+			if (ct->ct_data >= DMAP_MAX_ADDRESS - DMAP_MIN_ADDRESS)
+				return (ERANGE);
 			*ptr = (void *)PHYS_TO_DMAP(ct->ct_data);
 			return (0);
 		}