DragonFlyBSD Kernel Audit
DF-1917 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/raid/mrsas/mrsas_ioctl.c b/sys/dev/raid/mrsas/mrsas_ioctl.c
--- a/sys/dev/raid/mrsas/mrsas_ioctl.c
+++ b/sys/dev/raid/mrsas/mrsas_ioctl.c
@@ -216,6 +216,25 @@
      * kernel buffers in SGLs. The location of SGL is embedded in the
      * struct iocpacket itself.
      */
+    /*
+     * DF-1917: user_ioc->sgl_off is an attacker-controlled u32 from
+     * userspace (mrsas_ioctl.h:83) and was previously used verbatim as
+     * the in-frame offset at which up to MAX_IOCTL_SGE (=16) 8-byte SGE
+     * entries are written (mrsas_ioctl.c:225-255).  With no validation
+     * the writes could land past the 1024-byte cmd->frame DMA allocation
+     * (MRSAS_MFI_FRAME_SIZE).  Reject any sgl_off that would push the
+     * last SGE byte (sgl_off + sge_count*sizeof(mrsas_sge32)) past the
+     * end of the frame.
+     */
+    if (user_ioc->sgl_off > MRSAS_MFI_FRAME_SIZE ||
+        (uint64_t)user_ioc->sgl_off +
+        (uint64_t)user_ioc->sge_count * sizeof(struct mrsas_sge32) >
+            (uint64_t)MRSAS_MFI_FRAME_SIZE) {
+        device_printf(sc->mrsas_dev,
+            "In %s() sgl_off=0x%x sge_count=%u overflows frame\n",
+            __func__, user_ioc->sgl_off, user_ioc->sge_count);
+        return (EINVAL);
+    }
     kern_sge32 = (struct mrsas_sge32 *)
         ((unsigned long)cmd->frame + user_ioc->sgl_off);