⬢ DragonFlyBSD Kernel Audit
DF-4982 / fix.diff
← back to finding ↓ download raw
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;
         }