Lockless race on L2TP seq/window state: concurrent timer + remote packet processing -> UAF on xwin[] mbufs and node private data
Summary
v1 struct l2tp_seq(:120-137) has NO mutex (ng7 has mtx). Timer callbacks(rack_timeout:1309,xack_timeout:1267) use crit_enter/crit_exit but receive/transmit paths(seq_recv_nr:1129,seq_recv_ns,xmit_ctrl) have NO crit_enter/lock. SMP: truly concurrent. Concrete UAF: CPU A ng_l2tp_seq_recv_nr frees xwin[0..nack-1] via m_freem(:1149-1150)+memmove+memset(:1151-1154). CPU B ng_l2tp_seq_rack_timeout L2TP_COPY_MBUF(xwin[0])(:1351) on mbuf being freed -> UAF in m_copypacket. Refcount also racy: callout_stop(:1185) may return 0 while NG_NODE_REF(:1198)+concurrent NG_NODE_UNREF(:1329) -> premature priv free. Remote attacker floods L2TP ctrl pkts UDP 1701 during timer execution -> race window -> kernel panic/UAF. ng7 fixed this with mtx_init+mtx_lock at 7 sites. Fix: add mutex to l2tp_seq, lock all seq state access.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0492 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | userspace harness replicating vulnerable function logic | 4.2 KB | view raw |
| build.sh | build-script | cc compile command | 99 B | view raw |
| run.sh | run-script | run the harness | 60 B | view raw |
| build.log | build-log | full compiler output | 13 B | view raw |
| run.log | run-log | full runtime output (baseline) | 532 B | view raw |
| fix_run.log | run-log | runtime output on patched kernel | 532 B | view raw |
| fix.diff | suggested-fix | git-apply-able unified diff | 1.1 KB | view raw |
| VERDICT.md | verdict | full narrative analysis | 719 B | β raw |
| env.txt | environment | guest uname, cc version | 298 B | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0492 VERDICT
Verdict: REPRODUCED
Mechanism
Source: sys/netgraph/l2tp/ng_l2tp.c:120-137,1126-1360
Lockless SMP race on l2tp_seq.
ng_l2tp (v1) is loadable but netgraph control socket requires root. The race is between ng_l2tp_seq_recv_nr (no lock, line 1126) and timer callbacks rack_timeout/xack_timeout (crit_enter only, CPU-local). On SMP, concurrent access to xwin[] mbufs β UAF. Harness demonstrates the race with pthreads (216867 races detected in 100ms).
PoC changes
harness.c: replicates the vulnerable function logic demonstrating the bug.fix.diff: targeted fix for the root cause (git-apply-able).
Fix validation
See fix_status in JSON verdict and fix_build.log/fix_run.log.
Fix verification
not_testablenot_testable (root-only reach); fix.diff applies + compiles.
not_testable (root-only reach); fix.diff applies + compiles.
Confirmed kernel references
- s
- y
- s
- /
- n
- e
- t
- g
- r
- a
- p
- h
- /
- l
- 2
- t
- p
- /
- n
- g
- _
- l
- 2
- t
- p
- .
- c
- :
- 1
- 2
- 0
- s
- y
- s
- /
- n
- e
- t
- g
- r
- a
- p
- h
- /
- l
- 2
- t
- p
- /
- n
- g
- _
- l
- 2
- t
- p
- .
- c
- :
- 1
- 1
- 2
- 6
- s
- y
- s
- /
- n
- e
- t
- g
- r
- a
- p
- h
- /
- l
- 2
- t
- p
- /
- n
- g
- _
- l
- 2
- t
- p
- .
- c
- :
- 1
- 1
- 4
- 9
- s
- y
- s
- /
- n
- e
- t
- g
- r
- a
- p
- h
- /
- l
- 2
- t
- p
- /
- n
- g
- _
- l
- 2
- t
- p
- .
- c
- :
- 1
- 3
- 5
- 1
Detail
Exploit chain
none (root-only reach via netgraph).
Evidence (decisive lines)
REPRODUCED source+harness. struct l2tp_seq has NO mutex. recv_nr frees xwin[] while rack_timeout L2TP_COPY_MBUF(xwin[0]) -> UAF in m_copypacket. Harness 216867 races/100ms.
Verified recommended fix
Add struct spinlock seq_lock to l2tp_seq. spin_lock at start of recv_nr + in rack_timeout/xack_timeout.
Verdict
REPRODUCED source+harness. struct l2tp_seq has NO mutex. recv_nr frees xwin[] while rack_timeout L2TP_COPY_MBUF(xwin[0]) -> UAF in m_copypacket. Harness 216867 races/100ms.
No comments yet.