Skip to content

Commit c8952a7

Browse files
committed
vfio/pci: Fix NULL pointer oops in error interrupt setup handling
There are multiple cases in vfio_pci_set_ctx_trigger_single() where we assume we can safely read from our data pointer without actually checking whether the user has passed any data via the count field. VFIO_IRQ_SET_DATA_NONE in particular is entirely broken since we attempt to pull an int32_t file descriptor out before even checking the data type. The other data types assume the data pointer contains one element of their type as well. In part this is good news because we were previously restricted from doing much sanitization of parameters because it was missed in the past and we didn't want to break existing users. Clearly DATA_NONE is completely broken, so it must not have any users and we can fix it up completely. For DATA_BOOL and DATA_EVENTFD, we'll just protect ourselves, returning error when count is zero since we previously would have oopsed. Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Reported-by: Chris Thompson <the_cartographer@hotmail.com> Cc: stable@vger.kernel.org Reviewed-by: Eric Auger <eric.auger@redhat.com>
1 parent 29b4817 commit c8952a7

File tree

1 file changed

+49
-36
lines changed

1 file changed

+49
-36
lines changed

drivers/vfio/pci/vfio_pci_intrs.c

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -564,67 +564,80 @@ static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev,
564564
}
565565

566566
static int vfio_pci_set_ctx_trigger_single(struct eventfd_ctx **ctx,
567-
uint32_t flags, void *data)
567+
unsigned int count, uint32_t flags,
568+
void *data)
568569
{
569-
int32_t fd = *(int32_t *)data;
570-
571-
if (!(flags & VFIO_IRQ_SET_DATA_TYPE_MASK))
572-
return -EINVAL;
573-
574570
/* DATA_NONE/DATA_BOOL enables loopback testing */
575571
if (flags & VFIO_IRQ_SET_DATA_NONE) {
576-
if (*ctx)
577-
eventfd_signal(*ctx, 1);
578-
return 0;
572+
if (*ctx) {
573+
if (count) {
574+
eventfd_signal(*ctx, 1);
575+
} else {
576+
eventfd_ctx_put(*ctx);
577+
*ctx = NULL;
578+
}
579+
return 0;
580+
}
579581
} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
580-
uint8_t trigger = *(uint8_t *)data;
582+
uint8_t trigger;
583+
584+
if (!count)
585+
return -EINVAL;
586+
587+
trigger = *(uint8_t *)data;
581588
if (trigger && *ctx)
582589
eventfd_signal(*ctx, 1);
583-
return 0;
584-
}
585590

586-
/* Handle SET_DATA_EVENTFD */
587-
if (fd == -1) {
588-
if (*ctx)
589-
eventfd_ctx_put(*ctx);
590-
*ctx = NULL;
591591
return 0;
592-
} else if (fd >= 0) {
593-
struct eventfd_ctx *efdctx;
594-
efdctx = eventfd_ctx_fdget(fd);
595-
if (IS_ERR(efdctx))
596-
return PTR_ERR(efdctx);
597-
if (*ctx)
598-
eventfd_ctx_put(*ctx);
599-
*ctx = efdctx;
592+
} else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
593+
int32_t fd;
594+
595+
if (!count)
596+
return -EINVAL;
597+
598+
fd = *(int32_t *)data;
599+
if (fd == -1) {
600+
if (*ctx)
601+
eventfd_ctx_put(*ctx);
602+
*ctx = NULL;
603+
} else if (fd >= 0) {
604+
struct eventfd_ctx *efdctx;
605+
606+
efdctx = eventfd_ctx_fdget(fd);
607+
if (IS_ERR(efdctx))
608+
return PTR_ERR(efdctx);
609+
610+
if (*ctx)
611+
eventfd_ctx_put(*ctx);
612+
613+
*ctx = efdctx;
614+
}
600615
return 0;
601-
} else
602-
return -EINVAL;
616+
}
617+
618+
return -EINVAL;
603619
}
604620

605621
static int vfio_pci_set_err_trigger(struct vfio_pci_device *vdev,
606622
unsigned index, unsigned start,
607623
unsigned count, uint32_t flags, void *data)
608624
{
609-
if (index != VFIO_PCI_ERR_IRQ_INDEX)
625+
if (index != VFIO_PCI_ERR_IRQ_INDEX || start != 0 || count > 1)
610626
return -EINVAL;
611627

612-
/*
613-
* We should sanitize start & count, but that wasn't caught
614-
* originally, so this IRQ index must forever ignore them :-(
615-
*/
616-
617-
return vfio_pci_set_ctx_trigger_single(&vdev->err_trigger, flags, data);
628+
return vfio_pci_set_ctx_trigger_single(&vdev->err_trigger,
629+
count, flags, data);
618630
}
619631

620632
static int vfio_pci_set_req_trigger(struct vfio_pci_device *vdev,
621633
unsigned index, unsigned start,
622634
unsigned count, uint32_t flags, void *data)
623635
{
624-
if (index != VFIO_PCI_REQ_IRQ_INDEX || start != 0 || count != 1)
636+
if (index != VFIO_PCI_REQ_IRQ_INDEX || start != 0 || count > 1)
625637
return -EINVAL;
626638

627-
return vfio_pci_set_ctx_trigger_single(&vdev->req_trigger, flags, data);
639+
return vfio_pci_set_ctx_trigger_single(&vdev->req_trigger,
640+
count, flags, data);
628641
}
629642

630643
int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags,

0 commit comments

Comments
 (0)