tulip_srom_decode() unbounded media-info pointer walk: OOB WRITE past tulip_softc.tulip_mediainfo[10] into the softc tail and kernel heap
Summary
The SROM decoder advances the media-info pointer mi once per well-formed media block with blocks taken as a raw EEPROM byte (max 255) and no bound against the 10-entry array. >=11 blocks write past the array over tulip_setup_tag/map/dma_addr, the live tulip_setupbuf kernel pointer, tulip_setupdata[48], tulip_boardid[24], tulip_rombuf[128] and into adjacent kernel heap, and store OOB pointers into sc->tulip_mediums[] that tulip_media_set() dereferences later. On 21142/21143 the decode gate skips the SROM CRC entirely. HARNESS on the guest: 1:1 logic model with exact softc-tail layout and canaries - 13 OOB writes, tulip_setupbuf CLOBBERED. Attacker-crafted or reprogrammed DEC 2114x SROM/EEPROM (malicious PCI card or brief physical access): 28-byte-stride OOB writes of SROM-controlled data; clobbering tulip_setupbuf forges a kernel pointer that tulip_txput_setup() bcopy()s 192 bytes through - potential arbitrary kernel write with a fully crafted EEPROM. Low solely due to the hardware precondition (same precedent as DF-1205, the OOB *read* side of the same parse - different array, different primitive).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3756 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| srom_mediainfo_model.c | β | 8.0 KB | view raw | |
| build.sh | β | 114 B | view raw | |
| run.sh | β | 117 B | view raw | |
| build.log | β | 284 B | view raw | |
| run.log | β | 1.9 KB | view raw | |
| env.txt | β | 865 B | view raw | |
| fix.diff | β | 1.2 KB | view raw | |
| VERDICT.md | β | 6.0 KB | β raw | |
| manifest.json | β | 1.2 KB | view raw | |
| verdict.json | β | 3.6 KB | view raw |
DF-3756 VERDICT β OOB write past tulip_mediainfo[10] in tulip_srom_decode()
Status: inconclusive (kernel path untestable on this guest β no DEC 2114x
hardware; defect itself source-certain and logic-model-CONFIRMED)
Root cause
tulip_srom_decode() (sys/dev/netif/de/if_de.c:2073) parses the card's
serial EEPROM ("SROM") contents byte by byte using a walking pointer dp
(the OOB read side of that walk is known finding DF-1205). Independently of
the read walk, the function also fills a fixed 10-entry array through a
second walking pointer:
sys/dev/netif/de/if_devar.h:483βtulip_media_info_t tulip_mediainfo[10];if_de.c:2080βtulip_media_info_t *mi = sc->tulip_mediainfo;if_de.c:2121(21041 branch) /if_de.c:2183(2114x branch) βblocks = *dp++;β raw, unvalidated SROM byte (0β255)- block loop
if_de.c:2122/if_de.c:2184β for every well-formed media block the code writes throughmiand advances it with no bound check: - case 0 (21140 GPR): writes
if_de.c:2205-2222,mi++atif_de.c:2223 - case 1 (21140 MII):
mi++atif_de.c:2276(PHY found) - case 2 (21142 SIA): writes
if_de.c:2289-2322,mi++atif_de.c:2323 - case 3 (21142 MII):
mi++atif_de.c:2373 - case 4 (21143 SYM): writes
if_de.c:2386-2400,mi++atif_de.c:2401 - 21041 extended blocks:
mi++atif_de.c:2138
Nothing relates blocks (β€255) to the 10 available slots. An SROM leaf with
β₯11 type-0 blocks (4 bytes each; 23 fit in a 128-byte SROM) drives mi past
the array into the remainder of the heap-allocated softc and beyond:
tulip_mediainfo[10] (+280B region) tulip_setup_tag if_devar.h:488 tulip_setup_map if_devar.h:489 tulip_setup_dma_addr if_devar.h:490 tulip_setupbuf if_devar.h:491 <- real kernel pointer (DMA-mapped buffer) tulip_setupdata[48] if_devar.h:492 tulip_boardid[24] if_devar.h:493 tulip_rombuf[128] if_devar.h:494 ... then past the softc allocation into adjacent kernel heap
sizeof(tulip_media_info_t) = 28 (x86-64); the model's crafted 23-block SROM
produced 13 OOB writes reaching +364 bytes past the array, clobbering
tulip_setup_tag, tulip_setup_map, tulip_setupbuf (the pointer later
used as the bcopy() destination in tulip_txput_setup(), if_de.c:3637),
13/48 tulip_setupdata words, and 8 tulip_boardid bytes (run.log).
Additionally each block stores sc->tulip_mediums[media] = mi
(if_de.c:2206/2290β¦), leaving OOB pointers that tulip_media_set()
(if_de.c:247) dereferences at runtime.
Reachability / gates
tulip_pci_attach() β tulip_read_macaddr() (if_de.c:4278) β
tulip_srom_decode() (if_de.c:2502). Decode is reached when
TULIP_HAVE_ISVSROM is set, which happens when:
- the SROM CRC is valid (
if_de.c:2480) β the CRC is 2 attacker-chosen bytes, trivially computed for a crafted image; or - bytes 126/127 are 0xFF with the legacy zero-prefix format
(
if_de.c:2486-2497); or - chipid β₯ 21142 β unconditionally, no CRC check at all
(
if_de.c:2498-2500).
So on 21142/21143-based cards, any EEPROM garbage with a plausible leaf pointer reaches the overflow. All of this runs at boot or module load, in device attach context.
Threat model
Requires attacker-crafted SROM content: a malicious or reprogrammed PCI card
planted in the machine, or an attacker with brief physical access able to
reprogram the card's serial EEPROM (the 2114x SROM is rewriteable on many
boards). Not reachable remotely or by local users of a stock machine. Given
that precondition the primitive is strong: 28-byte-stride OOB writes of
SROM-controlled data over kernel heap including a live kernel pointer
(tulip_setupbuf) whose later use (bcopy at if_de.c:3637,
bus_dmamem_free paths) turns corruption into a write-through/freed-pointer
primitive β potential arbitrary kernel write with a fully crafted EEPROM.
Severity Low because of the hardware precondition (same precedent as
DF-1205); the memory-safety defect itself is certain.
Verification narrative
- Read the full driver +
if_devar.h; established array sizes and everymi++site (listed above). No bound check exists anywhere betweenmi = sc->tulip_mediainfoand the lastmi++. - Guest check (
env.txt): driverpci/if_decompiled into X86_64_GENERIC,hw.de.*OID count = 0, no 0x1011 PCI devices β no de(4) instance can exist β kernel path not executable on this rig. - Built and ran the 1:1 userspace logic model on the guest
(
build.log,run.log): 13 OOB writes confirmed, +364 bytes past the array, pointer members clobbered. Model arithmetic matches the kernel struct layout exactly (28B entries; verified by printingsizeof(tulip_media_info_t)). - QEMU's upstream
tulipdevice model (DEC 21143) has a hardcoded, valid SROM inhw/net/tulip.c; feeding a crafted EEPROM would require patching QEMU itself, so kernel-path reproduction is not possible on this rig without new infrastructure.
Classification: inconclusive / reproduced=0 / impact=none (on this
guest). The bound violation is proven at source level and by the logic model;
kernel-path execution requires hardware absent from the audit rig.
Recommended fix
Bound mi against sc->tulip_mediainfo[10] at the top of both block loops
(see fix.diff β also stops the 21041-branch variant at if_de.c:2138).
The DP/rombuf read walk is DF-1205's to fix; this diff deliberately only
bounds the mi writes.
Kernel references
- sys/dev/netif/de/if_devar.h:483 (array[10]), 488-494 (vulnerable tail)
- sys/dev/netif/de/if_de.c:2080, 2121-2138, 2183-2184, 2205-2223, 2276, 2289-2323, 2373, 2386-2401, 2480-2502, 3637, 4278
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablefix.diff authored (mi bound guard in both block loops); not kernel-tested: no DEC 2114x hardware on the guest, and a patched-kernel build cannot attach the driver either.
Same hardware absence as baseline: env.txt (hw.de.* count=0). Logic model's guard check pattern mirrors the proposed bound.
Confirmed kernel references
Detail
Exploit chain
Crafted EEPROM on 21142/21143 (no CRC gate) or CRC-valid crafted SROM -> tulip_pci_attach -> tulip_read_macaddr (if_de.c:4278) -> tulip_srom_decode -> 13+ mi writes past tulip_mediainfo[10] -> clobber tulip_setupbuf pointer with SROM-controlled 32-bit pairs -> next multicast filter update bcopy()s 192 bytes through the forged pointer (if_de.c:3637) -> arbitrary kernel write; OOB tulip_mediums[] pointers additionally dereferenced at runtime by tulip_media_set (if_de.c:247). Requires attacker-supplied/reprogrammed PCI hardware.
Evidence (decisive lines)
run.log: 'OOB writes performed: 13' + 'tulip_setupbuf CLOBBERED (0x00000001a4a4a4a4)'; VERDICT.md root-cause walk with all mi++ sites; env.txt: kldstat pci/if_de present, sysctl hw.de.* count=0, no DEC PCI devices.
PoC changes
No seed PoC existed; authored from scratch. One iteration: first build failed on a mimic field name (gpr_gpdata vs model's anonymous struct), fixed and rebuilt clean.
Verified recommended fix
Bound mi against tulip_mediainfo[NELEM] at the top of both SROM block loops in tulip_srom_decode (fix.diff).
Verdict
Source-certain OOB write: tulip_srom_decode() advances the media-info pointer mi once per well-formed SROM block with no bound against tulip_softc.tulip_mediainfo[10] (if_devar.h:483); blocks is a raw EEPROM byte (if_de.c:2183), so >=11 blocks write past the array into the softc tail (tulip_setup_tag/map/dma_addr/setupbuf pointer, tulip_setupdata[48], tulip_boardid[24]) and further into the kernel heap. A 1:1 userspace logic model built and run on the audit guest confirmed 13 OOB writes reaching +364 bytes and clobbering the tulip_setupbuf kernel pointer. The kernel path itself executes only at device attach on a physical DEC 2114x card with a crafted EEPROM; the guest has the driver compiled in but zero DEC hardware (hw.de.* count=0, no 0x1011 PCI devices), and QEMU's tulip model has a hardcoded valid SROM, so kernel-path reproduction is impossible on this rig -> inconclusive. Decode gate: valid SROM CRC (attacker-computable) or, on 21142/21143, NO CRC CHECK AT ALL (if_de.c:2498-2500). Distinct from DF-1205 (OOB reads past tulip_rombuf): different array, write primitive.
No comments yet.