⬢ DragonFlyBSD Kernel Audit
DF-5768 / fix.diff
← back to finding ↓ download raw
fix.diff for DF-5768 — clamp the card-derived erase sector size to >= 1.

Root cause: sys/bus/mmc/mmc.c derives ivar->erase_sector from card-supplied
CSD fields with truncating integer division:

  SD:  mmc.c:1476-1477   ivar->erase_sector = csd.erase_sector *
                                    csd.write_bl_len / MMC_SECTOR_SIZE;
  MMC: mmc.c:1595-1596   (same expression)

csd.erase_sector >= 1 and csd.write_bl_len >= 1 are both attacker-chosen
(WP_GRP_SIZE/ERASE_GRP_SIZE and WRITE_BL_LEN bit fields of the CSD), so a
crafted card with WRITE_BL_LEN bits == 0 (write_bl_len = 1 byte) yields
erase_sector == 0.  The value flows to mmcsd(4) via MMC_IVAR_ERASE_SECTOR
and mmcsd_delete() (sys/dev/disk/mmcsd/mmcsd.c:407-411) divides/modulos by
it on the first BIO_FREEBLKS (TRIM) against the device:

  start = block + erase_sector - 1;
  start -= start % erase_sector;      <-- %0  =>  #DE => panic

Fix: floor the derived value at 1 erase sector (512-byte granularity is
guaranteed sane because the driver pins the I/O block length to 512).

--- a/sys/bus/mmc/mmc.c
+++ b/sys/bus/mmc/mmc.c
@@ -1473,8 +1473,8 @@
 			if (ivar->csd.csd_structure > 0)
 				ivar->high_cap = 1;
 			ivar->tran_speed = ivar->csd.tran_speed;
-			ivar->erase_sector = ivar->csd.erase_sector *
-			    ivar->csd.write_bl_len / MMC_SECTOR_SIZE;
+			ivar->erase_sector = MAX(ivar->csd.erase_sector *
+			    ivar->csd.write_bl_len / MMC_SECTOR_SIZE, 1);
 
 			err = mmc_send_status(sc->dev, sc->dev, ivar->rca,
 			    &status);
@@ -1592,8 +1592,8 @@
 		mmc_decode_csd_mmc(ivar->raw_csd, &ivar->csd);
 		ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE;
 		ivar->tran_speed = ivar->csd.tran_speed;
-		ivar->erase_sector = ivar->csd.erase_sector *
-		    ivar->csd.write_bl_len / MMC_SECTOR_SIZE;
+		ivar->erase_sector = MAX(ivar->csd.erase_sector *
+		    ivar->csd.write_bl_len / MMC_SECTOR_SIZE, 1);
 
 		err = mmc_send_status(sc->dev, sc->dev, ivar->rca, &status);
 		if (err != MMC_ERR_NONE) {