pf_fragcache: m_dup NULL dereferenced before NULL check in m_adj argument: remote DoS under memory pressure with fragcrop
Summary
pf_fragcache(:661-666) fragcrop overlap path: *m0=m_dup(m,M_NOWAIT) can return NULL under memory pressure. Argument expression at :663-664 m_adj(*m0,(h->ip_hl<<2)-(*m0)->m_pkthdr.len) dereferences *m0 BEFORE NULL check at :665. Comment "From KAME Project: We have missed this!" acknowledges the issue but the check is still AFTER the deref. Trigger: pf scrub with PFRULE_FRAGCROP + overlapping fragment + memory pressure (fragment flood uses M_NOWAIT). Remote unauthenticated kernel panic. Requires non-default fragcrop config.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0391 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df_0391_fragcache.c | trigger-source | raw-IP overlapping-frag sender + mbuf-pressure children | 6.0 KB | view raw |
| build.sh | build-script | cc -O2 -Wall -o df_0391_fragcache | 162 B | view raw |
| run.sh | run-script | checks pf rule loaded, runs trigger as root | 651 B | view raw |
| README.md | readme | bug summary + reach + build/run/expected + reality | 2.0 KB | β raw |
| run.log | run-log | baseline: pfctl counters (normalize 1066, fragment 1), objcache-exhausted warnings, no panic | 1.6 KB | view raw |
| fix.diff | suggested-fix | move NULL check before m_adj argument expression | 717 B | view raw |
| fix_run.log | run-log | patched kernel: path still reached, no panic (consistent with baseline) | 2.3 KB | view raw |
| env.txt | environment | uname + cc version | 247 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-0391 β pf_fragcache m_dup NULL-deref trigger
Bug (certain by inspection)
sys/net/pf/pf_norm.c:661-666 (pf_fragcache, the precut > 0 overlap path):
*m0 = m_dup(m, M_NOWAIT);
/* From KAME Project : We have missed this! */
m_adj(*m0, (h->ip_hl << 2) - (*m0)->m_pkthdr.len); /* <-- derefs *m0 */
if (*m0 == NULL) /* <-- check too late */
goto no_mem;
The argument expression of m_adj evaluates (*m0)->m_pkthdr.len BEFORE
the NULL-check on the next line. The "We have missed this!" KAME comment
acknowledges the corner but the ordering is still wrong.
Trigger requirements
pf.koloaded (root:kldload pf).- A PF rule with
scrub ... fragment crop(PFRULE_FRAGCROP) β non-default. - Two IP fragments where the second overlaps the cached first (enters
precut > 0). m_dup(m, M_NOWAIT)returning NULL β i.e., mbuf-pressure on the system.
Build / Run
cc -O2 -Wall -o df_0391_fragcache df_0391_fragcache.c
Setup as root:
kldload pf cat > /etc/pf-0391.conf <<EOF scrub in on lo0 all fragment crop pass in on lo0 all pass out all EOF pfctl -d; pfctl -F all; pfctl -f /etc/pf-0391.conf; pfctl -e ./df_0391_fragcache
Expected
- Path reachability (always observable):
pfctl -s infoshows non-zeronormalizecounter (pf_normalize_ip called) and non-zerofragmentcounter (overlap detected, fragcacheprecut > 0path entered). - Live panic (non-deterministic): only if
m_dup(M_NOWAIT)returns NULL. On DragonFly the objcache transparently refills from the master allocator before failing, so NULL is hard to provoke even whenobjcache ... exhaustedwarnings appear in dmesg. - FIX: reorder the NULL check before the
m_adjargument expression.
Reality
Code-certain CWE-476; fragcache path reachable on default GENERIC + loaded
pf.ko with a non-default fragcrop scrub rule; live panic requires sustained
memory pressure that defeats DragonFly's objcache refill. Non-default-config
remote DoS (CWE-476) β realistic threat for admins who deploy scrub crop.
Fix verification
not_testablecompile validated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. pf_fragcache m_adj(*m0,...) evaluates before NULL check. Path reached but m_dup never returned NULL (objcache refills).
No comments yet.