DF-1919 / fix.diff
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 @@ -44,6 +44,18 @@ #include <dev/raid/mrsas/mrsas.h> #include <dev/raid/mrsas/mrsas_ioctl.h> +/* + * DF-1919: cap the per-SGE DMA data transfer. The previous code stored + * user_ioc->sgl[i].iov_len (size_t on amd64) into an `int ioctl_data_size` + * (mrsas_ioctl.c:161), truncating the size used for the bus_dma_tag / + * bus_dmamem_alloc, while copyin() (mrsas_ioctl.c:258) was given the + * ORIGINAL size_t length -- so an iov_len like 0x100000008 allocated an + * 8-byte DMA buffer and then copyin'd 4 GiB+8 of attacker bytes into it. + * The cap below is enforced against the full 64-bit iov_len, and + * ioctl_data_size is widened to bus_size_t so the value used for the + * allocation and the value used for copyin can never disagree. + */ +#define MRSAS_IOCTL_MAX_DATA_SIZE (1024 * 1024) /* 1 MiB per SGE */ /* * Function prototypes @@ -158,7 +170,8 @@ bus_dmamap_t ioctl_sense_dmamap = 0; void *ioctl_sense_mem = NULL; bus_addr_t ioctl_sense_phys_addr = 0; - int i, adapter, ioctl_data_size, ioctl_sense_size, ret=0; + int i, adapter, ioctl_sense_size, ret=0; + bus_size_t ioctl_data_size; /* DF-1919: was `int` -- size_t truncation */ struct mrsas_sge32 *kern_sge32; unsigned long *sense_ptr; @@ -225,6 +238,20 @@ for (i=0; i < user_ioc->sge_count; i++) { if (!user_ioc->sgl[i].iov_len) continue; + /* + * DF-1919: validate the full 64-bit iov_len BEFORE storing it into + * ioctl_data_size. Reject anything beyond the per-SGE cap; this + * also blocks the size_t->int truncation that previously split + * the DMA allocation size from the copyin length. + */ + if (user_ioc->sgl[i].iov_len == 0 || + user_ioc->sgl[i].iov_len > MRSAS_IOCTL_MAX_DATA_SIZE) { + device_printf(sc->mrsas_dev, + "In %s() SGE[%d] iov_len %ju out of range\n", + __func__, i, (uintmax_t)user_ioc->sgl[i].iov_len); + ret = EINVAL; + goto out; + } ioctl_data_size = user_ioc->sgl[i].iov_len; if (bus_dma_tag_create( sc->mrsas_parent_tag, // parent 1, 0, // algnmnt, boundary @@ -252,11 +279,11 @@ /* Save the physical address and length */ kern_sge32[i].phys_addr = (u_int32_t)ioctl_data_phys_addr[i]; - kern_sge32[i].length = user_ioc->sgl[i].iov_len; + kern_sge32[i].length = (u_int32_t)ioctl_data_size; /* DF-1919: validated, no truncation */ /* Copy in data from user space */ - ret = copyin(user_ioc->sgl[i].iov_base, ioctl_data_mem[i], - user_ioc->sgl[i].iov_len); + ret = copyin(user_ioc->sgl[i].iov_base, ioctl_data_mem[i], + ioctl_data_size); /* DF-1919: same size we allocated */ if (ret) { device_printf(sc->mrsas_dev, "IOCTL copyin failed!\n"); goto out; |