⬢ DragonFlyBSD Kernel Audit
DF-4569 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/drm_ioctl.c
+++ b/sys/dev/drm/drm_ioctl.c
@@ -830,6 +830,31 @@
 		ioctl = &drm_ioctls[nr];
 	}
 
+	/*
+	 * DF-4569: On DragonFly the ioctl argument buffer is allocated by
+	 * mapped_ioctl() (sys/kern/sys_generic.c) with the size taken from
+	 * the USER-supplied command word (IOCPARM_LEN(cmd)); IOC_VOID
+	 * commands always receive the 128-byte on-stack buffer and the
+	 * final copyout ships `size' bytes back to userspace.  Our handlers
+	 * interpret `data' as the full kernel structure for ioctl->cmd, so
+	 * a user-mangled encoding (short size, wrong direction bits) turns
+	 * every handler into an out-of-bounds access (drm_getstats() memsets
+	 * sizeof(struct drm_stats) == 248 bytes; drm_getmagic() returns 0
+	 * and leaks kernel stack through the oversized copyout).
+	 *
+	 * Upstream Linux enforces the same rule by overriding the command
+	 * with its own definition (cmd = ioctl->cmd, with asize =
+	 * max(_IOC_SIZE(cmd), _IOC_SIZE(ioctl->cmd))).  As the copy happens
+	 * outside drm_ioctl() here, require the exact canonical command:
+	 * group byte, direction bits and size bits included.
+	 */
+	if ((unsigned int)cmd != ioctl->cmd) {
+		DRM_DEBUG("ioctl cmd mismatch: 0x%lx != 0x%x (%s)\n",
+			  (unsigned long)cmd, ioctl->cmd, ioctl->name);
+		retcode = EINVAL;
+		goto err_i1;
+	}
+
 	DRM_DEBUG_IOCTL("pid=%d, dev=0x%lx, auth=%d, %s\n",
 		  DRM_CURRENTPID,
 		  (long)dev->dev,