⬢ DragonFlyBSD Kernel Audit
DF-4192 / es_batt.c
← back to finding ↓ download raw
/* es137x pass-2 live battery — run as UNPRIVILEGED user against QEMU ES1370.
 * Hammers mixer ioctls (codec index paths), dsp fragment/speed/format math,
 * trigger cycles, and concurrent open/close + setfragment-during-stream races.
 * Goal: panic / hang / anything. Exit 0 = survived.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

static volatile sig_atomic_t stop = 0;
static void onalrm(int s __attribute__((unused))) { stop = 1; }

static void
mixer_child(void)
{
	int fd = open("/dev/mixer", O_RDWR);
	int v, i;
	if (fd < 0) { perror("mixer open"); _exit(0); }
	while (!stop) {
		/* every possible device index, incl. out-of-range 25..255 */
		for (i = 0; i < 256; i++) {
			v = 0x6464;
			ioctl(fd, MIXER_WRITE(i), &v);
			v = 0xffff;
			ioctl(fd, MIXER_WRITE(i), &v);
			v = 0;
			ioctl(fd, MIXER_WRITE(i), &v);
			v = -1;
			ioctl(fd, MIXER_WRITE(i), &v);
			v = 0x7f7f;
			ioctl(fd, MIXER_READ(i), &v);
		}
		/* recsrc patterns */
		v = 0;         ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &v);
		v = ~0;        ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &v);
		v = 0x5555;    ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &v);
		v = 0x80000000; ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &v);
		for (i = 0; i < 25; i++) {
			v = 1 << i;
			ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &v);
		}
	}
	close(fd);
	_exit(0);
}

static const int frags[] = {
	0x00000000, 0x00010000, 0x00000004, 0x0000000a, 0x00000010,
	0x0000ffff, 0x00ff000a, 0x01000006, 0x0002007f, 0x7fff0000,
	0x7fff7fff, 0xffff0010, 0x00040010, 0x10000004, 0x0003000a,
};
static const int speeds[] = {
	0, 1, 2, 3999, 4000, 5512, 8000, 22050, 44100, 48000, 48001,
	65535, 1000000, 0x7fffffff, (int)0x80000000, -1,
};

static void
dsp_child(int rec)
{
	const char *dev = rec ? "/dev/dsp" : "/dev/dsp";
	int fd = open(dev, rec ? O_RDONLY : O_WRONLY | O_NONBLOCK);
	char buf[8192];
	int i, v, fmt, ch;
	audio_buf_info abi;

	if (fd < 0) { perror("dsp open"); _exit(0); }
	while (!stop) {
		for (i = 0; i < (int)(sizeof(frags)/sizeof(frags[0])); i++) {
			v = frags[i];
			ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &v);
		}
		for (i = 0; i < (int)(sizeof(speeds)/sizeof(speeds[0])); i++) {
			v = speeds[i];
			ioctl(fd, SNDCTL_DSP_SPEED, &v);
		}
		for (fmt = 0; fmt <= 0x30; fmt++) {
			v = fmt;
			ioctl(fd, SNDCTL_DSP_SETFMT, &v);
			for (ch = 0; ch <= 8; ch++) {
				v = ch;
				ioctl(fd, SNDCTL_DSP_CHANNELS, &v);
			}
		}
		/* trigger cycle */
		v = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT;
		ioctl(fd, SNDCTL_DSP_SETTRIGGER, &v);
		v = 0;
		ioctl(fd, SNDCTL_DSP_SETTRIGGER, &v);
		if (ioctl(fd, rec ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE,
		    &abi) == 0)
			; /* sanity read */
		v = 0;
		ioctl(fd, rec ? SNDCTL_DSP_GETIPTR : SNDCTL_DSP_GETOPTR, &v);
		/* stream a bit */
		if (rec) {
			while (read(fd, buf, sizeof(buf)) < 0 && errno == EINTR)
				;
		} else {
			memset(buf, 0x5a, sizeof(buf));
			write(fd, buf, sizeof(buf));
			write(fd, buf, 1);        /* 1-byte write */
			write(fd, buf, 3);        /* unaligned */
		}
		{
			struct pollfd pfd = { .fd = fd,
			    .events = rec ? POLLIN : POLLOUT };
			poll(&pfd, 1, 10);
		}
	}
	close(fd);
	_exit(0);
}

/* race: SETFRAGMENT/SPEED storm while another thread streams on same fd */
static void
race_child(void)
{
	int fd = open("/dev/dsp", O_RDWR | O_NONBLOCK);
	char buf[4096];
	int i, v;
	if (fd < 0) { perror("race open"); _exit(0); }
	v = 16 << 16 | 10;   /* 16 frags of 1k */
	ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &v);
	v = 44100;
	ioctl(fd, SNDCTL_DSP_SPEED, &v);
	v = AFMT_S16_LE;
	ioctl(fd, SNDCTL_DSP_SETFMT, &v);
	v = 2;
	ioctl(fd, SNDCTL_DSP_CHANNELS, &v);
	v = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT;
	ioctl(fd, SNDCTL_DSP_SETTRIGGER, &v);
	while (!stop) {
		for (i = 0; i < (int)(sizeof(frags)/sizeof(frags[0])); i++) {
			v = frags[i];
			ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &v);
		}
		v = speeds[rand() % (int)(sizeof(speeds)/sizeof(speeds[0]))];
		ioctl(fd, SNDCTL_DSP_SPEED, &v);
		memset(buf, 0xa5, sizeof(buf));
		write(fd, buf, sizeof(buf));
		read(fd, buf, 64);
		v = 0;
		ioctl(fd, SNDCTL_DSP_SETTRIGGER, &v);
		v = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT;
		ioctl(fd, SNDCTL_DSP_SETTRIGGER, &v);
	}
	close(fd);
	_exit(0);
}

/* rapid open/close churn */
static void
churn_child(void)
{
	while (!stop) {
		int fd = open("/dev/dsp", O_RDWR | O_NONBLOCK);
		if (fd >= 0) {
			int v = 0x0003000c;
			ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &v);
			close(fd);
		}
		fd = open("/dev/mixer", O_RDWR);
		if (fd >= 0)
			close(fd);
	}
	_exit(0);
}

int
main(int argc, char **argv)
{
	pid_t p[5];
	int i, st;
	const char *mode = argc > 1 ? argv[1] : "all";

	signal(SIGALRM, onalrm);
	alarm(atoi(getenv("BATT_SECS") ? getenv("BATT_SECS") : "25"));

	i = 0;
#define RUN(idx, fn) do { \
	if (!strcmp(mode, "all") || !strcmp(mode, #idx)) { \
		p[i] = fork(); if (p[i] == 0) fn; i++; \
	} } while (0)
	RUN(mix, mixer_child());
	RUN(play, dsp_child(0));
	RUN(rec, dsp_child(1));
	RUN(race, race_child());
	RUN(churn, churn_child());
#undef RUN

	for (i = 0; i < 5; i++)
		if (p[i] > 0)
			waitpid(p[i], &st, 0);
	printf("battery(%s): children reaped, rc=0\n", mode);
	return (0);
}