Missing privilege check in FWOHCI_WRREG/RDREG ioctl allows operator-group users to write arbitrary OHCI hardware registers
Summary
fwohci_ioctl handler allows reading and writing arbitrary OHCI MMIO registers (up to offset 0x800) and PHY registers via FWOHCI_WRREG/FWOHCI_RDREG/FWOHCI_WRPHYREG/FWOHCI_RDPHYREG without any suser()/priv_check() call. Device node is mode 0660 GID_OPERATOR. Any user in operator group can write arbitrary data to DMA control registers interrupt masks HCC reset register and PHY access enabling controller reset (DoS) DMA redirection to attacker-controlled physical addresses (potential kernel memory access on non-IOMMU systems) and PHY bus disruption. Writable range [0x000 0x800] covers entire OHCI register file including DMA command pointer registers interrupt masks HCCCTL PHYACCESS. [VERIFIED pass2: reproduced - opuser programs ATQ DMA command pointer INTMASK HCCCTL PHY registers via FWOHCI_WRREG with zero privilege checks, read-backs land; fwdev swallows fc->ioctl errors so even the EINVAL bound never reaches userspace; privesc-class impact on FireWire workstations - DMA context programming is classic arbitrary physical memory R/W on non-IOMMU systems; fix-validated SYSCAP_NODRIVER gate]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2300 Β· 16 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.5 KB | β raw | |
| VERDICT.md | β | 4.3 KB | β raw | |
| build.log | β | 928 B | view raw | |
| build_fix.sh | β | 511 B | view raw | |
| build_kernel.sh | β | 380 B | view raw | |
| env.txt | β | 1.3 KB | view raw | |
| fix.diff | β | 640 B | view raw | |
| fwohci_harness.c | β | 22.3 KB | view raw | |
| fwohci_harness.h | β | 3.9 KB | view raw | |
| fwpoc.c | β | 7.9 KB | view raw | |
| prep_guest.sh | β | 1.8 KB | view raw | |
| run.log | β | 992 B | view raw | |
| run.sh | β | 104 B | view raw | |
| run_fixed.log | β | 3.8 KB | view raw | |
| run_fixed.sh | β | 1.9 KB | view raw | |
| verdict.json | β | 4.3 KB | view raw |
DF-2300 β missing privilege check in FWOHCI_WRREG/RDREG/PHYREG ioctls
What this is
Verification pack for finding DF-2300 (sys/bus/firewire/fwohci.c:354-392).
Medium / operator-group arbitrary OHCI register access. Claim:
fwohci_ioctl() (fwohci.c:333-398) services FWOHCI_WRREG / FWOHCI_RDREG /
FWOHCI_WRPHYREG / FWOHCI_RDPHYREG / DUMPDMA with no privilege check of any
kind; the /dev/fw* nodes are 0660 root:operator, so any operator-group
user can program the OHCI DMA engine registers (context command pointers,
interrupt masks, HCC link/reset control) and the PHY.
Verdict (short)
REPRODUCED on the verbatim-interposition harness (kernel-compiled,
fwohci.c TU-included unmodified): an operator-group user (opuser,
gid operator) issued, through the REAL dispatch chain
/dev/fw0.0 -> fw_ioctl (fwdev.c:411) -> default: sc->fc->ioctl(ap)
(fwdev.c:694) -> fwohci_ioctl (fwohci.c:333):
FWOHCI_WRREG(0x18c, 0xdeadbee0)β OHCI ATQ DMA context command pointer β rc=0, value stuck in the register file (root cross-check via the rig:MMIO[0x18c]=0xdeadbee0)FWOHCI_WRREG(0x88, 0xdeadbee1)β FWOHCI_INTMASK (interrupt mask)FWOHCI_WRREG(0x50, 0xdeadbee2)β OHCI_HCCCTL (link control / reset)FWOHCI_WRREG(0x00, 0xdeadbee3)β even the version registerFWOHCI_RDREGread every value back;FWOHCI_WRPHYREG/FWOHCI_RDPHYREGexecuted the full PHY access sequences unprivileged
No EPERM anywhere. Note: fwdev.c:694 discards fc->ioctl() return
values, so even the addr > 0x800 EINVAL of fwohci.c:360 never reaches
userspace (the write is still not executed β bounds check works, error
report does not).
Rig
The established FireWire verbatim-interposition harness extended to the
OHCI side (fwohci_harness.c): fwohci.c compiled unmodified into the
harness TU; an emulated MMIO register file stands in for the card
(on x86_64 bus_space_read_4/write_4 are direct KVA dereferences,
sys/cpu/x86_64/include/bus_dma.h:130-162, so sc->bst=MEM, sc->bsh=®s
gives the real OWRITE/OREAD a live register file); /dev/fw0.0 is created
against the stock firewire_ops with stock clone permissions
(0660 root:operator).
Reachability honesty
- On real hardware the writable range [0x000,0x800] covers the entire OHCI register file including the DMA ContextCommand/CommandPtr registers: an operator-group user can point AT/AR/IR/IT contexts at attacker-chosen physical addresses, i.e. the classic FireWire arbitrary-physical-memory read/write primitive on non-IOMMU systems, plus controller reset (DoS) and PHY bus disruption. Gate: operator group membership + a FireWire controller (driver auto-attaches on hardware; /dev/fw* clones appear on open).
- On this QEMU guest there is no OHCI hardware; the register file is the rig emulation. The code path is 100% stock, so the missing privilege gate is proven mechanically; the hardware impact ceiling is assessment, not measurement.
- Guest has no IOMMU-relevant config; PRIVESC ASSESSMENT: on real hardware this is a plausible unpriv->root path (DMA to kernel text/ cred memory). It is not demonstrable on the hardware-less guest; the finding stays Medium (operator-gated, hardware-gated) but should be treated as privesc-class on workstations with FireWire ports.
Fix
fix.diff adds caps_priv_check(ap->a_cred, SYSCAP_NODRIVER) (the DF
driver-hardware capability gate, cf. sio.c usage) to fwohci_ioctl.
A/B validated: patched kernel -> opuser ioctls execute no register
access (register file unchanged, PHY untouched); root still allowed.
DF-2300 VERDICT β missing privilege check in fwohci_ioctl
- finding: DF-2300, fwohci.c:354-392 (fwohci_ioctl, fwohci.c:333-398)
- status: reproduced (privilege-gate bypass; operator-group arbitrary OHCI MMIO + PHY access through the unmodified driver code)
- impact: limited_write (attacker-chosen 32-bit values into the OHCI register file, the register file that programs the DMA engine); on real hardware this escalates to the classic FireWire DMA arbitrary-physical-R/W primitive (non-IOMMU) β hardware-gated on this guest, so hardware impact is assessment, not measurement.
- confidence: certain
How it was reproduced
- Rig:
fwohci_harness.c(this pack) #includes the unmodifiedsys/bus/firewire/fwohci.c; boots a fabricated fwohci_softc whose bst/bsh point at an emulated 0x800-byte register file (fh_regs[]), injects a firewire_softc as unit 0 of the realfirewire_devclass, and creates/dev/fw0.0against the stockfirewire_opswith the stock clone permissions 0660 root:operator.fc->ioctl = fwohci_ioctlβ the real function. opuser(groups: operator) opens /dev/fw0.0 and issues FWOHCI_WRREG to 0x18c (OHCI_ATQCMD, the async-TX DMA context command pointer), 0x88 (FWOHCI_INTMASK), 0x50 (OHCI_HCCCTL), 0x00 (OHCI_VERSION); every ioctl returns rc=0 and the RDREG read-back returns the attacker value.- Root cross-check through the rig control device reads the emulated
register file directly:
MMIO[0x18c]=0xdeadbee0etc. β the writes landed exactly where the attacker put them. - FWOHCI_WRPHYREG/FWOHCI_RDPHYREG complete the full PHY access sequences (fwphy_wrdata -> fwphy_rddata, fwohci.c:240-331) unprivileged. Rig limitation (documented): the emulated PHY completes accesses on a 1 ms tick while fwphy_wrdata only waits 100 us, so the write data itself can be missed by the emulation; the register-access path and the absence of any privilege check are unaffected (they are the claim being verified).
Path (line-accurate)
- fwohci.c:354-362 FWOHCI_WRREG:
OWRITE(fc, reg->addr, reg->data)with onlyreg->addr <= 0x800as a bound β no suser/priv/caps check. - fwohci.c:363-369 FWOHCI_RDREG, 371-378 DUMPDMA, 381-392 PHYREG β same.
- Entry chain: fwdev.c:411 fw_ioctl -> fwdev.c:691-694
default: sc->fc->ioctl(ap);(return value discarded β even the EINVAL at fwohci.c:360 never reaches userspace). - Device gate: /dev/fw* clones are 0660 root:operator (fwdev.c fwdev_clone semantics, reproduced by the rig node).
Why it matters (threat)
The writable range covers OHCI_HCCCTL (link enable / soft reset / PHY access enable), all four async + 64 iso DMA context control and command-pointer registers, and the interrupt masks. An operator-group user can: (a) reset the controller/PHY (local DoS of the FireWire subsystem + bus disruption), (b) set DMA context command pointers to attacker-controlled physical addresses. OHCI contexts DMA to/from physical memory without an IOMMU translation on typical 2003-era hardware this driver targets (and on any box without VT-d active): that is arbitrary physical read/write β kernel-text/cred overwrite -> uid=0. We could not exercise hardware DMA on the QEMU guest (no OHCI device exists), so the privesc chain is assessment; the register-write primitive itself is mechanically proven with the unmodified driver.
Fix validation (A/B)
fix.diff: if (caps_priv_check(ap->a_cred, SYSCAP_NODRIVER)) return
(EPERM); after the a_data check (plus #include <sys/caps.h>).
* Baseline kernel (FWO #3): opuser writes land (see run.log).
* Fixed kernel (FWO #4): identical opuser commands leave the register
file untouched (root regrd shows pre-test values; PHY regs
unchanged), root via /dev/fw0.0 still programs registers normally.
See run_fixed.log.
Notes
- fwdev.c swallowing fc->ioctl errors is a separate (minor) stock bug observed during verification; the fix.diff returns EPERM before any register access so the swallowed-return does not matter for the gate (no side effect occurs either way).
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
fixedPatched kernel: identical opuser commands leave the register file untouched (CTL_REGRD shows pre-test values, PHY regs unchanged) while root via /dev/fw0.0 still writes/reads registers normally (0x600d0001 round-trip). Gate effective, functionality preserved.
run_fixed.log DF-2300 section: opuser WRREG/RDREG/PHY ioctls no longer modify MMIO; root cross-check CTL_REGRD=0x0; root write+readback works
Confirmed kernel references
Detail
Exploit chain
unpriv user in operator group -> open /dev/fw0.0 (0660) -> FWOHCI_WRREG{addr<=0x800, data} -> fw_ioctl default -> fwohci_ioctl -> OWRITE into controller register file. On real hardware: program AT/AR/IR context CommandPtr to target physical memory -> DMA engine moves attacker data to/from arbitrary physical addresses -> kernel text/cred overwrite -> uid=0 (assessed, not demonstrable on the QEMU guest without OHCI hardware).
Evidence (decisive lines)
run.log: FWOHCI_WRREG(0x18c,0xdeadbee0) rc=0 readback=0xdeadbee0 plus 0x88/0x50/0x00 writes, RDREG read-backs, PHY ioctls rc=0, root CTL_REGRD confirms MMIO file holds attacker values; run_fixed.log: patched kernel - opuser writes no longer land (MMIO unchanged) while root still programs registers
PoC changes
Built the OHCI-side verbatim-interposition rig (fwohci_harness.c) extending the DF-2293 fwdev rig: fwohci.c TU-included unmodified, emulated MMIO register file via bst=MEM/bsh=®s (x86_64 bus_space is a direct KVA dereference), emulated PHY with 1ms completion callout, fabricated parent bus_dma tags, softc injected as unit 0 of the real firewire_devclass, /dev/fw0.0 against stock firewire_ops 0660 root:operator, root-only /dev/fwoctl0 control device; userland trigger fwpoc.c (FWOHCI_* ioctls + rig control).
Verified recommended fix
Add caps_priv_check(ap->a_cred, SYSCAP_NODRIVER) gate to fwohci_ioctl before the register/PHY switch (fix.diff)
Verdict
Operator-group user wrote attacker-chosen 32-bit values into the OHCI DMA context command pointer (0x18c), interrupt mask (0x88), HCC link control (0x50) and version registers, and executed the PHY access ioctls, all through the unmodified fwohci_ioctl (fwohci.c:333-398) reached via the real fwdev.c dispatch (fwdev.c:694) on the /dev/fw0.0 node with stock 0660 root:operator permissions. No privilege check exists anywhere on the path; root cross-check of the emulated register file confirmed every value landed. On real hardware this register set programs the DMA engine (context command pointers to attacker physical addresses): the classic FireWire arbitrary-physical-R/W primitive on non-IOMMU systems, plus controller reset and PHY bus disruption. Hardware impact is assessment on this hardware-less guest; the missing-gate primitive itself is mechanically proven. Also observed: fwdev.c:694 discards fc->ioctl return values, so even the addr>0x800 EINVAL never reaches userspace.
No comments yet.