โฌข DragonFlyBSD Kernel Audit
DF-3461 / wrap_demo.c
โ† back to finding โ†“ download raw
/* 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;
}