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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0391 Β· 10 files
FileTypeDescriptionSize
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
README.md readme bug summary + reach + build/run/expected + reality
↓ download 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

  1. pf.ko loaded (root: kldload pf).
  2. A PF rule with scrub ... fragment crop (PFRULE_FRAGCROP) β€” non-default.
  3. Two IP fragments where the second overlaps the cached first (enters precut > 0).
  4. 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 info shows non-zero normalize counter (pf_normalize_ip called) and non-zero fragment counter (overlap detected, fragcache precut > 0 path 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 when objcache ... exhausted warnings appear in dmesg.
  • FIX: reorder the NULL check before the m_adj argument 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_testable

compile 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).