DF-4569 / fix.diff
--- 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, |