⬢ DragonFlyBSD Kernel Audit
DF-5119 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/misc/efirt/efidev.c
+++ b/sys/dev/misc/efirt/efidev.c
@@ -39,6 +39,22 @@
 #include <machine/efi.h>
 #include <sys/efiio.h>
 
+/*
+ * DF-5119: sanity limits for user-supplied buffer sizes.
+ *
+ * efi_var_ioc::datasize / ::namesize come straight from the ioctl and
+ * were passed unvalidated to kmalloc(M_WAITOK).  Huge sizes panic the
+ * kernel ("kmem_slab_alloc(): kernel_map ran out of space!") and sizes
+ * near SIZE_MAX wrap round_page() down to 0, producing a zero-length
+ * kernel_map entry (duplicate-address map corruption) and a pointer that
+ * kfree() misclassifies as a slab-zone chunk.  Real EFI variables are
+ * orders of magnitude smaller (UEFI firmware typically caps them at
+ * 64KB), so cap requests at 1 MiB (matching Linux efivarfs) and require
+ * a name buffer big enough to hold at least the NUL terminator.
+ */
+#define	EFI_IOC_MAX_DATA	(1024 * 1024)
+#define	EFI_IOC_MAX_NAME	(1024 * 1024)
+
 static struct lock efidev_lock = LOCK_INITIALIZER("efidev", 0, 0);
 
 static d_ioctl_t efidev_ioctl;
@@ -102,6 +118,12 @@
 		void *data;
 		efi_char *name;
 
+		if (ev->datasize > EFI_IOC_MAX_DATA ||
+		    ev->namesize < sizeof(efi_char) ||
+		    ev->namesize > EFI_IOC_MAX_NAME) {
+			error = EINVAL;
+			break;
+		}
 		data = kmalloc(ev->datasize, M_TEMP, M_WAITOK);
 		name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
 		error = copyin(ev->name, name, ev->namesize);
@@ -137,6 +159,11 @@
 		struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
 		efi_char *name;
 
+		if (ev->namesize < sizeof(efi_char) ||
+		    ev->namesize > EFI_IOC_MAX_NAME) {
+			error = EINVAL;
+			break;
+		}
 		name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
 		error = copyin(ev->name, name, ev->namesize);
 		if (error)
@@ -160,6 +187,12 @@
 		void *data = NULL;
 		efi_char *name;
 
+		if (ev->datasize > EFI_IOC_MAX_DATA ||
+		    ev->namesize < sizeof(efi_char) ||
+		    ev->namesize > EFI_IOC_MAX_NAME) {
+			error = EINVAL;
+			break;
+		}
 		/* datasize == 0 -> delete (more or less) */
 		if (ev->datasize > 0)
 			data = kmalloc(ev->datasize, M_TEMP, M_WAITOK);