diff --git a/code/renderergl1/tr_animation.c b/code/renderergl1/tr_animation.c index b01cf813..2979ab9f 100644 --- a/code/renderergl1/tr_animation.c +++ b/code/renderergl1/tr_animation.c @@ -263,9 +263,9 @@ void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl1/tr_image.c b/code/renderergl1/tr_image.c index b131f366..a926dd82 100644 --- a/code/renderergl1/tr_image.c +++ b/code/renderergl1/tr_image.c @@ -1508,6 +1508,7 @@ RE_RegisterSkin =============== */ qhandle_t RE_RegisterSkin( const char *name ) { + skinSurface_t parseSurfaces[MAX_SKIN_SURFACES]; qhandle_t hSkin; skin_t *skin; skinSurface_t *surf; @@ -1557,8 +1558,8 @@ qhandle_t RE_RegisterSkin( const char *name ) { // If not a .skin file, load as a single shader if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); return hSkin; } @@ -1591,12 +1592,12 @@ qhandle_t RE_RegisterSkin( const char *name ) { // parse the shader name token = CommaParse( &text_p ); - if ( skin->numSurfaces >= MD3_MAX_SURFACES ) { - ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MD3_MAX_SURFACES ); + if ( skin->numSurfaces >= MAX_SKIN_SURFACES ) { + ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MAX_SKIN_SURFACES ); break; } - surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); + surf = &parseSurfaces[skin->numSurfaces]; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); skin->numSurfaces++; @@ -1610,6 +1611,10 @@ qhandle_t RE_RegisterSkin( const char *name ) { return 0; // use default skin } + // copy surfaces to skin + skin->surfaces = ri.Hunk_Alloc( skin->numSurfaces * sizeof( skinSurface_t ), h_low ); + memcpy( skin->surfaces, parseSurfaces, skin->numSurfaces * sizeof( skinSurface_t ) ); + return hSkin; } @@ -1628,8 +1633,8 @@ void R_InitSkins( void ) { skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = tr.defaultShader; + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = tr.defaultShader; } /* @@ -1658,10 +1663,10 @@ void R_SkinList_f( void ) { for ( i = 0 ; i < tr.numSkins ; i++ ) { skin = tr.skins[i]; - ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name ); + ri.Printf( PRINT_ALL, "%3i:%s (%d surfaces)\n", i, skin->name, skin->numSurfaces ); for ( j = 0 ; j < skin->numSurfaces ; j++ ) { ri.Printf( PRINT_ALL, " %s = %s\n", - skin->surfaces[j]->name, skin->surfaces[j]->shader->name ); + skin->surfaces[j].name, skin->surfaces[j].shader->name ); } } ri.Printf (PRINT_ALL, "------------------\n"); diff --git a/code/renderergl1/tr_local.h b/code/renderergl1/tr_local.h index aedf1a4f..37e0e070 100644 --- a/code/renderergl1/tr_local.h +++ b/code/renderergl1/tr_local.h @@ -411,6 +411,12 @@ typedef struct { //================================================================================= +// max surfaces per-skin +// This is an arbitry limit. Vanilla Q3 only supported 32 surfaces in skins but failed to +// enforce the maximum limit when reading skin files. It was possile to use more than 32 +// surfaces which accessed out of bounds memory past end of skin->surfaces hunk block. +#define MAX_SKIN_SURFACES 256 + // skins allow models to be retextured without modifying the model file typedef struct { char name[MAX_QPATH]; @@ -420,7 +426,7 @@ typedef struct { typedef struct skin_s { char name[MAX_QPATH]; // game path, including extension int numSurfaces; - skinSurface_t *surfaces[MD3_MAX_SURFACES]; + skinSurface_t *surfaces; // dynamically allocated array of surfaces } skin_t; diff --git a/code/renderergl1/tr_mesh.c b/code/renderergl1/tr_mesh.c index d0be29bc..4cdd9fc2 100644 --- a/code/renderergl1/tr_mesh.c +++ b/code/renderergl1/tr_mesh.c @@ -361,8 +361,8 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { shader = tr.defaultShader; for ( j = 0 ; j < skin->numSurfaces ; j++ ) { // the names have both been lowercased - if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) { - shader = skin->surfaces[j]->shader; + if ( !strcmp( skin->surfaces[j].name, surface->name ) ) { + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl1/tr_model_iqm.c b/code/renderergl1/tr_model_iqm.c index 6c47ed07..2cb5cd4f 100644 --- a/code/renderergl1/tr_model_iqm.c +++ b/code/renderergl1/tr_model_iqm.c @@ -903,9 +903,9 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl2/tr_animation.c b/code/renderergl2/tr_animation.c index 9acd6fc7..38fffc64 100644 --- a/code/renderergl2/tr_animation.c +++ b/code/renderergl2/tr_animation.c @@ -267,9 +267,9 @@ void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl2/tr_image.c b/code/renderergl2/tr_image.c index 0fa79612..7340377d 100644 --- a/code/renderergl2/tr_image.c +++ b/code/renderergl2/tr_image.c @@ -3060,6 +3060,7 @@ RE_RegisterSkin =============== */ qhandle_t RE_RegisterSkin( const char *name ) { + skinSurface_t parseSurfaces[MAX_SKIN_SURFACES]; qhandle_t hSkin; skin_t *skin; skinSurface_t *surf; @@ -3109,8 +3110,8 @@ qhandle_t RE_RegisterSkin( const char *name ) { // If not a .skin file, load as a single shader if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); return hSkin; } @@ -3143,12 +3144,12 @@ qhandle_t RE_RegisterSkin( const char *name ) { // parse the shader name token = CommaParse( &text_p ); - if ( skin->numSurfaces >= MD3_MAX_SURFACES ) { - ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MD3_MAX_SURFACES ); + if ( skin->numSurfaces >= MAX_SKIN_SURFACES ) { + ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MAX_SKIN_SURFACES ); break; } - surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); + surf = &parseSurfaces[skin->numSurfaces]; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); skin->numSurfaces++; @@ -3162,6 +3163,10 @@ qhandle_t RE_RegisterSkin( const char *name ) { return 0; // use default skin } + // copy surfaces to skin + skin->surfaces = ri.Hunk_Alloc( skin->numSurfaces * sizeof( skinSurface_t ), h_low ); + memcpy( skin->surfaces, parseSurfaces, skin->numSurfaces * sizeof( skinSurface_t ) ); + return hSkin; } @@ -3180,8 +3185,8 @@ void R_InitSkins( void ) { skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = tr.defaultShader; + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = tr.defaultShader; } /* @@ -3210,10 +3215,10 @@ void R_SkinList_f( void ) { for ( i = 0 ; i < tr.numSkins ; i++ ) { skin = tr.skins[i]; - ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name ); + ri.Printf( PRINT_ALL, "%3i:%s (%d surfaces)\n", i, skin->name, skin->numSurfaces ); for ( j = 0 ; j < skin->numSurfaces ; j++ ) { ri.Printf( PRINT_ALL, " %s = %s\n", - skin->surfaces[j]->name, skin->surfaces[j]->shader->name ); + skin->surfaces[j].name, skin->surfaces[j].shader->name ); } } ri.Printf (PRINT_ALL, "------------------\n"); diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index 1fc91f70..bdd5a859 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -766,6 +766,12 @@ typedef struct { //================================================================================= +// max surfaces per-skin +// This is an arbitry limit. Vanilla Q3 only supported 32 surfaces in skins but failed to +// enforce the maximum limit when reading skin files. It was possile to use more than 32 +// surfaces which accessed out of bounds memory past end of skin->surfaces hunk block. +#define MAX_SKIN_SURFACES 256 + // skins allow models to be retextured without modifying the model file typedef struct { char name[MAX_QPATH]; @@ -775,7 +781,7 @@ typedef struct { typedef struct skin_s { char name[MAX_QPATH]; // game path, including extension int numSurfaces; - skinSurface_t *surfaces[MD3_MAX_SURFACES]; + skinSurface_t *surfaces; // dynamically allocated array of surfaces } skin_t; diff --git a/code/renderergl2/tr_mesh.c b/code/renderergl2/tr_mesh.c index 5f7c4ee9..53bccb46 100644 --- a/code/renderergl2/tr_mesh.c +++ b/code/renderergl2/tr_mesh.c @@ -365,8 +365,8 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { shader = tr.defaultShader; for ( j = 0 ; j < skin->numSurfaces ; j++ ) { // the names have both been lowercased - if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) { - shader = skin->surfaces[j]->shader; + if ( !strcmp( skin->surfaces[j].name, surface->name ) ) { + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl2/tr_model_iqm.c b/code/renderergl2/tr_model_iqm.c index 67d3491c..caa4308b 100644 --- a/code/renderergl2/tr_model_iqm.c +++ b/code/renderergl2/tr_model_iqm.c @@ -907,9 +907,9 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } }