From df1c9a5029aacecf0f274bf7c6448443aa125325 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 3 Jan 2023 07:01:36 +0300 Subject: [PATCH] engine: simplify blue shift swapped lump check, change TestBmodelLumps to avoid reading past mod buffer --- engine/common/con_utils.c | 16 ++-- engine/common/mod_bmodel.c | 152 +++++++++++++++++++------------------ engine/common/mod_local.h | 2 +- engine/server/sv_game.c | 14 ++-- 4 files changed, 95 insertions(+), 89 deletions(-) diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 2cd5ffc0..3aed173f 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -80,8 +80,9 @@ int Cmd_ListMaps( search_t *t, char *lastmapname, size_t len ) if( f ) { - dheader_t *header; + dheader_t *header; dextrahdr_t *hdrext; + dlump_t entities; memset( buf, 0, sizeof( buf )); FS_Read( f, buf, sizeof( buf )); @@ -89,10 +90,10 @@ int Cmd_ListMaps( search_t *t, char *lastmapname, size_t len ) ver = header->version; // check all the lumps and some other errors - if( Mod_TestBmodelLumps( t->filenames[i], buf, true )) + if( Mod_TestBmodelLumps( f, t->filenames[i], buf, true, &entities )) { - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; + lumpofs = entities.fileofs; + lumplen = entities.filelen; ver = header->version; } @@ -904,21 +905,22 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) { int num_spawnpoints = 0; dheader_t *header; + dlump_t entities; memset( buf, 0, MAX_SYSPATH ); FS_Read( f, buf, MAX_SYSPATH ); header = (dheader_t *)buf; // check all the lumps and some other errors - if( !Mod_TestBmodelLumps( t->filenames[i], buf, true )) + if( !Mod_TestBmodelLumps( f, t->filenames[i], buf, true, &entities )) { FS_Close( f ); continue; } // after call Mod_TestBmodelLumps we gurantee what map is valid - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; + lumpofs = entities.fileofs; + lumplen = entities.filelen; Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename )); COM_StripExtension( entfilename ); diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index af55ac07..a2abc27f 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -2738,33 +2738,14 @@ static void Mod_LoadLighting( dbspmodel_t *bmod ) /* ================= -Mod_LumpLooksLikePlanes +Mod_LumpLooksLikeEntities ================= */ -static qboolean Mod_LumpLooksLikePlanes( const byte *in, dlump_t *lump, qboolean fast ) +static int Mod_LumpLooksLikeEntities( const char *lump, const size_t lumplen ) { - int numplanes, i; - const dplane_t *planes; - - if( lump->filelen < sizeof( dplane_t ) && - lump->filelen % sizeof( dplane_t ) != 0 ) - return false; - - if( fast ) - return true; - - numplanes = lump->filelen / sizeof( dplane_t ); - planes = (const dplane_t*)(in + lump->fileofs); - - for( i = 0; i < numplanes; i++ ) - { - // planes can only be from 0 to 5: PLANE_X, Y, Z and PLANE_ANYX, Y and Z - if( planes[i].type < 0 || planes[i].type > 5 ) - return false; - } - - return true; + // look for "classname" string + return Q_memmem( lump, lumplen, "\"classname\"", sizeof( "\"classname\"" ) - 1 ) != NULL ? 1 : 0; } /* @@ -2800,44 +2781,30 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) #endif switch( header->version ) { - case Q1BSP_VERSION: case HLBSP_VERSION: - case QBSP2_VERSION: - break; - default: - Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", loadmodel->name, header->version, HLBSP_VERSION ); - loadstat.numerrors++; - return false; - } - - bmod->version = header->version; // share up global - if( isworld ) world.flags = 0; // clear world settings - bmod->isworld = isworld; - - if( header->version == HLBSP_VERSION ) - { // only relevant for half-life maps - if( !Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_PLANES], false ) && - Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_ENTITIES], false )) + if( !Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_ENTITIES].fileofs, header->lumps[LUMP_ENTITIES].filelen ) && + Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_PLANES].fileofs, header->lumps[LUMP_PLANES].filelen )) { // blue-shift swapped lumps srclumps[0].lumpnumber = LUMP_PLANES; srclumps[1].lumpnumber = LUMP_ENTITIES; + break; } - else - { - // everything else - srclumps[0].lumpnumber = LUMP_ENTITIES; - srclumps[1].lumpnumber = LUMP_PLANES; - } - } - else - { + // intended fallthrough + case Q1BSP_VERSION: + case QBSP2_VERSION: // everything else srclumps[0].lumpnumber = LUMP_ENTITIES; srclumps[1].lumpnumber = LUMP_PLANES; + break; + default: } + bmod->version = header->version; // share up global + if( isworld ) world.flags = 0; // clear world settings + bmod->isworld = isworld; + // loading base lumps for( i = 0; i < ARRAYSIZE( srclumps ); i++ ) Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], isworld ? (LUMP_SAVESTATS|LUMP_SILENT) : 0 ); @@ -2907,14 +2874,42 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) return true; } +static int Mod_LumpLooksLikeEntitiesFile( file_t *f, dlump_t *l, int flags, const char *msg ) +{ + char *buf; + int ret; + + if( FS_Seek( f, l->fileofs, SEEK_SET ) < 0 ) + { + if( !FBitSet( flags, LUMP_SILENT )) + Con_DPrintf( S_ERROR "map ^2%s^7 %s lump past end of file\n", loadstat.name, msg ); + return -1; + } + + buf = Z_Malloc( l->filelen + 1 ); + if( FS_Read( f, buf, l->filelen ) != l->filelen ) + { + if( !FBitSet( flags, LUMP_SILENT )) + Con_DPrintf( S_ERROR "can't read %s lump of map ^2%s^7", msg, loadstat.name ); + Z_Free( buf ); + return -1; + } + + ret = Mod_LumpLooksLikeEntities( buf, l->filelen ); + + Z_Free( buf ); + return ret; +} + /* ================= Mod_TestBmodelLumps check for possible errors +return real entities lump (for bshift swapped lumps) ================= */ -qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean silent ) +qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, qboolean silent, dlump_t *entities ) { dheader_t *header = (dheader_t *)mod_base; int i, flags = LUMP_TESTONLY; @@ -2934,11 +2929,42 @@ qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean s return false; } #endif + switch( header->version ) { - case Q1BSP_VERSION: case HLBSP_VERSION: + { + int ret; + + ret = Mod_LumpLooksLikeEntitiesFile( f, &header->lumps[LUMP_ENTITIES], flags, "entities" ); + if( ret < 0 ) + return false; + + if( !ret ) + { + ret = Mod_LumpLooksLikeEntitiesFile( f, &header->lumps[LUMP_PLANES], flags, "planes" ); + if( ret < 0 ) + return false; + + if( ret ) + { + // blue-shift swapped lumps + *entities = header->lumps[LUMP_PLANES]; + + srclumps[0].lumpnumber = LUMP_PLANES; + srclumps[1].lumpnumber = LUMP_ENTITIES; + break; + } + } + } + // intended fallthrough + case Q1BSP_VERSION: case QBSP2_VERSION: + // everything else + *entities = header->lumps[LUMP_ENTITIES]; + + srclumps[0].lumpnumber = LUMP_ENTITIES; + srclumps[1].lumpnumber = LUMP_PLANES; break; default: // don't early out: let me analyze errors @@ -2948,30 +2974,6 @@ qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean s break; } - if( header->version == HLBSP_VERSION ) - { - // only relevant for half-life maps - if( Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_ENTITIES], true ) && - !Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_PLANES], true )) - { - // blue-shift swapped lumps - srclumps[0].lumpnumber = LUMP_PLANES; - srclumps[1].lumpnumber = LUMP_ENTITIES; - } - else - { - // everything else - srclumps[0].lumpnumber = LUMP_ENTITIES; - srclumps[1].lumpnumber = LUMP_PLANES; - } - } - else - { - // everything else - srclumps[0].lumpnumber = LUMP_ENTITIES; - srclumps[1].lumpnumber = LUMP_PLANES; - } - // loading base lumps for( i = 0; i < ARRAYSIZE( srclumps ); i++ ) Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], flags ); diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index 5c7e579d..16c29531 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -148,7 +148,7 @@ void Mod_FreeUnused( void ); // mod_bmodel.c // void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *loaded ); -qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean silent ); +qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, qboolean silent, dlump_t *entities ); qboolean Mod_HeadnodeVisible( mnode_t *node, const byte *visbits, int *lastleaf ); int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis ); qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits ); diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 46e1b271..b439ae29 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -849,6 +849,7 @@ void SV_WriteEntityPatch( const char *filename ) byte buf[MAX_TOKEN]; // 1 kb string bspfilename; dheader_t *header; + dlump_t entities; file_t *f; Q_snprintf( bspfilename, sizeof( bspfilename ), "maps/%s.bsp", filename ); @@ -861,14 +862,14 @@ void SV_WriteEntityPatch( const char *filename ) header = (dheader_t *)buf; // check all the lumps and some other errors - if( !Mod_TestBmodelLumps( bspfilename, buf, true )) + if( !Mod_TestBmodelLumps( f, bspfilename, buf, true, &entities )) { FS_Close( f ); return; } - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; + lumpofs = entities.fileofs; + lumplen = entities.filelen; if( lumplen >= 10 ) { @@ -899,6 +900,7 @@ static char *SV_ReadEntityScript( const char *filename, int *flags ) byte buf[MAX_TOKEN]; char *ents = NULL; dheader_t *header; + dlump_t entities; size_t ft1, ft2; file_t *f; @@ -915,7 +917,7 @@ static char *SV_ReadEntityScript( const char *filename, int *flags ) header = (dheader_t *)buf; // check all the lumps and some other errors - if( !Mod_TestBmodelLumps( bspfilename, buf, (host_developer.value) ? false : true )) + if( !Mod_TestBmodelLumps( f, bspfilename, buf, (host_developer.value) ? false : true, &entities )) { SetBits( *flags, MAP_INVALID_VERSION ); FS_Close( f ); @@ -923,8 +925,8 @@ static char *SV_ReadEntityScript( const char *filename, int *flags ) } // after call Mod_TestBmodelLumps we gurantee what map is valid - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; + lumpofs = entities.fileofs; + lumplen = entities.filelen; // check for entfile too Q_snprintf( entfilename, sizeof( entfilename ), "maps/%s.ent", filename );