DF-4846 / fxfmt.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | /* * 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); } |