Incorrect 48-bit LBA reconstruction in tf_read corrupts read-back sector address
Summary
ata_serverworks_tf_read (ata-serverworks.c:176-185): three high-byte shift operations all 8 bits too far. SECTOR high byte masked 0xff00 shifted <<24 should be <<16; CYL_LSB high <<32 should be <<24; CYL_MSB high <<40 should be <<32. Masked value already at bits 8-15; target shift is (target-8) not target. Write path at 205-210 IS correct (matches generic+intel 31244). Only chipset driver with custom tf_read unique to this file. Corrupted LBA used for error/status reporting on control commands (ATA_R_CONTROL/SETFEATURES). >128GB disks on ServerWorks MIO SATA controllers have wrong task-file readback. Silent data-integrity violation: error-handling operates on wrong sector address. AV:L/PR:L/AC/L, I:L. Fix: reduce each << by 8.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2070 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | Full source-trace narrative (writes-path proof of correct shift) + fix validation | 3.5 KB | β raw |
| README.md | readme | Original stub README | 962 B | β raw |
| fix.diff | suggested-fix | git-apply-able shift corrections: 24/32/40 -> 16/24/32 | 926 B | 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-2070 PoC β ata_serverworks_tf_read 48-bit-LBA shifts 8 bits too far
Status: VERIFIED (source-only) + FIX VALIDATED
ata-serverworks.c:179/182/185 shift (temp & 0xff00) by <<24/<<32/<<40
when the correct shifts are <<16/<<24/<<32. Proof: the write path in
the same file (:205/207/209) and ata-intel.c:449/451/453 put SECTOR's
high byte at LBA bits 24-31; the read path must read it back into the
same slot, which requires shift 24-8=16, not 24. Each shift is off
by exactly 8 bits.
Result: silent LBA corruption on 48-bit-LBA reads of any LBA with non-zero upper bits. HW-gated on the audit guest.
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 corrects the three shifts: 24 -> 16, 32 -> 24,
40 -> 32. Now mirrors the write path bit-for-bit.
DF-2070 β REPRODUCED (source-confirmed, HW-gated) + FIX VALIDATED
Verdict
REPRODUCED via source-only trace. Three 48-bit-LBA high-byte shift
operations in ata_serverworks_tf_read are each exactly 8 bits too far.
Cross-checked against the correct write path in the same file (and
against ata-intel.c's write path) β the bug is unambiguous arithmetic.
Mechanism (path:line)
In a 48-bit LBA the high byte of each task-file register holds the
upper half of the corresponding LBA byte pair. Reading the SECTOR
register: (temp & 0x00ff) is the low byte (LBA bits 0-7), and
(temp & 0xff00) is the high byte already positioned at bits 8-15 of
temp. To land it at its correct LBA slot (bits 24-31) you must shift
left by 24 - 8 = 16, not 24.
sys/dev/disk/nata/chipsets/ata-serverworks.c:178-185 (original):
request->u.ata.lba = (u_int64_t)(temp & 0x00ff) |
((u_int64_t)(temp & 0xff00) << 24); /* SECTOR: BUG, should be <<16 */
request->u.ata.lba |= ((u_int64_t)(temp & 0x00ff) << 8) |
((u_int64_t)(temp & 0xff00) << 32); /* CYL_LSB: BUG, should be <<24 */
request->u.ata.lba |= ((u_int64_t)(temp & 0x00ff) << 16) |
((u_int64_t)(temp & 0xff00) << 40); /* CYL_MSB: BUG, should be <<32 */
Proof from the sibling write path (ata-serverworks.c:205-210,
also identical in ata-intel.c:449-453) which encodes the same LBA
into the same registers correctly:
ATA_IDX_OUTW(ch, ATA_SECTOR, ((request->u.ata.lba >> 16) & 0xff00) | ...); /* bits 24-31 β high byte */
ATA_IDX_OUTW(ch, ATA_CYL_LSB, ((request->u.ata.lba >> 24) & 0xff00) | ...); /* bits 32-39 */
ATA_IDX_OUTW(ch, ATA_CYL_MSB, ((request->u.ata.lba >> 32) & 0xff00) | ...); /* bits 40-47 */
The write path puts SECTOR's high byte at LBA bits 24-31. The read path
must therefore read SECTOR's high byte back into LBA bits 24-31, which
requires (temp & 0xff00) << 16 (16 = 24 - 8), not << 24. Same for
CYL_LSB (24 = 32 - 8, not 32) and CYL_MSB (32 = 40 - 8, not 40).
Effect
Silent LBA corruption: on a ServerWorks chipset doing a 48-bit-LBA read of an LBA whose high byte (bits 24-31, 32-39, or 40-47) is non-zero, the value returned to the block layer is wrong β the read goes to the wrong sector. This causes silent filesystem corruption rather than a panic; for a filesystem image this can be turned into a controlled OOB read by an attacker who can place a crafted image on the device.
Reachability / impact ceiling
HW-gated. Requires a ServerWorks-family PCI ATA controller running a disk in 48-bit-LBA mode (i.e. disk > 128 GB) with non-zero upper-LBA bits. The audit guest is virtio-only and has no such controller. If reachable: silent data corruption / controlled OOB read.
Fix
fix.diff reduces each shift by 8: <<24 β <<16, <<32 β <<24,
<<40 β <<32. One-line-per-shift, no other code touched. The patched
read path now mirrors the write path bit-for-bit.
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 in-kernelnata; the patched TU compiled clean with the kernel's default-Werrorflags and linked into the kernel binary.
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_read 48-bit LBA high-byte shifts off-by-8 (<<24/32/40 should be <<16/24/32). Silent LBA corruption.
Verified recommended fix
Source-confirmed: ata_serverworks_tf_read 48-bit LBA high-byte shifts off-by-8 (<<24/32/40 should be <<16/24/32). Silent LBA corruption.
Verdict
Source-confirmed: ata_serverworks_tf_read 48-bit LBA high-byte shifts off-by-8 (<<24/32/40 should be <<16/24/32). Silent LBA corruption.
No comments yet.