# 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):

```c
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:

```c
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)
* `0` `error:` lines in the full 35,696-line build log
* `ata-serverworks.c` is part of in-kernel `nata`; the patched TU
  compiled clean with the kernel's default `-Werror` flags 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)
```
