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)
PoC verification
Evidence pack
findings/poc/DF-0406 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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
- The egress interface must NOT advertise
CSUM_DELAY_DATAinif_hwassist(otherwiseip_output:641skipsin_delayed_cksum).vtnet0andlo0both advertise TX csum offload by default, so on the default guestifconfig vtnet0 -txcsum -rxcsumis required to makein_delayed_cksumactually run on TX. - 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, som->m_lenequals the full packet length and the straddleifis never entered. The straddle needs a multi-mbuf chain whose first mbuf ends nearoffset(typically 26 for UDP, 36 for TCP, more with IP options) β an unusual layout. m_pullupmust 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 modeat the assignment inin_delayed_cksum. - LIVE (this guest):
in_delayed_cksumis called for every TX packet but the straddleifis never entered (single-mbuf TX). Nodelayed m_pullupkprintf, no panic. Code-certain; non-deterministic live trigger. - FIX: drop the packet on
m_pullupfailure 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_testablecompile 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).
No comments yet.