From 670ef7b77137a5ebe8b3a2effd5dddaf319587bf Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Tue, 31 Mar 2026 17:38:19 -0400 Subject: [PATCH] mednafen: 1 CWE-407 defect, MOAD 0002-0005 CLEAN --- .../mednafen-0001/patch/mednafen-0001.patch | 58 +++++ defects/mednafen-0001/test/test_mednafen_0001 | Bin 0 -> 16696 bytes .../mednafen-0001/test/test_mednafen_0001.c | 242 ++++++++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 defects/mednafen-0001/patch/mednafen-0001.patch create mode 100755 defects/mednafen-0001/test/test_mednafen_0001 create mode 100644 defects/mednafen-0001/test/test_mednafen_0001.c diff --git a/defects/mednafen-0001/patch/mednafen-0001.patch b/defects/mednafen-0001/patch/mednafen-0001.patch new file mode 100644 index 000000000..ae07d3d47 --- /dev/null +++ b/defects/mednafen-0001/patch/mednafen-0001.patch @@ -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; + } diff --git a/defects/mednafen-0001/test/test_mednafen_0001 b/defects/mednafen-0001/test/test_mednafen_0001 new file mode 100755 index 0000000000000000000000000000000000000000..bd509fc1719c995bedd51422fd3500930b97f5ca GIT binary patch literal 16696 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 +#include +#include +#include +#include + +/* ---- 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; +}