DF-4569 / dfioctl_rep.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | /* * dfioctl_rep.c -- kernel replica of the DragonFly DRM ioctl dispatch * semantics that matter for DF-4569. * * The QEMU guest has no Intel GPU, so /dev/dri/card0 cannot exist and * sys/dev/drm/drm_ioctl.c:drm_ioctl() itself cannot run there. This module * reproduces EXACTLY the two dispatch properties under test: * * (1) drm_ioctl() dispatches on nr = DRM_IOCTL_NR(cmd) == (cmd & 0xff) * only. The group byte, the IOC direction bits and -- critically -- * the IOCPARM_LEN(cmd) size bits of the user-supplied command are * never examined (sys/dev/drm/drm_ioctl.c:809, 819-831, 847, 854). * * (2) The kernel argument buffer (`data') handed to the handler is the * buffer allocated by mapped_ioctl() in sys/kern/sys_generic.c, * sized from the USER-controlled size bits: * sys/kern/sys_generic.c:668 size = IOCPARM_LEN(com); * sys/kern/sys_generic.c:674 kmalloc(size) only when size > 128 * and (com & IOC_VOID) == 0; * otherwise data = 128-byte stack buf. * * It registers /dev/dfioctl with two handlers that are byte-for-byte * faithful replicas of the corresponding DRM core handlers: * * nr 0x02 (DRM_IOCTL_GET_MAGIC / drm_getmagic, drm_auth.c:58-76): * writes 4 bytes at offset 0, returns 0 -- like drm_getmagic, * which only writes auth->magic and returns 0 on success. * nr 0x06 (DRM_IOCTL_GET_STATS / drm_getstats, drm_ioctl.c:215-224): * memset(data, 0, sizeof(struct drm_stats) == 248) and return 0. * * DF-4569: DragonFly port of drm_ioctl() dropped the upstream * canonical-size protection (Linux v4.7 drivers/gpu/drm/drm_ioctl.c: * "drv_size = _IOC_SIZE(ioctl->cmd); usize = _IOC_SIZE(cmd); * asize = max(usize, drv_size); cmd = ioctl->cmd;"). */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/device.h> #include <sys/uio.h> #include <sys/types.h> #include <sys/conf.h> /* exact copies of the values used by the DRM core */ #define REP_GET_MAGIC_NR 0x02 /* DRM_IOCTL_GET_MAGIC */ #define REP_GET_STATS_NR 0x06 /* DRM_IOCTL_GET_STATS */ #define REP_DRM_STATS_SIZE 248 /* sizeof(struct drm_stats) on LP64: * 8 + 15 * {unsigned long; enum} = 248 */ static int dfioctl_rep_ioctl(struct dev_ioctl_args *ap) { unsigned long cmd = ap->a_cmd; caddr_t data = ap->a_data; /* drm_ioctl(): "unsigned int nr = DRM_IOCTL_NR(cmd)" -- low byte only. * NOTHING in drm_ioctl() looks at the group, direction or size bits. */ switch (cmd & 0xff) { case REP_GET_MAGIC_NR: /* drm_getmagic() writes auth->magic (int, offset 0) and * returns 0. It reads no other input field. */ *(int *)data = 0x5a5a5a5a; return 0; case REP_GET_STATS_NR: /* drm_getstats(): "memset(stats, 0, sizeof(*stats))" into the * buffer mapped_ioctl() sized from the user's size bits. */ memset(data, 0, REP_DRM_STATS_SIZE); return 0; default: return EINVAL; /* like a hole in the drm_ioctls[] table */ } } static int dfioctl_rep_open(struct dev_open_args *ap) { return 0; } static struct dev_ops dfioctl_ops = { { "dfioctl", 0, D_MPSAFE }, .d_open = dfioctl_rep_open, .d_ioctl = dfioctl_rep_ioctl, }; static cdev_t dfioctl_dev; static int dfioctl_rep_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: /* world-accessible so the unprivileged PoC can run */ dfioctl_dev = make_dev(&dfioctl_ops, 0, UID_ROOT, GID_WHEEL, 0666, "dfioctl"); kprintf("dfioctl_rep: loaded (DRM dispatch replica)\n"); return 0; case MOD_UNLOAD: destroy_dev(dfioctl_dev); kprintf("dfioctl_rep: unloaded\n"); return 0; default: return 0; } } DEV_MODULE(dfioctl_rep, dfioctl_rep_modevent, NULL); |