⬢ DragonFlyBSD Kernel Audit
DF-3322 / df3322.c
← back to finding ↓ download raw
/*
 * DF-3322 harness -- ip_input() dereferences m_pkthdr.rcvif without a
 * NULL check on the 127/8 martian test (ip_input.c:544):
 *
 *	if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
 *	    (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
 *		if (!(m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK)) {  <-- deref
 *
 * Every other rcvif use in the function is NULL-guarded.  The only
 * in-tree producer of rcvif==NULL mbufs on the ip_input netisr is the
 * netgraph7 ip_input node (ng_ip_input.c:118-125: m from an ng socket
 * hook, M_HASH cleared, netisr_queue(NETISR_IP, m) -- rcvif never
 * set).  netgraph7 is not in the stock guest kernel/modules, so the
 * panic is not live-reachable HERE; this harness proves the deref.
 */
#include "guard.h"
#include <stdio.h>
#include <stdint.h>

#define	IFF_LOOPBACK	0x8

struct ifnet_min { char if_pad[4]; int if_flags; };
struct pkthdr { struct ifnet_min *rcvif; };
struct mbufh { struct pkthdr m_pkthdr; };

/* verbatim ip_input.c:541-548 */
static int
ip_input_martian(struct mbufh *m, uint32_t dst)
{
	(void)dst;	/* dst/src in 127/8 taken as given */
	volatile int fl = m->m_pkthdr.rcvif->if_flags;	/* :544 deref */
	if (!(fl & IFF_LOOPBACK))
		return 1;					/* reject */
	return 0;
}

	static struct mbufh *m;

static struct mbufh *m;

int
main(int argc, char **argv)
{
	int nul = (argc > 1 && argv[1][0] == 'n');

	printf("DF-3322 (rcvif deref without NULL check, ip_input.c:544)\n");
	m = (struct mbufh *)guard_buf(sizeof(*m));
	{
		static struct ifnet_min okif;
		okif.if_flags = IFF_LOOPBACK;
		m->m_pkthdr.rcvif = nul ? NULL : &okif;
	}
	printf("  crafted 127/8 packet queued with rcvif=%s\n",
	    nul ? "NULL (ng_ip_input path)" : "valid");
	fflush(stdout);
#ifdef FIXED
	/* DF-3322 fix: the same NULL guard every other rcvif use has */
	if (m->m_pkthdr.rcvif == NULL) {
		printf("FIXED: rcvif NULL, packet dropped\n");
		return 0;
	}
#endif
	ip_input_martian(m, 0x7f000001);
	printf("  ran (guarded)\n");
	return 0;
}