⬢ DragonFlyBSD Kernel Audit
DF-2807 / udev_short.c
← back to finding ↓ download raw
/* DF-2807: short-buffer read() on /dev/udev silently drops the event
 * and reports ENOMEM.
 *
 * Run as root. Open /dev/udev, generate two distinctive events:
 *   e1 = vn0 CREATE        (vnconfig -c vn0 on a blank image)
 *   e2 = h41 CREATE        (h41 module make_dev via debug.h41.cmd=4)
 * then read(fd, 1 byte):
 * expect (vulnerable): -1 ENOMEM, e1 consumed silently; follow-up big
 *   read returns e2 (h41) first -> e1 (vn0) lost forever.
 * expect (patched):    -1 EMSGSIZE, e1 NOT consumed; follow-up big read
 *   returns e1 (vn0) first.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

static void
dump_event(const char *tag, const char *buf, int rv)
{
	int i;

	printf("DF2807 %-22s rv=%d errno=%d\n", tag, rv, errno);
	if (rv <= 0)
		return;
	printf("DF2807 %-22s event: ", "");
	for (i = 0; i < rv; i++)
		putchar((buf[i] >= 32 && buf[i] < 127) ? buf[i] : '.');
	putchar('\n');
}

static int
has(const char *xml, const char *key)
{
	return strstr(xml, key) != NULL;
}

int
main(void)
{
	static char big[65536];
	int fd, rv;
	char one;

	system("dd if=/dev/zero of=/tmp/df2807.img bs=1m count=2 2>/dev/null");

	fd = open("/dev/udev", O_RDONLY | O_NONBLOCK);
	if (fd < 0) { perror("open /dev/udev"); return 1; }
	printf("DF2807 opened /dev/udev fd=%d\n", fd);

	/* initiate the reader: first read arms the marker (kern_udev.c:826) */
	{
		char c;
		errno = 0;
		rv = read(fd, &c, 1);
		printf("DF2807 initiation read rv=%d errno=%d (35/EAGAIN expected)\n",
		    rv, errno);
	}

	/* e1: vn0 attach */
	system("vnconfig -c vn0 /tmp/df2807.img 2>/dev/null");
	/* e2: h41 create */
	system("sysctl -w debug.h41.cmd=4 >/dev/null 2>&1");
	sleep(1);

	one = 0;
	errno = 0;
	rv = read(fd, &one, 1);
	dump_event("short read(1B)", &one, rv);

	errno = 0;
	rv = read(fd, big, sizeof(big));
	dump_event("full read #1", big, rv);
	if (rv > 0) {
		char *nm = strstr(big, "<key>name</key>");
		char namev[64] = "?";
		if (nm) {
			char *s1 = strstr(nm, "<string>");
			char *e1;
			if (s1) {
				s1 += 8;
				e1 = strstr(s1, "</string>");
				if (e1 && e1 - s1 < 63) {
					memcpy(namev, s1, e1 - s1);
					namev[e1 - s1] = 0;
				}
			}
		}
		printf("DF2807 first surviving event name=\"%s\" "
		    "(vn0 = e1 kept -> patched; anything else -> e1 DROPPED)\n",
		    namev);
	}

	/* drain one more to show what remains */
	errno = 0;
	rv = read(fd, big, sizeof(big));
	dump_event("full read #2", big, rv);

	system("vnconfig -u vn0 2>/dev/null");
	return 0;
}