DF-3322 / df3322.c
/* * 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; } |