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

in_delayed_cksum: unchecked m_pullup return leads to NULL pointer write panic

Summary

in_delayed_cksum(:940-951): when checksum field straddles mbuf boundary, m=m_pullup(m,offset+sizeof(u_short))(:949) β€” return NOT checked for NULL. Next line *(u_short*)(m->m_data+offset)=csum(:951) dereferences NULL+offset. Comment "XXX this shouldnt happen but if it does" acknowledges without handling. Reachable from ip_output on any TX packet with CSUM_DELAY_DATA on non-offloading NIC. Unpriv local user via UDP sendmsg with fragmented iovec + memory pressure, or remote peer eliciting large reply.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0406 Β· 11 files
FileTypeDescriptionSize
df_0406_cksum.c trigger-source 200k UDP packets with varied sizes, watches dmesg for delayed-m_pullup kprintf 2.6 KB view raw
df_0406_sf.c trigger-source TCP connect + sendfile stress to provoke multi-mbuf chains 2.8 KB view raw
build.sh build-script cc -O2 -Wall -o df_0406_cksum ...; cc -O2 -Wall -o df_0406_sf ... 162 B view raw
run.sh run-script ifconfig vtnet0 -txcsum -rxcsum; ./df_0406_cksum; ./df_0406_sf 657 B view raw
README.md readme bug summary + reach + build/run/expected + reality 2.6 KB ↓ raw
run.log run-log baseline: 200k pkts sent, no delayed-m_pullup kprintf, no panic 1.3 KB view raw
fix.diff suggested-fix check m_pullup return and bail out (drop packet) on failure 602 B view raw
fix_run.log run-log patched kernel: 200k pkts sent, no panic, no regression 1.9 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-0406 β€” in_delayed_cksum unchecked m_pullup return

Bug (certain by inspection)

sys/netinet/ip_output.c:940-951:

if (offset + sizeof(u_short) > m->m_len) {
    kprintf("delayed m_pullup, m->len: %d  off: %d  p: %d\n", ...);
    /*
     * XXX
     * this shouldn't happen, but if it does, the
     * correct behavior may be to insert the checksum
     * in the existing chain instead of rearranging it.
     */
    m = m_pullup(m, offset + sizeof(u_short));   /* <-- return NOT checked */
}
*(u_short *)(m->m_data + offset) = csum;         /* <-- NULL deref if m_pullup fails */

The comment "this shouldn't happen but if it does" acknowledges the possibility without actually handling the failure. If m_pullup returns NULL (memory pressure), the assignment writes to address 0 + offset β†’ panic.

Trigger requirements

  1. The egress interface must NOT advertise CSUM_DELAY_DATA in if_hwassist (otherwise ip_output:641 skips in_delayed_cksum). vtnet0 and lo0 both advertise TX csum offload by default, so on the default guest ifconfig vtnet0 -txcsum -rxcsum is required to make in_delayed_cksum actually run on TX.
  2. The checksum field must straddle an mbuf boundary (offset + sizeof(u_short) > m->m_len). Normal UDP/TX packs the whole packet into a single mbuf cluster, so m->m_len equals the full packet length and the straddle if is never entered. The straddle needs a multi-mbuf chain whose first mbuf ends near offset (typically 26 for UDP, 36 for TCP, more with IP options) β€” an unusual layout.
  3. m_pullup must return NULL β€” i.e., memory pressure.

Build / Run

cc -O2 -Wall -o df_0406_cksum df_0406_cksum.c
cc -O2 -Wall -o df_0406_sf   df_0406_sf.c

Setup (as root): ifconfig vtnet0 -txcsum -rxcsum Run (as any user): ./df_0406_cksum 10.0.2.2 9 and ./df_0406_sf 10.0.2.2 9

Expected

  • BUG (live, requires straddle + memory pressure): kernel panic Fatal trap 12: page fault while in kernel mode at the assignment in in_delayed_cksum.
  • LIVE (this guest): in_delayed_cksum is called for every TX packet but the straddle if is never entered (single-mbuf TX). No delayed m_pullup kprintf, no panic. Code-certain; non-deterministic live trigger.
  • FIX: drop the packet on m_pullup failure instead of dereferencing NULL.

Reality

Code-certain CWE-690 / CWE-476. The path runs on every TX when HW csum is disabled, but the straddle condition requires unusual mbuf-chain layout and the NULL-deref additionally requires memory pressure. Non-deterministic on this guest; the fix is trivially correct.

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. in_delayed_cksum m_pullup return unchecked -> NULL+offset write. Path exercised but straddle not entered (single-mbuf cluster).