fix.diff for DF-4982 — iir(4) GDT_IOCTL_EVENT: eliminate the user-memory double-fetch by operating on a single kernel copy, clamp event_data.size to sizeof(eu), and copy the result back out. Authored AFTER verification (source-trace confirmation) against the read-only sys/ tree; never applied there. --- a/sys/dev/raid/iir/iir_ctrl.c +++ b/sys/dev/raid/iir/iir_ctrl.c @@ -319,10 +319,16 @@ iir_ioctl(struct dev_ioctl_args *ap) case GDT_IOCTL_EVENT: { - gdt_event_t *p; + gdt_event_t ev; + gdt_event_t *p = &ev; - p = (gdt_event_t *)ap->a_data; + /* work on a single kernel copy: ap->a_data is user memory, + * and gdt_store_event()/gdt_read_event() re-read fields from + * it (evt->size is used as a memcmp length in iir.c) */ + if (copyin(ap->a_data, &ev, sizeof(ev)) != 0) + return (EFAULT); if (p->erase == 0xff) { + u_int16_t esize; + if (p->dvr.event_source == GDT_ES_TEST) p->dvr.event_data.size = sizeof(p->dvr.event_data.eu.test); else if (p->dvr.event_source == GDT_ES_DRIVER) @@ -331,6 +337,13 @@ iir_ioctl(struct dev_ioctl_args *ap) else if (p->dvr.event_source == GDT_ES_SYNC) p->dvr.event_data.size = sizeof(p->dvr.event_data.eu.sync); else p->dvr.event_data.size = sizeof(p->dvr.event_data.eu.async); + esize = p->dvr.event_data.size; + /* paranoia: never trust a size beyond the eu union */ + if (esize == 0 || esize > sizeof(p->dvr.event_data.eu)) + esize = sizeof(p->dvr.event_data.eu); + p->dvr.event_data.size = esize; crit_enter(); gdt_store_event(p->dvr.event_source, p->dvr.event_idx, &p->dvr.event_data); @@ -344,6 +357,10 @@ iir_ioctl(struct dev_ioctl_args *ap) } else { gdt_readapp_event((u_int8_t)p->erase, &p->dvr); } + if (copyout(&ev, ap->a_data, sizeof(ev)) != 0) + return (EFAULT); break; }