plip: lpoutput resurrects IFF_RUNNING and drives the port without IFF_UP or ppbus ownership β cross-driver corruption & broken interface state
Summary
lpoutput unconditionally sets IFF_RUNNING and writes CTR=IRQENABLE with no IFF_UP or ownership check. REPRODUCED on guest: after ifconfig down (bus released, intr torn down), ONE unpriv packet made the DOWN interface RUNNING; the following up landed in a broken UP-without-RUNNING half-state (the setup branch requires !RUNNING). While down it also stomps concurrent lpt0 jobs and enables IRQs with no handler. Early-return-when-down fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4658 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| run.log | β | 849 B | view raw | |
| env.txt | β | 271 B | view raw | |
| README.md | β | 1.6 KB | β raw | |
| VERDICT.md | β | 1.5 KB | β raw |
DF-4658 β lpoutput drives hardware & resurrects IFF_RUNNING without IFF_UP/bus ownership
File: sys/dev/netif/plip/if_plip.c:602,609 (plus :613-616,685-688,730-733) Class: CWE-1254/incorrect state management β Low severity, certain Verdict: REPRODUCED (mechanism + anomalous flags; dos-flavored)
Repro (guest, run.log)
- root:
ifconfig lp0 10.99.0.1 10.99.0.2 up(plip owns ppbus, intr attached) - root:
ifconfig lp0 downβ flags<POINTOPOINT,SIMPLEX,MULTICAST,PROXY>(no UP, no RUNNING; ppb_release_bus tore down lp_intr β ppbconf.c:514-521) - unpriv user sends ONE UDP packet to the still-routed peer:
./plipspin 10.99.0.2 1 1400β lpoutput runs anyway: sets IFF_RUNNING (if_plip.c:602), ppb_wctr(IRQENABLE) (:609), transmits, and calls lp_intr directly (:730) β while plip no longer owns the bus. β flags now<POINTOPOINT,RUNNING,...>: RUNNING on a DOWN interface. - root:
ifconfig lp0 upβ lands in a broken half-state (<UP,POINTOPOINT,...>w/o RUNNING/setup-path): the UP transition at if_plip.c:315 requires !RUNNING; the flag merge vs the RUNNING bit set by the stray output makes the setup path skip/mis-order β no bus request/intr setup is guaranteed for the resurrected state.
Impact
Any unprivileged user with a route via lp0 during a down window: (a) corrupts a concurrent lpt0 job on the same port (writes DTR/CTR), (b) re-enables port IRQs with no handler attached (stray-IRQ storm potential), (c) leaves the interface in inconsistent RUNNING states. On the guest, (c)+flags anomaly demonstrated byte-for-byte in run.log.
Status: reproduced (logic/state machine, dos-flavored) Β· confidence: certain
Demonstrated on the QEMU guest (same environment as DF-4657):
- after ifconfig lp0 down, flags show no RUNNING;
- ONE unprivileged UDP packet to 10.99.0.2 (host route survives down on
this stack; a static -iface route also works) β ifconfig lp0 shows
RUNNING on the down interface (if_plip.c:602), i.e. lpoutput wrote
CTR=IRQENABLE (:609) and drove the data register with no bus ownership
and no interrupt handler attached (torn down at release, ppbconf.c:514);
- the subsequent ifconfig lp0 up no longer runs the setup branch
(if_plip.c:315 requires !(if_flags & IFF_RUNNING)) β output shows the
half-state UP without RUNNING, i.e. the admin's up/down cycle has
been corrupted by an unprivileged packet.
Cross-driver consequence (not emulated: no second consumer loaded): the same code path writes the parallel-port registers while lpt0 owns the bus, garbling print jobs, and re-enables IRQs with no handler.
fix: lpoutput must return early when !(ifp->if_flags & IFF_UP) or bus not owned; never set IFF_RUNNING from the output path.
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testablenot validated (Low; fix is a two-line early-return in lpoutput when !IFF_UP)
Confirmed kernel references
Detail
Exploit chain
unpriv user -> UDP routed via downed lp0 -> lpoutput writes DTR/CTR without bus ownership + sets RUNNING -> lpt job corruption / stray IRQs / admin's up-cycle corrupted
Evidence (decisive lines)
run.log: flags sequence POINTOPOINT-only -> +RUNNING after one unpriv packet while DOWN -> UP-without-RUNNING after ifconfig up
PoC changes
reused DF-4657 sender; no changes needed
Verified recommended fix
In lpoutput(): if (!(ifp->if_flags & IFF_UP)) { m_freem(m); return ENETDOWN; } and never set IFF_RUNNING from the output path
Verdict
lpoutput() unconditionally sets IFF_RUNNING (if_plip.c:602), writes CTR=IRQENABLE (:609) and drives the port + calls lp_intr regardless of IFF_UP or ppbus ownership. Reproduced on the guest: one unprivileged UDP packet during a down-window put RUNNING on the DOWN interface, and the following ifconfig up landed in a broken UP-without-RUNNING half-state (setup branch at :315 requires !RUNNING). Enables cross-driver corruption of a concurrent lpt0 job and IRQ enablement with no handler.
No comments yet.