Browse Source

engine: simplify blue shift swapped lump check, change TestBmodelLumps to avoid reading past mod buffer

pull/2/head
Alibek Omarov 2 years ago
parent
commit
df1c9a5029
  1. 16
      engine/common/con_utils.c
  2. 152
      engine/common/mod_bmodel.c
  3. 2
      engine/common/mod_local.h
  4. 14
      engine/server/sv_game.c

16
engine/common/con_utils.c

@ -80,8 +80,9 @@ int Cmd_ListMaps( search_t *t, char *lastmapname, size_t len )
if( f ) if( f )
{ {
dheader_t *header; dheader_t *header;
dextrahdr_t *hdrext; dextrahdr_t *hdrext;
dlump_t entities;
memset( buf, 0, sizeof( buf )); memset( buf, 0, sizeof( buf ));
FS_Read( f, buf, 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; ver = header->version;
// check all the lumps and some other errors // 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; lumpofs = entities.fileofs;
lumplen = header->lumps[LUMP_ENTITIES].filelen; lumplen = entities.filelen;
ver = header->version; ver = header->version;
} }
@ -904,21 +905,22 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
{ {
int num_spawnpoints = 0; int num_spawnpoints = 0;
dheader_t *header; dheader_t *header;
dlump_t entities;
memset( buf, 0, MAX_SYSPATH ); memset( buf, 0, MAX_SYSPATH );
FS_Read( f, buf, MAX_SYSPATH ); FS_Read( f, buf, MAX_SYSPATH );
header = (dheader_t *)buf; header = (dheader_t *)buf;
// check all the lumps and some other errors // 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 ); FS_Close( f );
continue; continue;
} }
// after call Mod_TestBmodelLumps we gurantee what map is valid // after call Mod_TestBmodelLumps we gurantee what map is valid
lumpofs = header->lumps[LUMP_ENTITIES].fileofs; lumpofs = entities.fileofs;
lumplen = header->lumps[LUMP_ENTITIES].filelen; lumplen = entities.filelen;
Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename )); Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename ));
COM_StripExtension( entfilename ); COM_StripExtension( entfilename );

152
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; // look for "classname" string
const dplane_t *planes; return Q_memmem( lump, lumplen, "\"classname\"", sizeof( "\"classname\"" ) - 1 ) != NULL ? 1 : 0;
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;
} }
/* /*
@ -2800,44 +2781,30 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld )
#endif #endif
switch( header->version ) switch( header->version )
{ {
case Q1BSP_VERSION:
case HLBSP_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 // only relevant for half-life maps
if( !Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_PLANES], false ) && if( !Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_ENTITIES].fileofs, header->lumps[LUMP_ENTITIES].filelen ) &&
Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_ENTITIES], false )) Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_PLANES].fileofs, header->lumps[LUMP_PLANES].filelen ))
{ {
// blue-shift swapped lumps // blue-shift swapped lumps
srclumps[0].lumpnumber = LUMP_PLANES; srclumps[0].lumpnumber = LUMP_PLANES;
srclumps[1].lumpnumber = LUMP_ENTITIES; srclumps[1].lumpnumber = LUMP_ENTITIES;
break;
} }
else // intended fallthrough
{ case Q1BSP_VERSION:
// everything else case QBSP2_VERSION:
srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES;
}
}
else
{
// everything else // everything else
srclumps[0].lumpnumber = LUMP_ENTITIES; srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES; 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 // loading base lumps
for( i = 0; i < ARRAYSIZE( srclumps ); i++ ) for( i = 0; i < ARRAYSIZE( srclumps ); i++ )
Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], isworld ? (LUMP_SAVESTATS|LUMP_SILENT) : 0 ); 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; 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 Mod_TestBmodelLumps
check for possible errors 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; dheader_t *header = (dheader_t *)mod_base;
int i, flags = LUMP_TESTONLY; int i, flags = LUMP_TESTONLY;
@ -2934,11 +2929,42 @@ qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean s
return false; return false;
} }
#endif #endif
switch( header->version ) switch( header->version )
{ {
case Q1BSP_VERSION:
case HLBSP_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: case QBSP2_VERSION:
// everything else
*entities = header->lumps[LUMP_ENTITIES];
srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES;
break; break;
default: default:
// don't early out: let me analyze errors // 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; 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 // loading base lumps
for( i = 0; i < ARRAYSIZE( srclumps ); i++ ) for( i = 0; i < ARRAYSIZE( srclumps ); i++ )
Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], flags ); Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], flags );

2
engine/common/mod_local.h

@ -148,7 +148,7 @@ void Mod_FreeUnused( void );
// mod_bmodel.c // mod_bmodel.c
// //
void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *loaded ); 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 ); 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 ); 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 ); qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits );

14
engine/server/sv_game.c

@ -849,6 +849,7 @@ void SV_WriteEntityPatch( const char *filename )
byte buf[MAX_TOKEN]; // 1 kb byte buf[MAX_TOKEN]; // 1 kb
string bspfilename; string bspfilename;
dheader_t *header; dheader_t *header;
dlump_t entities;
file_t *f; file_t *f;
Q_snprintf( bspfilename, sizeof( bspfilename ), "maps/%s.bsp", filename ); Q_snprintf( bspfilename, sizeof( bspfilename ), "maps/%s.bsp", filename );
@ -861,14 +862,14 @@ void SV_WriteEntityPatch( const char *filename )
header = (dheader_t *)buf; header = (dheader_t *)buf;
// check all the lumps and some other errors // 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 ); FS_Close( f );
return; return;
} }
lumpofs = header->lumps[LUMP_ENTITIES].fileofs; lumpofs = entities.fileofs;
lumplen = header->lumps[LUMP_ENTITIES].filelen; lumplen = entities.filelen;
if( lumplen >= 10 ) if( lumplen >= 10 )
{ {
@ -899,6 +900,7 @@ static char *SV_ReadEntityScript( const char *filename, int *flags )
byte buf[MAX_TOKEN]; byte buf[MAX_TOKEN];
char *ents = NULL; char *ents = NULL;
dheader_t *header; dheader_t *header;
dlump_t entities;
size_t ft1, ft2; size_t ft1, ft2;
file_t *f; file_t *f;
@ -915,7 +917,7 @@ static char *SV_ReadEntityScript( const char *filename, int *flags )
header = (dheader_t *)buf; header = (dheader_t *)buf;
// check all the lumps and some other errors // 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 ); SetBits( *flags, MAP_INVALID_VERSION );
FS_Close( f ); 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 // after call Mod_TestBmodelLumps we gurantee what map is valid
lumpofs = header->lumps[LUMP_ENTITIES].fileofs; lumpofs = entities.fileofs;
lumplen = header->lumps[LUMP_ENTITIES].filelen; lumplen = entities.filelen;
// check for entfile too // check for entfile too
Q_snprintf( entfilename, sizeof( entfilename ), "maps/%s.ent", filename ); Q_snprintf( entfilename, sizeof( entfilename ), "maps/%s.ent", filename );

Loading…
Cancel
Save