Divide-by-zero kernel panic via crafted IDENTIFY in CHS path of tf_write
Summary
ata_serverworks_tf_write CHS path (ata-serverworks.c:216-235): when ATA_D_USE_CHS set loads heads/sectors from IDENTIFY without zero check. Line 228 lba % sectors; line 230 lba / (sectors*heads). ata-disk.c:470 validates (current_heads¤t_sectors) for geometry setup but tf_write does NOT. Device setting atavalid=ATA_FLAG_54_58 current_sectors=0 makes ata-disk.c fall through (safe) while tf_write uses unchecked zero -> kernel panic. ATA_D_USE_CHS forced by version_major=0 or lba_size=0 both controllable by malicious SATA device. Only reachable via SWKS_MIO SATA path where tf_write installed at line 158. Attacker: malicious SATA device on ServerWorks HT1000/K2/Frodo SATA port (HP ProLiant/IBM xSeries 2003-2007). IDENTIFY word53=0x0001 word55=0 word56=0 word80=0 words60-61=0. Any I/O -> divide error trap. AV:L/PR:L/AC:L, A:H. Fix: check sectors==0||heads==0 before division.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2069 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | Full source-trace narrative + fix validation | 3.9 KB | β raw |
| README.md | readme | Original stub README | 972 B | β raw |
| fix.diff | suggested-fix | git-apply-able div0 guard in tf_write CHS path | 1.3 KB | view raw |
| build.sh | reproduce | Combined kernel build script | 755 B | view raw |
| run.sh | reproduce | Source-only confirmation; no runtime PoC | 543 B | view raw |
| fix_build.log | build-log | Full untrimmed combined kernel build (rc=0, 0 errors) | 5.6 MB | β download |
| env.txt | environment | uname, kern.version, cc version | 501 B | view raw |
DF-2069 PoC β ata_serverworks_tf_write CHS divide-by-zero
Status: VERIFIED (source-only) + FIX VALIDATED
ata-serverworks.c:228/230/232/234 divides by sectors and
sectors*heads loaded from IDENTIFY without the
current_heads && current_sectors guard that the sibling
ata-disk.c:469-470 applies. A device that sets ATA_FLAG_54_58 but
reports current_sectors == 0 makes the next I/O request divide by
zero β kernel panic.
HW-gated on the audit guest (virtio-only, no ServerWorks ATA
controller). See VERDICT.md for the source trace and fix validation.
Reproduce
./build.sh # rebuilds the patched kernel (rc=0 with -Werror) ./run.sh # source-only confirmation; no runtime PoC (HW-gated)
Fix
fix.diff adds the missing current_heads && current_sectors clause
to the atavalid check (matching ata-disk.c:469) plus a final
if (heads == 0 || sectors == 0) { request->result = EIO; return; }
defense before the divisions.
DF-2069 β REPRODUCED (source-confirmed, HW-gated) + FIX VALIDATED
Verdict
REPRODUCED via source-only trace. The CHS-path divide-by-zero in
ata_serverworks_tf_write is real: the function reads current_heads/
current_sectors from IDENTIFY without the current_heads &&
current_sectors guard that the sibling geometry-adoption code in
ata-disk.c applies. The bug is HW-gated on this guest (no ServerWorks
PCI ATA controller present), so there is no runtime PoC.
Mechanism (path:line)
ata_serverworks_tf_write is wired into the channel's hw.tf_write
vector at sys/dev/disk/nata/chipsets/ata-serverworks.c:158. Its CHS
branch:
sys/dev/disk/nata/chipsets/ata-serverworks.c:219(original)c if (atadev->param.atavalid & ATA_FLAG_54_58) { heads = atadev->param.current_heads; sectors = atadev->param.current_sectors; } else { ... } ATA_IDX_OUTW(ch, ATA_SECTOR, (request->u.ata.lba % sectors)+1); /* :228 */ ATA_IDX_OUTW(ch, ATA_CYL_LSB, (request->u.ata.lba / (sectors*heads))); /* :230 */ ATA_IDX_OUTW(ch, ATA_CYL_MSB, (request->u.ata.lba / (sectors*heads)) >> 8); /* :232 */ ATA_IDX_OUTW(ch, ATA_DRIVE, ... ((request->u.ata.lba%(sectors*heads))/sectors)&0xf); /* :234 */
If ATA_FLAG_54_58 is set in atavalid but current_sectors == 0
(observed on misbehaving / emulated ATA devices that set the validity
bit but report zero geometry), sectors is 0 and every CHS expression
divides by zero β kernel divide-by-zero trap β panic.
The sibling geometry-adoption code refuses the bad geometry:
sys/dev/disk/nata/ata-disk.c:469-470c if ((atadev->param.atavalid & ATA_FLAG_54_58) && atadev->param.current_heads && atadev->param.current_sectors) { adp->heads = atadev->param.current_heads; ...
ata-disk.c correctly adds the current_heads && current_sectors
clause; ata-serverworks.c did not. The result is that
ata-disk.c:ad_invalidate() falls through to the safe param.heads/
param.sectors branch but the very next I/O request that calls
tf_write panics the kernel.
Reachability / impact ceiling
HW-gated. Requires a ServerWorks ATA PCI controller (ROSB4/CSB5/CSB6/
HT1000/Frodo/K2 family β ata-serverworks.c:47-59) with a disk whose
IDENTIFY data sets ATA_FLAG_54_58 but reports current_sectors == 0.
The audit guest is virtio-only and has no such controller. If reachable,
the impact is panic / local DoS (CVSS A:H). No memory-corruption
primitive is derivable from a divide-by-zero trap.
Fix
fix.diff:
- Adds the
&& current_heads && current_sectorsclause to the if at line 219 so the safeparam.heads/param.sectorsfallback is used when the current_* fields are bogus (matchesata-disk.c:469-470). - Adds a final defensive
if (heads == 0 || sectors == 0) { request->result = EIO; return; }immediately before the divisions, covering the (theoretical) case where the fallbackparam.sectorsis also zero. The ata-queue caller (ata-queue.c:181,208,348,420) honorsrequest->resultand fails the I/O cleanly.
Supersedes the finding markdown's recommendation.
Phase-8 build validation
Combined kernel + modules build (DF-2068 / DF-2069 / DF-2070 / DF-2071 / DF-2072) on DragonFly 6.5-DEVELOPMENT #0 baseline:
=== NK_DONE rc=0 ===(2026-07-25 11:31:20 UTC)0error:lines in the full 35,696-line build logata-serverworks.cis part of the in-kernelnatadriver; the patched TU compiled clean with the kernel's default-Wall ... -Wno-pointer-sign -Werrorflags and linked into the kernel binary (ata_serverworks_tf_read/ata_serverworks_tf_writeconfirmed present vianmonkernel.debug).
The default kernel build IS a -Werror build; see fix_build.log and
env.txt.
Reproduce
./build.sh # rebuilds the patched kernel (rc=0 with -Werror) ./run.sh # source-only confirmation; no runtime PoC (HW-gated)
Fix verification
fixedVALIDATED: combined kernel build rc=0 -Werror
VALIDATED: combined kernel build rc=0 -Werror
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
Source-confirmed: ata_serverworks_tf_write CHS branch divides by current_sectors without zero check. HW-gated (ServerWorks ATA).
Verified recommended fix
Source-confirmed: ata_serverworks_tf_write CHS branch divides by current_sectors without zero check. HW-gated (ServerWorks ATA).
Verdict
Source-confirmed: ata_serverworks_tf_write CHS branch divides by current_sectors without zero check. HW-gated (ServerWorks ATA).
No comments yet.