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

in_delayed_cksum writes through unchecked m_pullup return and silently drops the (possibly reallocated) mbuf pointer

Summary

in_delayed_cksum calls m_pullup to coalesce transport checksum field into first mbuf but (a) never checks return for NULL before dereferencing at :951 and (b) takes mbuf by value so callers (ip_output:641 ip_fragment:802 ip_mloopback:1881) never see new pointer m_pullup may return. Under rare condition where m_pullup takes allocate-new-head branch allocation failure panics kernel via NULL->m_data deref. Successful reallocation leaves every caller holding freed pointer UAF on subsequent m->m_pkthdr.csum_flags write and ifp->if_output handoff. Other two m_pullup callers in this file both NULL-check return this one is outlier. XXX comment at :944-947 shows authors knew layout assumption fragile. Trigger requires non-standard mbuf layout M_EXT/headroom-short/m_next==NULL not reachable from standard UDP/TCP/RAWOUTPUT paths. [VERIFIED pass2: reproduced mechanism - cluster-head split forces m_pullup allocate-new-head branch leaving caller-held pointer on freed mbuf; no standard-path trigger demonstrated; NULL-check fix in combined kernel]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2565 Β· 5 files
FileTypeDescriptionSize
rig.c β€” 5.7 KB view raw
gen.sh β€” 361 B view raw
build.sh β€” 95 B view raw
run.sh β€” 90 B view raw
run.log β€” 646 B view raw

Fix verification

not_testable
↓ fix.diffper-fix-DF-2565

Confirmed kernel references

Detail

Evidence (decisive lines)

cksum2565_run.log mode 0: caller-held m invalid after the call (stale pointer to freed head); control clean

PoC changes

verbatim extract; cluster-head chain forces the reallocation branch

Verified recommended fix

NULL-check the m_pullup return and return the (possibly new) m to callers (in combined fix kernel)

Verdict

Both code defects verified against the verbatim in_delayed_cksum (ip_output.c:929-952): (a) the m_pullup return is used without a NULL check (a too-short chain makes the pullup fail and the next line writes through NULL+offset); (b) m is taken by value -- a cluster-backed head with zero leading space (M_EXT) forces m_pullups allocate-new-head branch (uipc_mbuf.c), which FREES the old head, and every caller (ip_output.c:641/802, ip_mloopback) keeps the stale pointer. Harness: split-header input left the callers pointer targeting a consumed mbuf (m_len=0 read through it) while the checksum went into the new head. No standard TCP/UDP/RAWOUTPUT path was shown to produce the required non-contiguous layout (XXX comment at :944 acknowledges this); impact beyond the demonstrated mechanism: hardening.