⬢ DragonFlyBSD Kernel Audit
DF-4810 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4810",
  "status": "untested",
  "reproduced": 0,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Latent-hazard (Info) finding, not guest-verified because it is unreachable as-is: the AHCI command-table stride ATA_AHCI_CT_SIZE=256 (ata-all.h:239) disagrees with sizeof(struct ata_ahci_cmd_tab)=1152 (ata-all.h:249-255, __packed: 128-byte header + 64 PRDs x 16). All table placements and the hardware-visible cmd_table_phys (ata-ahci.c:254-255, 286-287) use the wrong stride and are held in-bounds only by the hardcoded tag=0 (ata-ahci.c:250, 343) within the 8192-byte work area (MAXWSPCSZ, ata-dma.c:55-56, 130-145). tag>=1 aliases live tables; tag>=28 writes prd_tab past the end of the bus_dmamem_allocd work area and programs the OOB physical address into the AHCI command list. Would become memory corruption the moment NCQ/per-tag dispatch is added. Also: ata_ahci_dmasetprd KASSERTs nsegs<=64 only AFTER the write loop (ata-ahci.c:538-544) and the true bound rests solely on max_iosize=63*PAGE_SIZE == exactly 64 page-spans == prd_tab[64] (ata-ahci.c:558) with the dma tag itself allowing 256 segments (ata-dma.c:99,112).",
  "exploit_chain": "",
  "evidence": "findings/poc/DF-4810/VERDICT.md (arithmetic proof); findings/poc/DF-4810/fix.diff (constant fix + _Static_assert + KASSERT reorder)",
  "kernel_refs": [
    "sys/dev/disk/nata/ata-all.h:239",
    "sys/dev/disk/nata/ata-all.h:249",
    "sys/dev/disk/nata/ata-all.h:253",
    "sys/dev/disk/nata/chipsets/ata-ahci.c:250",
    "sys/dev/disk/nata/chipsets/ata-ahci.c:254",
    "sys/dev/disk/nata/chipsets/ata-ahci.c:286",
    "sys/dev/disk/nata/chipsets/ata-ahci.c:343",
    "sys/dev/disk/nata/chipsets/ata-ahci.c:538",
    "sys/dev/disk/nata/chipsets/ata-ahci.c:544",
    "sys/dev/disk/nata/chipsets/ata-ahci.c:558",
    "sys/dev/disk/nata/ata-dma.c:55",
    "sys/dev/disk/nata/ata-dma.c:130"
  ],
  "poc_changes": "not applicable (no seed PoC; finding produced by pass-2 static analysis)",
  "attempts": 0,
  "guest_uname": "",
  "runtime_sec": 0,
  "guest_dirty": 0,
  "fix_status": "not_applicable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff corrects ATA_AHCI_CT_SIZE to 1152, adds a _Static_assert tying the constant to the struct, and moves the dmasetprd KASSERT before the write loop; runtime-neutral today (only tag 0 used, CT_OFFSET+0*CT_SIZE unchanged); not build-validated on the guest because no code path can exercise tag!=0",
  "fix_evidence": "findings/poc/DF-4810/fix.diff",
  "notes": "Phase V skipped per contract: Info/hardening, no runnable PoC possible (tag hardcoded 0). QEMU guest not used for this finding.",
  "recommended_fix": "Set ATA_AHCI_CT_SIZE=1152 with _Static_assert(ATA_AHCI_CT_SIZE == sizeof(struct ata_ahci_cmd_tab)) and hoist the nsegs KASSERT above the PRD write loop"
}