DF-3461 / wrap_demo.c
/* wrap_demo.c โ userland arithmetic mirror of sys/netinet6/in6_cksum.c:116-158 * * Demonstrates that the in6_cksum() sanity check * if (m->m_pkthdr.len < off + len) panic(...) * is bypassable when (off + len) wraps as unsigned 32-bit, and that the * mbuf walk then terminates with m == NULL, so line 157 * w = (u_int16_t *)(mtod(m, u_char *) + off); * reads m->m_data through a NULL mbuf pointer (low-page fault -> panic). * * This is an arithmetic demonstration only: no in-tree caller of in6_cksum * can currently supply wrapping (off, len) โ see VERDICT.md. Severity Info / * defense-in-depth. */ #include <stdio.h> #include <stdint.h> struct mbuf { int m_len; char *m_data; }; /* exact comparison shape as in the kernel: int pkthdr.len vs u_int32_t off+len */ static const char *sanity(int pkthdr_len, uint32_t off, uint32_t len) { uint32_t sum = off + len; if ((uint32_t)pkthdr_len < sum) return "PANIC(sanity) โ intended behavior"; return "PASSES โ sanity check BYPASSED"; } int main(void) { uint32_t off = 0xFFFFFFFFu, len = 41; int pkthdr_len = 100; struct mbuf m = { .m_len = 100, .m_data = (char *)0x1000 }; struct mbuf *mp = &m; printf("off=0x%08x len=%u -> off+len=0x%08x (%u) wrapped=%d\n", off, len, off + len, off + len, (off + len) < off); printf("m->m_pkthdr.len=%d: %s\n", pkthdr_len, sanity(pkthdr_len, off, len)); /* mirror the walk (in6_cksum.c:150-156); kernel promotes m_len to u32 */ while (mp != NULL && off > 0) { if ((uint32_t)mp->m_len <= off) off -= (uint32_t)mp->m_len; else break; mp = NULL; /* m = m->m_next (chain end) */ } printf("after walk: m=%p off=0x%08x -> w = mtod(m)+off " "= read (struct mbuf*)NULL->m_data -> kernel panic\n", (void *)mp, off); return 0; } |