mednafen: 1 CWE-407 defect, MOAD 0002-0005 CLEAN

This commit is contained in:
russell@unturf.com 2026-03-31 17:38:19 -04:00
parent 0031b1296d
commit 670ef7b771
3 changed files with 300 additions and 0 deletions

View file

@ -0,0 +1,58 @@
# UNDF: UNDF-2026-000001042
--- a/mednafen/mempatcher.cpp
+++ b/mednafen/mempatcher.cpp
@@ -476,15 +476,55 @@ bool MDFNI_DecodeGBGG(const char *instr, uint32 *a, uint8 *v, uint8 *c, char *t
}
-static int GGtobin(char c)
+/*
+ * GGtobin: map a Game Genie nibble character to its 4-bit value.
+ *
+ * Original: O(16) linear scan over a 16-char lookup string, called 8 times
+ * per code decode (MDFNI_DecodeGG) -- up to 128 comparisons per 8-char
+ * Game Genie code. The character set is fixed and known at compile time.
+ *
+ * Fix: replace with a 256-byte direct-index LUT initialised at compile time.
+ * Every GGtobin() call becomes a single array access O(1), reducing decode
+ * cost from O(16*8) = 128 comparisons to 8 array reads.
+ *
+ * Game Genie NES alphabet: A=0 P=1 Z=2 L=3 G=4 I=5 T=6 Y=7
+ * E=8 O=9 X=10 U=11 K=12 S=13 V=14 N=15
+ *
+ * Complexity before: O(16) per character, O(128) per 8-char code
+ * Complexity after: O(1) per character, O(8) per 8-char code
+ * Speedup: 16x per character lookup
+ */
+#define _ (-1)
+static const signed char GGLut[256] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+/* 00 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* 10 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* 20 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* 30 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* 40 */ _, 0, _, _, _, 8, _, 4, _, 5, _, 12, 3, _, 15, 9,
+/* 50 */ 1, _, _, 13, 6, 11, 14, _, 10, 7, 2, _, _, _, _, _,
+/* 60 */ _, 0, _, _, _, 8, _, 4, _, 5, _, 12, 3, _, 15, 9,
+/* 70 */ 1, _, _, 13, 6, 11, 14, _, 10, 7, 2, _, _, _, _, _,
+/* 80 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* 90 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* A0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* B0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* C0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* D0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* E0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+/* F0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+};
+#undef _
+
+static int GGtobin(char c)
{
- static char lets[16]={'A','P','Z','L','G','I','T','Y','E','O','X','U','K','S','V','N'};
- int x;
-
- for(x=0;x<16;x++)
- if(lets[x] == toupper(c)) return(x);
- return(0);
+ int v = (int)GGLut[(unsigned char)c];
+ return (v < 0) ? 0 : v;
}

Binary file not shown.

View file

@ -0,0 +1,242 @@
/*
* test_mednafen_0001.c
*
* Unit test for mednafen-0001: GGtobin() O(16) linear scan over a 16-char
* Game Genie alphabet replaced with a 256-byte direct-index LUT for O(1).
*
* Validates that the LUT produces identical results to the original linear scan
* for all 256 input bytes, and measures the iteration reduction.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
/* ---- original O(16) implementation ---- */
static int GGtobin_linear(char c)
{
static char lets[16] = {'A','P','Z','L','G','I','T','Y','E','O','X','U','K','S','V','N'};
int x;
for (x = 0; x < 16; x++)
if (lets[x] == toupper((unsigned char)c)) return x;
return 0;
}
/* Count actual iterations for one character */
static int GGtobin_linear_iters(char c)
{
static char lets[16] = {'A','P','Z','L','G','I','T','Y','E','O','X','U','K','S','V','N'};
int x;
for (x = 0; x < 16; x++) {
if (lets[x] == toupper((unsigned char)c)) return x + 1;
}
return 16; /* scanned all, fell through to return 0 */
}
/* ---- O(1) LUT implementation ---- */
/*
* Game Genie NES alphabet: A=0 P=1 Z=2 L=3 G=4 I=5 T=6 Y=7
* E=8 O=9 X=10 U=11 K=12 S=13 V=14 N=15
* Generated from: static char lets[16]={'A','P','Z','L','G','I','T','Y','E','O','X','U','K','S','V','N'};
* LUT indexed by ASCII byte value; -1 = not in alphabet.
*/
#define _ (-1)
static const signed char GGLut[256] = {
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 00 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* 10 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* 20 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* 30 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* 40 */ _, 0, _, _, _, 8, _, 4, _, 5, _, 12, 3, _, 15, 9,
/* 50 */ 1, _, _, 13, 6, 11, 14, _, 10, 7, 2, _, _, _, _, _,
/* 60 */ _, 0, _, _, _, 8, _, 4, _, 5, _, 12, 3, _, 15, 9,
/* 70 */ 1, _, _, 13, 6, 11, 14, _, 10, 7, 2, _, _, _, _, _,
/* 80 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* 90 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* A0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* B0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* C0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* D0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* E0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
/* F0 */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
};
#undef _
static int GGtobin_lut(char c)
{
int v = (int)GGLut[(unsigned char)c];
return (v < 0) ? 0 : v; /* preserve original return-0 on unknown */
}
/* ---- Game Genie decode stub to verify end-to-end ---- */
static int decode_gg_6(const char *str, int use_lut, uint16_t *a_out, uint8_t *v_out)
{
int (*fn)(char) = use_lut ? GGtobin_lut : GGtobin_linear;
uint16_t A = 0x8000;
uint8_t V = 0;
uint8_t t;
if (strlen(str) != 6) return 0;
t = fn(*str++); V |= (t & 0x07); V |= (t & 0x08) << 4;
t = fn(*str++); V |= (t & 0x07) << 4; A |= (t & 0x08) << 4;
t = fn(*str++); A |= (t & 0x07) << 4;
t = fn(*str++); A |= (t & 0x07) << 12; A |= (t & 0x08);
t = fn(*str++); A |= (t & 0x07); A |= (t & 0x08) << 8;
t = fn(*str++); A |= (t & 0x07) << 8; V |= (t & 0x08);
*a_out = A;
*v_out = V;
return 1;
}
int main(void)
{
int i, tests = 0, pass = 0;
/* Test 1: LUT matches linear scan for all 16 valid chars (uppercase) */
printf("Test 1: LUT matches linear for all 16 valid uppercase chars\n");
{
static const char valid[] = "APZLGITYEOXUKSVN";
for (i = 0; i < 16; i++) {
int lin = GGtobin_linear(valid[i]);
int lut = GGtobin_lut(valid[i]);
tests++;
if (lin == lut) pass++;
else printf(" FAIL char='%c' linear=%d lut=%d\n", valid[i], lin, lut);
}
printf(" %d/%d\n", pass, tests);
}
/* Test 2: LUT matches linear scan for all 16 valid chars (lowercase) */
printf("Test 2: LUT matches linear for all 16 valid lowercase chars\n");
{
static const char valid_lc[] = "apzlgityeoxuksvn";
for (i = 0; i < 16; i++) {
int lin = GGtobin_linear(valid_lc[i]);
int lut = GGtobin_lut(valid_lc[i]);
tests++;
if (lin == lut) pass++;
else printf(" FAIL char='%c' linear=%d lut=%d\n", valid_lc[i], lin, lut);
}
printf(" %d/%d\n", pass, tests);
}
/* Test 3: correct nibble values for known chars */
printf("Test 3: correct nibble values for known chars\n");
{
struct { char c; int expected; } cases[] = {
{'A', 0}, {'P', 1}, {'Z', 2}, {'L', 3},
{'G', 4}, {'I', 5}, {'T', 6}, {'Y', 7},
{'E', 8}, {'O', 9}, {'X',10}, {'U',11},
{'K',12}, {'S',13}, {'V',14}, {'N',15},
};
for (i = 0; i < 16; i++) {
tests++;
int v = GGtobin_lut(cases[i].c);
if (v == cases[i].expected) pass++;
else printf(" FAIL '%c': expected %d got %d\n",
cases[i].c, cases[i].expected, v);
}
printf(" %d/%d\n", pass, tests);
}
/* Test 4: end-to-end 6-char decode identical between linear and LUT */
printf("Test 4: end-to-end 6-char decode identical\n");
{
/* Synthetic codes exercising different nibble positions */
static const char *codes[] = {
"SXIOPO", /* uses S=13, X=10, I=5, O=9, P=1, O=9 */
"YEAAAA", /* Y=7, E=8, A=0, A=0, A=0, A=0 */
"APZLGN", /* all first 5 of alphabet + N */
"TYVUKS", /* T=6, Y=7, V=14, U=11, K=12, S=13 */
"NNNNNN", /* all N=15 */
};
int ncodes = sizeof(codes) / sizeof(codes[0]);
for (i = 0; i < ncodes; i++) {
uint16_t a_lin, a_lut;
uint8_t v_lin, v_lut;
decode_gg_6(codes[i], 0, &a_lin, &v_lin);
decode_gg_6(codes[i], 1, &a_lut, &v_lut);
tests++;
if (a_lin == a_lut && v_lin == v_lut) {
pass++;
printf(" PASS code=%-8s addr=0x%04x val=0x%02x\n",
codes[i], a_lut, v_lut);
} else {
printf(" FAIL code=%-8s lin=0x%04x/0x%02x lut=0x%04x/0x%02x\n",
codes[i], a_lin, v_lin, a_lut, v_lut);
}
}
}
/* Test 5: iteration reduction -- O(16) vs O(1) */
printf("Test 5: iteration count reduction\n");
{
/* 'N' is last in alphabet: 16 iterations */
int iter_last = GGtobin_linear_iters('N');
int iter_first = GGtobin_linear_iters('A');
int lut_ops = 1;
printf(" linear 'A' (first): %d iters\n", iter_first);
printf(" linear 'N' (last): %d iters\n", iter_last);
printf(" LUT any char: %d op\n", lut_ops);
printf(" worst-case speedup: %dx\n", iter_last / lut_ops);
int linear_code_worst = 16 * 8;
int lut_code_cost = 8;
printf(" per 8-char code: linear worst=%d comps, LUT=%d ops, speedup=%dx\n",
linear_code_worst, lut_code_cost,
linear_code_worst / lut_code_cost);
assert(iter_first == 1);
assert(iter_last == 16);
tests++; pass++;
}
/* Test 6: invalid characters handled consistently */
printf("Test 6: invalid characters\n");
{
char invalids[] = { 'B', 'C', 'D', 'F', 'H', 'J', 'M', 'Q', 'R', 'W', '0', '1', ' ' };
int n = (int)sizeof(invalids);
int all_match = 1;
for (i = 0; i < n; i++) {
int lin = GGtobin_linear(invalids[i]);
int lut = GGtobin_lut(invalids[i]);
if (lin != lut) {
all_match = 0;
printf(" FAIL char=0x%02x lin=%d lut=%d\n",
(unsigned char)invalids[i], lin, lut);
}
}
tests++;
if (all_match) { pass++; printf(" PASS: all invalid chars match (both return 0)\n"); }
}
/* Test 7: LUT matches linear for all 256 byte values */
printf("Test 7: full 256-byte equivalence\n");
{
int mismatches = 0;
for (i = 0; i < 256; i++) {
int lin = GGtobin_linear((char)i);
int lut = GGtobin_lut((char)i);
if (lin != lut) {
mismatches++;
if (mismatches <= 5)
printf(" MISMATCH byte=0x%02x lin=%d lut=%d\n", i, lin, lut);
}
}
tests++;
if (mismatches == 0) { pass++; printf(" PASS: all 256 bytes match\n"); }
else printf(" FAIL: %d mismatches\n", mismatches);
}
printf("\n%d/%d tests passed\n", pass, tests);
if (pass != tests) { printf("FAIL\n"); return 1; }
printf("PASS: O(1) LUT replaces O(16) linear scan in GGtobin()\n");
return 0;
}