β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-0492

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0492 Β· 11 files
FileTypeDescriptionSize
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
VERDICT.md verdict full narrative analysis
↓ download 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_testable
baseline reproduced→ patch + rebuild →patched clean

not_testable (root-only reach); fix.diff applies + compiles.

not_testable (root-only reach); fix.diff applies + compiles.
↓ fix.diff6.5-DEV #1

Confirmed kernel references

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.