java-topology/defects/xonotic-0001/patch/xonotic-0001.patch
russell@unturf.com 806713a90c xonotic: 4 CWE-407 defects in DarkPlaces engine, MOAD 0002-0005 CLEAN
xonotic-0001: Mod_Mesh_GetTexture O(T) linear scan per draw call, 43x
  model_shared.c:4481 scans all textures for every quad/char/image drawn.
  Fix: hash table keyed on (name, drawflag, texflags, matflags).

xonotic-0002: SV_ModelIndex O(M) linear scan, 81x
  sv_main.c:1421 scans model_precache[] (up to 8192) on every model ref.
  Fix: hash table on model filename.

xonotic-0003: SV_SoundIndex O(S) linear scan, 66x
  sv_main.c:1484 scans sound_precache[] (up to 4096) on every sound ref.
  Fix: hash table on sound filename.

xonotic-0004: S_FindName O(N) linked list scan, 72x
  snd_main.c:913 traverses linked list (has "TODO: hash table search?").
  Fix: hash chain on sfx name.

MOAD-0002 (Intertangle): CLEAN, god objects are idiomatic Quake engine.
MOAD-0003 (Leaked Context): CLEAN, no thread_local usage.
MOAD-0004 (Logged Secret): CLEAN, rcon_password uses CF_PRIVATE flag.
MOAD-0005 (Thundering Herd): CLEAN, single-threaded cache access.
2026-03-31 12:43:16 -04:00

59 lines
2.6 KiB
Diff

--- a/model_shared.c
+++ b/model_shared.c
@@ -4473,12 +4473,36 @@ void Mod_Mesh_Restart(model_t *mod)
mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
}
+// Hash table for Mod_Mesh_GetTexture to avoid O(T) linear scan per draw call.
+// Key: texture name + drawflag + texflags + materialflags, Value: texture index.
+#define MOD_MESH_TEX_HASHSIZE 256
+static unsigned Mod_Mesh_TexHashKey(const char *name, int drawflag, int texflags, int materialflags)
+{
+ unsigned hash = 0;
+ const unsigned char *p;
+ for (p = (const unsigned char *)name; *p; p++)
+ hash = hash * 31 + *p;
+ hash ^= (unsigned)drawflag * 2654435761u;
+ hash ^= (unsigned)texflags * 2246822519u;
+ hash ^= (unsigned)materialflags * 3266489917u;
+ return hash % MOD_MESH_TEX_HASHSIZE;
+}
+
texture_t *Mod_Mesh_GetTexture(model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
{
- int i;
- texture_t *t;
+ int i, hashkey;
+ texture_t *t, *oldtextures;
int drawflag = defaultdrawflags & DRAWFLAG_MASK;
- for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
+
+ // Use hash table for O(1) average lookup instead of O(T) linear scan.
+ // This function is called per draw call (every quad, character, image),
+ // so O(T) was a per-frame bottleneck scaling with texture count.
+ hashkey = Mod_Mesh_TexHashKey(name, drawflag, defaulttexflags, defaultmaterialflags);
+ for (i = mod->mesh_texture_hashtable[hashkey]; i >= 0; i = mod->mesh_texture_hashnext[i])
+ {
+ t = &mod->data_textures[i];
+ if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
+ return t;
+ }
+
+ // Fallback: linear scan for textures not yet in hash table (should not happen)
+ for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
return t;
if (mod->max_textures <= mod->num_textures)
--- a/model_shared.h
+++ b/model_shared.h
@@ -428,6 +428,10 @@ typedef struct model_s
int max_textures; // preallocated for expansion (Mod_Mesh_GetTexture)
int num_textures;
texture_t *data_textures;
+ // Hash table for Mod_Mesh_GetTexture O(1) lookup.
+ // mesh_texture_hashtable[hashkey] = first texture index, -1 if empty.
+ int mesh_texture_hashtable[256];
+ int *mesh_texture_hashnext; // per-texture chain, -1 = end
int num_surfaces;
int max_surfaces; // preallocated for expansion (Mod_Mesh_AddSurface)
msurface_t *data_surfaces;