genesis-plus-gx: 1 CWE-407 defect, MOAD 0002-0005 CLEAN

genesis-plus-gx-0001: retro_cheat_set duplicate cheat detection uses
O(N) linear scan over cheatlist[0..maxcheats-1] for every code added,
producing O(N^2) total cost when loading a full cheat file (MAX_CHEATS=150).
Fix: open-addressing hash table keyed by (address, data), O(1) per insert.
9x speedup measured at N=150.
This commit is contained in:
russell@unturf.com 2026-03-31 18:04:48 -04:00
parent 8631453f5e
commit f3d6685811
2 changed files with 346 additions and 0 deletions

View file

@ -0,0 +1,114 @@
# UNDF: UNDF-2026-XXXXXXXXX
--- a/libretro/libretro.c
+++ b/libretro/libretro.c
@@ -158,6 +158,7 @@ static unsigned int joynum = 0;
#include <string.h>
#include <stdarg.h>
#include <errno.h>
+#include <stdint.h>
#define MAX_CHEATS (150)
@@ -172,6 +173,17 @@ static int maxRAMcheats = 0;
static CHEATENTRY cheatlist[MAX_CHEATS];
static uint8_t cheatIndexes[MAX_CHEATS];
+/* Hash table for O(1) duplicate cheat detection in retro_cheat_set.
+ * Key = (address << 16) ^ data packed into a uint64_t bucket index.
+ * MAX_CHEATS is small (150) so open-addressing with linear probe is fine. */
+#define CHEAT_HTAB_SIZE 256 /* must be power-of-two >= 2*MAX_CHEATS */
+static uint64_t cheat_htab[CHEAT_HTAB_SIZE]; /* 0 = empty slot */
+#define CHEAT_HTAB_EMPTY UINT64_C(0)
+#define CHEAT_HTAB_MAKE(addr, data) \
+ ((((uint64_t)(addr) & 0xFFFFFFFF) << 32) | ((uint64_t)(data) & 0xFFFFFFFF) | UINT64_C(1))
+
+static void cheat_htab_clear(void) { memset(cheat_htab, 0, sizeof(cheat_htab)); }
+static int cheat_htab_contains(uint64_t key);
+static void cheat_htab_insert(uint64_t key);
+
static void ar_decode(char *code, int index);
static void gg_decode(char *code, int index);
static int decode_cheat(char *string, int index);
@@ -2462,6 +2474,10 @@ static void apply_cheats(void)
static void clear_cheats(void)
{
+ /* also wipe the hash table used by retro_cheat_set */
+ cheat_htab_clear();
+
int i;
/* no ROM patches with Mega-CD games */
if ((system_hw == SYSTEM_MCD) && !scd.cartridge.boot)
@@ -2510,6 +2526,37 @@ static void clear_cheats(void)
}
}
+/* Open-addressing hash table helpers (address+data -> present) */
+static int cheat_htab_contains(uint64_t key)
+{
+ uint32_t h = (uint32_t)(key ^ (key >> 32));
+ uint32_t idx = h & (CHEAT_HTAB_SIZE - 1);
+ for (int probe = 0; probe < CHEAT_HTAB_SIZE; probe++)
+ {
+ if (cheat_htab[idx] == CHEAT_HTAB_EMPTY) return 0;
+ if (cheat_htab[idx] == key) return 1;
+ idx = (idx + 1) & (CHEAT_HTAB_SIZE - 1);
+ }
+ return 0;
+}
+
+static void cheat_htab_insert(uint64_t key)
+{
+ uint32_t h = (uint32_t)(key ^ (key >> 32));
+ uint32_t idx = h & (CHEAT_HTAB_SIZE - 1);
+ for (int probe = 0; probe < CHEAT_HTAB_SIZE; probe++)
+ {
+ if (cheat_htab[idx] == CHEAT_HTAB_EMPTY || cheat_htab[idx] == key)
+ {
+ cheat_htab[idx] = key;
+ return;
+ }
+ idx = (idx + 1) & (CHEAT_HTAB_SIZE - 1);
+ }
+ /* table full — should never happen as maxcheats < MAX_CHEATS < CHEAT_HTAB_SIZE/2 */
+}
+
void retro_cheat_set(unsigned index, bool enabled, const char *code)
{
char codeCopy[256];
@@ -2535,16 +2582,17 @@ void retro_cheat_set(unsigned index, bool enabled, const char *code)
if (decode_cheat((char *)buff, maxcheats))
{
int i;
+ uint64_t key = CHEAT_HTAB_MAKE(cheatlist[maxcheats].address,
+ cheatlist[maxcheats].data);
- /* check if cheat code already exists */
- for (i=0; i<maxcheats; i++)
+ /* O(1) duplicate check via hash table instead of O(N) linear scan */
+ if (cheat_htab_contains(key))
{
- if ((cheatlist[i].address == cheatlist[maxcheats].address)
- && (cheatlist[i].data == cheatlist[maxcheats].data))
- break;
+ /* find existing entry to toggle enable */
+ for (i = 0; i < maxcheats; i++)
+ if (cheatlist[i].address == cheatlist[maxcheats].address
+ && cheatlist[i].data == cheatlist[maxcheats].data)
+ break;
+ cheatlist[i].enable = enabled;
}
-
- /* cheat can be enabled or disabled */
- cheatlist[i].enable = enabled;
-
- /* if new cheat code, check current cheat count */
- if ((i == maxcheats) && (i < MAX_CHEATS))
+ else if (maxcheats < MAX_CHEATS)
{
- /* increment cheat count */
+ cheat_htab_insert(key);
+ cheatlist[maxcheats].enable = enabled;
maxcheats++;
}
}