⬢ DragonFlyBSD Kernel Audit
DF-4848 / fxfmt.c
← back to finding ↓ download raw
/*
 * fxfmt.c — adversarial harness for the REAL sys/dev/sound/pcm/feeder_format.c
 * (GLM 5.3 pass-2, FXROUND follow-up; DF-4846).
 *
 * T1  entry-rounding overshoot (:220): counts inside the claimed-exact domain
 *     [1,65536] where SND_FXROUND(count, oalign) > count => the feeder sizes
 *     the whole pass for count' > count, the source writes past the window
 *     and the return exceeds count (sndbuf_acquire ring overflow in-kernel).
 * T2  ialign>oalign (:233): FXROUND(count', ialign) misrounds => j > count'
 *     => src = dst + count - j < dst (write BEFORE the window) and the
 *     source window j exceeds the destination window.
 * T3  partial source returns in bad residues (:240): FXDIV(ret, ialign)
 *     misrounds a frame up => the feeder converts (and reports) more output
 *     bytes than the source delivered input: ret > given * oalign / ialign.
 */
#include "fxshim.h"
#include "feeder_format.c"

static struct pcm_feeder f;

static int
fmt_setup(uint32_t in, uint32_t out)
{
	memset(&f, 0, sizeof(f));
	f.desc = &f.desc_static;
	f.desc->in = in;
	f.desc->out = out;
	if (feed_format_init(&f) != 0)
		return (-1);
	return (0);
}

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

static uint64_t fails, checks;

static void
report(const char *tag, uint32_t count, uint32_t ret, const char *where,
    uint64_t maxoff, uint64_t maxask, uint32_t ch, uint32_t in, uint32_t out)
{
	printf("FAIL[%s] ch=%u in=%08x out=%08x count=%u ret=%u %s "
	    "maxwrite_off=%llu maxask=%llu\n",
	    tag, ch, in, out, count, ret, where ? where : "-",
	    (unsigned long long)maxoff, (unsigned long long)maxask);
	fails++;
}

#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)

static const uint32_t bad_aligns[] = { 3, 5, 6, 7, 10, 12, 14, 15, 20, 21, 24, 28, 30 };

