DragonFlyBSD Kernel Audit
DF-2492 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/disk/nata/ata-pci.c b/sys/dev/disk/nata/ata-pci.c
--- a/sys/dev/disk/nata/ata-pci.c
+++ b/sys/dev/disk/nata/ata-pci.c
@@ -380,6 +380,13 @@
 	struct ata_pci_controller *controller = device_get_softc(dev);
 	int unit = ((struct ata_channel *)device_get_softc(child))->unit;
 
+	/* interrupt[] is a fixed 8-entry array (ata-pci.h); reject channel
+	 * units outside it instead of writing OOB.  AHCI-class controllers can
+	 * advertise up to 32 channels, which would otherwise corrupt adjacent
+	 * heap.  See DF-2492 / DF-2510. */
+	if (unit < 0 || unit >= NELEM(controller->interrupt))
+	    return EINVAL;
+
 	controller->interrupt[unit].function = function;
 	controller->interrupt[unit].argument = argument;
 	*cookiep = controller;
@@ -400,6 +407,9 @@
 	struct ata_pci_controller *controller = device_get_softc(dev);
 	int unit = ((struct ata_channel *)device_get_softc(child))->unit;
 
+	if (unit < 0 || unit >= NELEM(controller->interrupt))
+	    return EINVAL;
+
 	controller->interrupt[unit].function = NULL;
 	controller->interrupt[unit].argument = NULL;
 	return 0;
@@ -579,9 +589,15 @@
 {
     struct ata_pci_controller *ctlr = data;
     struct ata_channel *ch;
-    int unit;
+    int unit, maxunit;
 
-    for (unit = 0; unit < ctlr->channels; unit++) {
+    /* interrupt[] is a fixed 8-entry array; never iterate past it even if
+     * ctlr->channels (set from hardware CAP/PI on AHCI, up to 32) exceeds
+     * it, to avoid an OOB read of wild function pointers.  See DF-2492. */
+    maxunit = NELEM(ctlr->interrupt);
+    if (ctlr->channels < maxunit)
+	maxunit = ctlr->channels;
+    for (unit = 0; unit < maxunit; unit++) {
 	if ((ch = ctlr->interrupt[unit].argument))
 	    ctlr->interrupt[unit].function(ch);
     }