Bug: The fread_mobile function is unsafe and also has leaks
Danger: High - Minor memory leaks + infinite loop potential in corrupt pfiles
Discovered in: AFKMud 1.77
Found by: Remcon
Fixed by: Remcon
---
save.c, fread_mobile
Replace the entire function with this:
/*
* This will read one mobile structure pointer to by fp --Shaddai
* Edited by Tarl 5 May 2002 to allow pets to load equipment.
*/
CHAR_DATA *fread_mobile( FILE * fp, bool shopmob )
{
CHAR_DATA *mob = NULL;
const char *word;
bool fMatch;
int inroom = 0, i, x;
ROOM_INDEX_DATA *pRoomIndex = NULL;
MOB_INDEX_DATA *pMobIndex = NULL;
if( !shopmob )
word = feof( fp ) ? "EndMobile" : fread_word( fp );
else
word = feof( fp ) ? "EndVendor" : fread_word( fp );
if( word[0] == '\0' )
{
bug( "%s: EOF encountered reading file!", __FUNCTION__ );
if( !shopmob )
word = "EndMobile";
else
word = "EndVendor";
}
if( !str_cmp( word, "Vnum" ) )
{
int vnum;
vnum = fread_number( fp );
pMobIndex = get_mob_index( vnum );
if( !pMobIndex )
{
for( ;; )
{
if( !shopmob )
word = feof( fp ) ? "EndMobile" : fread_word( fp );
else
word = feof( fp ) ? "EndVendor" : fread_word( fp );
if( word[0] == '\0' )
{
bug( "%s: EOF encountered reading file!", __FUNCTION__ );
if( !shopmob )
word = "EndMobile";
else
word = "EndVendor";
}
/*
* So we don't get so many bug messages when something messes up
* * --Shaddai
*/
if( !str_cmp( word, "EndMobile" ) || !str_cmp( word, "EndVendor" ) )
break;
}
bug( "%s: No index data for vnum %d", __FUNCTION__, vnum );
return NULL;
}
mob = create_mobile( pMobIndex );
}
else
{
for( ;; )
{
if( !shopmob )
word = feof( fp ) ? "EndMobile" : fread_word( fp );
else
word = feof( fp ) ? "EndVendor" : fread_word( fp );
if( word[0] == '\0' )
{
bug( "%s: EOF encountered reading file!", __FUNCTION__ );
if( !shopmob )
word = "EndMobile";
else
word = "EndVendor";
}
/*
* So we don't get so many bug messages when something messes up
* * --Shaddai
*/
if( !str_cmp( word, "EndMobile" ) || !str_cmp( word, "EndVendor" ) )
break;
}
extract_char( mob, TRUE );
bug( "%s: Vnum not found", __FUNCTION__ );
return NULL;
}
for( ;; )
{
if( !shopmob )
word = feof( fp ) ? "EndMobile" : fread_word( fp );
else
word = feof( fp ) ? "EndVendor" : fread_word( fp );
if( word[0] == '\0' )
{
bug( "%s: EOF encountered reading file!", __FUNCTION__ );
if( !shopmob )
word = "EndMobile";
else
word = "EndVendor";
}
fMatch = FALSE;
switch ( UPPER( word[0] ) )
{
case '*':
fMatch = TRUE;
fread_to_eol( fp );
break;
case '#':
if( !str_cmp( word, "#OBJECT" ) )
{
fread_obj( mob, fp, OS_CARRY );
fMatch = true;
break;
}
break;
case 'A':
if( !str_cmp( word, "Affect" ) || !str_cmp( word, "AffectData" ) )
{
AFFECT_DATA *paf;
CREATE( paf, AFFECT_DATA, 1 );
if( !str_cmp( word, "Affect" ) )
paf->type = fread_number( fp );
else
{
int sn;
char *sname = fread_word( fp );
if( ( sn = skill_lookup( sname ) ) < 0 )
{
if( ( sn = herb_lookup( sname ) ) < 0 )
bug( "%s", "Fread_mobile: unknown skill." );
else
sn += TYPE_HERB;
}
paf->type = sn;
}
paf->duration = fread_number( fp );
paf->modifier = fread_number( fp );
paf->location = fread_number( fp );
if( paf->location == APPLY_WEAPONSPELL
|| paf->location == APPLY_WEARSPELL
|| paf->location == APPLY_REMOVESPELL
|| paf->location == APPLY_STRIPSN || paf->location == APPLY_RECURRINGSPELL )
paf->modifier = slot_lookup( paf->modifier );
paf->bit = fread_number( fp );
LINK( paf, mob->first_affect, mob->last_affect, next, prev );
fMatch = true;
break;
}
KEY( "AffectedBy", mob->affected_by, fread_bitvector( fp ) );
break;
case 'C':
if( !str_cmp( word, "Coordinates" ) )
{
mob->x = fread_number( fp );
mob->y = fread_number( fp );
mob->map = fread_number( fp );
fMatch = TRUE;
break;
}
break;
case 'D':
if( !str_cmp( word, "Description" ) )
{
STRFREE( mob->chardesc );
mob->chardesc = fread_string( fp );
fMatch = TRUE;
break;
}
break;
case 'E':
if( !str_cmp( word, "EndMobile" ) || !str_cmp( word, "EndVendor" ) )
{
if( inroom == 0 )
inroom = ROOM_VNUM_TEMPLE;
pRoomIndex = get_room_index( inroom );
if( !pRoomIndex )
pRoomIndex = get_room_index( ROOM_VNUM_LIMBO );
if( !char_to_room( mob, pRoomIndex ) )
log_printf( "char_to_room: %s:%s, line %d.", __FILE__, __FUNCTION__, __LINE__ );
for( i = 0; i < MAX_WEAR; i++ )
for( x = 0; x < MAX_LAYERS; x++ )
if( mob_save_equipment[i][x] )
{
equip_char( mob, mob_save_equipment[i][x], i );
mob_save_equipment[i][x] = NULL;
}
return mob;
}
if( !str_cmp( word, "Exp" ) )
{
mob->exp = fread_number( fp );
fMatch = TRUE;
break;
}
break;
case 'F':
KEY( "Flags", mob->act, fread_bitvector( fp ) );
break;
case 'H':
if( !str_cmp( word, "HpManaMove" ) )
{
mob->hit = fread_number( fp );
mob->max_hit = fread_number( fp );
mob->mana = fread_number( fp );
mob->max_mana = fread_number( fp );
mob->move = fread_number( fp );
mob->max_move = fread_number( fp );
fMatch = TRUE;
break;
}
break;
case 'L':
KEY( "Level", mob->level, fread_number( fp ) );
if( !str_cmp( word, "Long" ) )
{
STRFREE( mob->long_descr );
mob->long_descr = fread_string( fp );
fMatch = TRUE;
break;
}
break;
case 'N':
if( !str_cmp( word, "Name" ) )
{
STRFREE( mob->name );
mob->name = fread_string( fp );
fMatch = TRUE;
break;
}
break;
case 'P':
KEY( "Position", mob->position, fread_number( fp ) );
break;
case 'R':
KEY( "Room", inroom, fread_number( fp ) );
break;
case 'S':
if( !str_cmp( word, "Short" ) )
{
STRFREE( mob->short_descr );
mob->short_descr = fread_string( fp );
fMatch = TRUE;
break;
}
break;
}
if( !fMatch )
{
bug( "%s: no match: %s", __FUNCTION__, word );
fread_to_eol( fp );
}
}
}
This is a cumulative fix ported over from SmaugFUSS which fixes some minor memory leaks and also adds protection against infinite loops if the pfiles containing the mobile data are corrupted and end prematurely. Such corruption and looping is an on-going problem with many Merc derived muds.