int
main(void)
{
	struct feed_format_info *info;
	uint32_t ch, in, out, count, ret, d, found, ai;
	uint8_t *win;
	const char *where;
	uint32_t prng = 0x9e3779b9u;
	int t;

	static const struct { uint32_t in, out; const char *tag; } pairs[] = {
		{ AFMT_S16_LE, AFMT_S32_LE, "T1 oalign>ialign" },
		{ AFMT_S16_LE, AFMT_S24_LE, "T1b" },
		{ AFMT_S32_LE, AFMT_S16_LE, "T2 ialign>oalign" },
		{ AFMT_S24_LE, AFMT_S16_LE, "T2b" },
	};

	for (t = 0; t < (int)(sizeof(pairs) / sizeof(pairs[0])); t++) {
		for (ch = 5; ch <= 8; ch++) {
			in  = SND_FORMAT(pairs[t].in,  ch, 0);
			out = SND_FORMAT(pairs[t].out, ch, 0);
			if (fmt_setup(in, out) != 0)
				continue;
			info = f.data;
			for (ai = 0; ai < sizeof(bad_aligns) / sizeof(bad_aligns[0]); ai++) {
				d = bad_aligns[ai];
				if (d != info->oalign && d != info->ialign)
					continue;
				found = 0;
				for (count = 1; count <= 65536 && found < 300; count++) {
					if (SND_FXROUND(count, d) <= count)
						continue;
					found++;
					fx_src_ready = 1 << 20;
					fx_src_give = 0;
					win = fx_win_alloc(count);
					RESET_AUDIT(win, count);
					ret = feed_format_feed(&f, NULL, win, count, NULL);
					where = NULL;
					checks++;
					if (fx_win_check(win, count, &where) != 0)
						report(pairs[t].tag, count, ret, where,
						    fx_audit_max_off, fx_audit_max_ask, ch, in, out);
					else if (ret > count)
						report(pairs[t].tag, count, ret,
						    "return > count (sndbuf_acquire ring overflow)",
						    fx_audit_max_off, fx_audit_max_ask, ch, in, out);
					else if (fx_audit_max_off > count)
						report(pairs[t].tag, count, ret,
						    "source wrote past window",
						    fx_audit_max_off, fx_audit_max_ask, ch, in, out);
					fx_win_free(win);
				}
			}
			feed_format_free(&f);
		}
	}

	/* ---- T3: partial source return in bad residue -> frame overcount ---- */
	{
		static const uint32_t t3[] = { 12, 14, 15, 24, 28 };
		uint32_t i3;
		for (i3 = 0; i3 < sizeof(t3) / sizeof(t3[0]); i3++) {
			uint32_t ial = t3[i3];
			uint32_t ibps = (ial == 15) ? 3 : (ial == 24 || ial == 28) ? 4 : 2;
			uint32_t chn = ial / ibps;
			uint32_t obps = (ibps == 2) ? 4 : 2;
			in  = SND_FORMAT(ibps == 2 ? AFMT_S16_LE :
			    (ibps == 3 ? AFMT_S24_LE : AFMT_S32_LE), chn, 0);
			out = SND_FORMAT(obps == 2 ? AFMT_S16_LE : AFMT_S32_LE, chn, 0);
			if (fmt_setup(in, out) != 0)
				continue;
			info = f.data;
			found = 0;
			for (count = 1; count <= 65536 && found < 200; count++) {
				/* `count` = a bad-residue partial return for ialign */
				if (SND_FXDIV(count, ial) != count / ial + 1)
					continue;
				found++;
				uint32_t window = 65520;
				/* make the window a clean multiple of oalign so only
				 * the :240 ret-misround is exercised */
				window -= (window % info->oalign);
				fx_src_ready = (uint64_t)count;	/* source dries up after `count` */
				fx_src_give = 0;
				win = fx_win_alloc(window);
				RESET_AUDIT(win, window);
				ret = feed_format_feed(&f, NULL, win, window, NULL);
				checks++;
				/* every delivered ialign-byte frame funds exactly
				 * oalign output bytes; ret must not exceed
				 * given * oalign / ialign (+ reservoir tail of
				 * one ialign -> oalign is impossible here since
				 * source is dry). */
				uint64_t allowed = (fx_audit_given * (uint64_t)info->oalign) / info->ialign;
				if (ret > allowed)
					report("T3 ret-misround over-conversion", window, ret,
					    "return > delivered*(oalign/ialign)",
					    fx_audit_max_off, fx_audit_max_ask, chn, in, out);
				fx_win_free(win);
			}
			feed_format_free(&f);
		}
	}

	/* random soak */
	for (t = 0; t < 20000; t++) {
		prng ^= prng << 13; prng ^= prng >> 17; prng ^= prng << 5;
		ch = 2 + (prng % 7);
		in  = SND_FORMAT(AFMT_S16_LE, ch, 0);
		out = SND_FORMAT(AFMT_S32_LE, ch, 0);
		if (fmt_setup(in, out) != 0)
			continue;
		prng ^= prng << 13; prng ^= prng >> 17; prng ^= prng << 5;
		count = 4096 + (prng % 61441);
		fx_src_ready = 1 << 20;
		fx_src_give = 0;
		win = fx_win_alloc(count);
		RESET_AUDIT(win, count);
		ret = feed_format_feed(&f, NULL, win, count, NULL);
		where = NULL;
		checks++;
		if (fx_win_check(win, count, &where) != 0)
			report("soak", count, ret, where, fx_audit_max_off,
			    fx_audit_max_ask, ch, in, out);
		else if (ret > count)
			report("soak", count, ret, "return > count",
			    fx_audit_max_off, fx_audit_max_ask, ch, in, out);
		fx_win_free(win);
		feed_format_free(&f);
	}

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