# UNDF: UNDF-2026-000001011 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -95,6 +95,28 @@ static void SV_SendSingleResource( const char *name, resourcetype_t type, int in /* ================ +CWE-407: SV_ModelIndex, SV_SoundIndex, SV_EventIndex, SV_GenericIndex + +Each precache-index function performs a linear scan of its precache array +to check for duplicates before registering a new entry. When a game mod +precaches N resources, each call scans up to N existing entries, producing +O(N^2/2) total string comparisons during map load. + +With MAX_MODELS=4096 and MAX_SOUNDS=2048, heavy mods hit millions of +Q_stricmp calls during level load. + +FIX: Maintain a parallel hash table for each precache array. On each call, +hash our normalized filename and probe our hash table for O(1) amortized +lookup. Insert into both our hash table and our precache array on miss. + +The hash table is reset alongside our precache array in SV_ClearServer(). +================ +*/ + +// Patch: add hash tables for O(1) precache dedup (one per resource type) +// Implementation would mirror our SV_BuildConsistencyHash pattern above. + +/* +================ SV_ModelIndex register unique model for a server and client @@ -113,6 +135,7 @@ int SV_ModelIndex( const char *filename ) Q_strncpy( name, filename, sizeof( name )); COM_FixSlashes( name ); + // DEFECT: O(N) linear scan, called N times = O(N^2/2) total for( i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++ ) { if( !Q_stricmp( sv.model_precache[i], name )) @@ -164,6 +187,7 @@ int GAME_EXPORT SV_SoundIndex( const char *filename ) Q_strncpy( name, filename, sizeof( name )); COM_FixSlashes( name ); + // DEFECT: O(N) linear scan, called N times = O(N^2/2) total for( i = 1; i < MAX_SOUNDS && sv.sound_precache[i][0]; i++ ) { if( !Q_stricmp( sv.sound_precache[i], name )) @@ -207,6 +231,7 @@ int SV_EventIndex( const char *filename ) Q_strncpy( name, filename, sizeof( name )); COM_FixSlashes( name ); + // DEFECT: O(N) linear scan, called N times = O(N^2/2) total for( i = 1; i < MAX_EVENTS && sv.event_precache[i][0]; i++ ) { if( !Q_stricmp( sv.event_precache[i], name )) @@ -249,6 +274,7 @@ int GAME_EXPORT SV_GenericIndex( const char *filename ) Q_strncpy( name, filename, sizeof( name )); COM_FixSlashes( name ); + // DEFECT: O(N) linear scan, called N times = O(N^2/2) total for( i = 1; i < MAX_CUSTOM && sv.files_precache[i][0]; i++ ) { if( !Q_stricmp( sv.files_precache[i], name ))