DragonFlyBSD Kernel Audit
DF-2069 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/disk/nata/chipsets/ata-serverworks.c b/sys/dev/disk/nata/chipsets/ata-serverworks.c
--- a/sys/dev/disk/nata/chipsets/ata-serverworks.c
+++ b/sys/dev/disk/nata/chipsets/ata-serverworks.c
@@ -216,7 +216,8 @@
 	if (atadev->flags & ATA_D_USE_CHS) {
 	    int heads, sectors;
 
-	    if (atadev->param.atavalid & ATA_FLAG_54_58) {
+	    if ((atadev->param.atavalid & ATA_FLAG_54_58) &&
+		atadev->param.current_heads && atadev->param.current_sectors) {
 		heads = atadev->param.current_heads;
 		sectors = atadev->param.current_sectors;
 	    }
@@ -225,6 +226,18 @@
 		sectors = atadev->param.sectors;
 	    }
 
+	    /* Defense-in-depth (DF-2069): IDENTIFY words 54-58 may legitimately
+	     * advertise ATA_FLAG_54_58 validity but report a zero geometry
+	     * (observed on misbehaving/emulated ATA devices). Without this
+	     * guard the divisions below trigger a kernel divide-by-zero trap.
+	     * ata-disk.c:ad_invalidate() applies the identical
+	     * (current_heads && current_sectors) check before adopting the
+	     * current_* geometry; this write path previously did not. */
+	    if (heads == 0 || sectors == 0) {
+		request->result = EIO;
+		return;
+	    }
+
 	    ATA_IDX_OUTW(ch, ATA_SECTOR, (request->u.ata.lba % sectors)+1);
 	    ATA_IDX_OUTW(ch, ATA_CYL_LSB,
 			 (request->u.ata.lba / (sectors * heads)));