⬢ DragonFlyBSD Kernel Audit
DF-4848 / ffvol.c
← back to finding ↓ download raw
/*
 * ffvol.c — adversarial harness for the REAL sys/dev/sound/pcm/feeder_volume.c
 * (GLM 5.3 pass-2, FXROUND follow-up; DF-4848).
 *
 * T1  :277 frame overcount: j = SND_FXDIV(ret, align) misrounds up one frame
 *     when the source return is a bad-residue value inside the claimed-exact
 *     domain => apply() processes (ret/align + 1) frames: reads AND writes
 *     up to align-1 bytes past the fed data; if ret == count the write is
 *     past the destination window.
 * T2  :284-286 count underflow: j*align > count => count -= j wraps to
 *     ~4GiB => the loop re-arms and asks the source for a wild window at
 *     dst+overrun; with more data ready the source writes far past the
 *     window end (bounded only by bufsoft-ready in-kernel).
 */
#include "fxshim.h"
#include "feeder_matrix.c"	/* provides feeder_matrix_default_channel_map() */

/* feeder_volume.c accesses c->volume[][]: give it a complete fake channel */
struct pcm_channel {
	int volume[8][SND_CHN_MAX];
	int flags;
	int direction;
	int feedcount;
};

#include "feeder_volume.c"

static struct pcm_channel ch;
static struct pcm_feeder f;

static int
vol_setup(uint32_t fmt)
{
	memset(&f, 0, sizeof(f));
	f.desc = &f.desc_static;
	f.desc->type = FEEDER_VOLUME;
	f.desc->in = fmt;
	f.desc->out = fmt;
	if (feed_volume_init(&f) != 0)
		return (-1);
	/* non-flat volume so the bypass paths do not short-circuit */
	for (int i = 0; i < 8; i++)
		for (int j = 0; j < SND_CHN_MAX; j++)
			ch.volume[i][j] = SND_VOL_FLAT + 1;
	return (0);
}

int
fx_feeder_set(struct pcm_feeder *fe, int what, int value)
{
	return (feed_volume_set(fe, what, value));
}

static uint64_t fails, checks;

#define RESET_AUDIT(win, sz) do {					\
	fx_audit_max_off = 0; fx_audit_max_ask = 0; fx_audit_given = 0;	\
	fx_audit_asks = 0; fx_audit_wild = 0;				\
	fx_win_base = (uintptr_t)(win);					\
	fx_win_limit = (uintptr_t)(win) + (sz);				\
	fx_arena_base = fx_win_base - FX_GUARD_FRONT;			\
	fx_arena_limit = fx_win_base + (sz) + 65536;			\
} while (0)

int
main(void)
{
	struct feed_volume_info *info;
	uint32_t chn, count, ret, found, align;
	uint8_t *win;
	const char *where;
	uint32_t prng = 0xc2b2ae35u;
	int t;

	/* channel counts giving non-pow2 aligns: 3,5,6,7 (align 6,10,12,14) */
	static const uint32_t chs[] = { 3, 5, 6, 7 };

	for (t = 0; t < (int)(sizeof(chs) / sizeof(chs[0])); t++) {
		chn = chs[t];
		uint32_t fmt = SND_FORMAT(AFMT_S16_LE, chn, 0);
		if (vol_setup(fmt) != 0) {
			printf("setup failed ch=%u\n", chn);
			continue;
		}
		info = f.data;
		align = info->bps * info->channels;

		/* T1: ret values (== count) in bad residues of align */
		found = 0;
		for (count = 1; count <= 65536 && found < 400; count++) {
			if (SND_FXDIV(count, align) != count / align + 1)
				continue;
			found++;
			fx_src_ready = (uint64_t)count;	/* source fills exactly `count` */
			fx_src_give = 0;
			win = fx_win_alloc(count);
			RESET_AUDIT(win, count);
			ret = feed_volume_feed(&f, &ch, win, count, NULL);
			where = NULL;
			checks++;
			if (fx_win_check(win, count, &where) != 0)
				printf("FAIL[T1 write] ch=%u align=%u count=%u "
				    "ret=%u %s maxoff=%llu\n", chn, align, count,
				    ret, where ? where : "-",
				    (unsigned long long)fx_audit_max_off), fails++;
			else if (ret > count)
				printf("FAIL[T1 ret] ch=%u align=%u count=%u ret=%u\n",
				    chn, align, count, ret), fails++;
			fx_win_free(win);
		}

		/* T2: count in bad residue + plenty more data ready:
		 * count -= j*align underflows, loop re-arms with ~4GiB */
		for (count = 1; count <= 65536; count++) {
			if (SND_FXDIV(count, align) != count / align + 1)
				continue;
			fx_src_ready = (uint64_t)count + 32768;	/* more data waiting */
			fx_src_give = 0;
			win = fx_win_alloc(count);
			RESET_AUDIT(win, count);
			ret = feed_volume_feed(&f, &ch, win, count, NULL);
			where = NULL;
			checks++;
			/* wild re-arm: source asked for a window past the end */
			if (fx_audit_wild > 0 || fx_audit_max_ask > count ||
			    fx_audit_max_off > count)
				printf("FAIL[T2 underflow re-arm] ch=%u align=%u "
				    "count=%u ret=%u wild=%d maxask=%llu maxoff=%llu\n",
				    chn, align, count, ret, fx_audit_wild,
				    (unsigned long long)fx_audit_max_ask,
				    (unsigned long long)fx_audit_max_off), fails++;
			else if (fx_win_check(win, count, &where) != 0)
				printf("FAIL[T2 write] ch=%u count=%u %s\n",
				    chn, count, where ? where : "-"), fails++;
			fx_win_free(win);
			if (fails > 20)
				break;
		}
		feed_volume_free(&f);
	}

	/* soak with partial random returns */
	for (t = 0; t < 20000; t++) {
		prng ^= prng << 13; prng ^= prng >> 17; prng ^= prng << 5;
		chn = 1 + (prng % 8);
		uint32_t fmt = SND_FORMAT(AFMT_S16_LE, chn, 0);
		if (vol_setup(fmt) != 0)
			continue;
		info = f.data;
		prng ^= prng << 13; prng ^= prng >> 17; prng ^= prng << 5;
		count = 4096 + (prng % 61441);
		prng ^= prng << 13; prng ^= prng >> 17; prng ^= prng << 5;
		fx_src_ready = (prng & 1) ? (1 << 20) : (prng % count);
		fx_src_give = 0;
		win = fx_win_alloc(count);
		RESET_AUDIT(win, count);
		ret = feed_volume_feed(&f, &ch, win, count, NULL);
		where = NULL;
		checks++;
		if (fx_win_check(win, count, &where) != 0)
			printf("FAIL[soak write] ch=%u count=%u %s\n",
			    chn, count, where ? where : "-"), fails++;
		else if (ret > count)
			printf("FAIL[soak ret] ch=%u count=%u ret=%u\n",
			    chn, count, ret), fails++;
		fx_win_free(win);
		feed_volume_free(&f);
	}

	printf("ffvol: checks=%llu failures=%llu -> %s\n",
	    (unsigned long long)checks, (unsigned long long)fails,
	    fails ? "FAIL" : "ALL PASS");
	return (fails != 0);
}