Skip to content

Commit d8af372

Browse files
authored
[GEN][ZH] Fix heap-use-after-free in W3DRadar::drawHeroIcon() (#1035)
1 parent c14b5ae commit d8af372

File tree

17 files changed

+253
-86
lines changed

17 files changed

+253
-86
lines changed

Core/GameEngine/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ set(GAMEENGINE_SRC
112112
# Include/Common/StateMachine.h
113113
# Include/Common/StatsCollector.h
114114
# Include/Common/STLTypedefs.h
115+
Include/Common/STLUtils.h
115116
# Include/Common/StreamingArchiveFile.h
116117
# Include/Common/SubsystemInterface.h
117118
# Include/Common/SystemInfo.h
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
** Command & Conquer Generals Zero Hour(tm)
3+
** Copyright 2025 TheSuperHackers
4+
**
5+
** This program is free software: you can redistribute it and/or modify
6+
** it under the terms of the GNU General Public License as published by
7+
** the Free Software Foundation, either version 3 of the License, or
8+
** (at your option) any later version.
9+
**
10+
** This program is distributed in the hope that it will be useful,
11+
** but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
** GNU General Public License for more details.
14+
**
15+
** You should have received a copy of the GNU General Public License
16+
** along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#pragma once
20+
21+
#include <Utility/CppMacros.h>
22+
#include <utility>
23+
24+
namespace stl
25+
{
26+
27+
// Finds first matching element in vector-like container and erases it.
28+
template <typename Container>
29+
bool find_and_erase(Container& container, const typename Container::value_type& value)
30+
{
31+
typename Container::const_iterator it = container.begin();
32+
for (; it != container.end(); ++it)
33+
{
34+
if (*it == value)
35+
{
36+
container.erase(it);
37+
return true;
38+
}
39+
}
40+
return false;
41+
}
42+
43+
// Finds first matching element in vector-like container and removes it by swapping it with the last element.
44+
// This is generally faster than erasing from a vector, but will change the element sorting.
45+
template <typename Container>
46+
bool find_and_erase_unordered(Container& container, const typename Container::value_type& value)
47+
{
48+
typename Container::iterator it = container.begin();
49+
for (; it != container.end(); ++it)
50+
{
51+
if (*it == value)
52+
{
53+
*it = CPP_11(std::move)(container.back());
54+
container.pop_back();
55+
return true;
56+
}
57+
}
58+
return false;
59+
}
60+
61+
} // namespace stl

Generals/Code/GameEngine/Include/Common/GameEngine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class GameEngine : public SubsystemInterface
8585

8686
protected:
8787

88+
virtual void resetSubsystems( void );
89+
8890
virtual FileSystem *createFileSystem( void ); ///< Factory for FileSystem classes
8991
virtual LocalFileSystem *createLocalFileSystem( void ) = 0; ///< Factory for LocalFileSystem classes
9092
virtual ArchiveFileSystem *createArchiveFileSystem( void ) = 0; ///< Factory for ArchiveFileSystem classes

Generals/Code/GameEngine/Include/Common/Radar.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,8 @@ class Radar : public Snapshot,
190190
Bool tryEvent( RadarEventType event, const Coord3D *pos ); ///< try to make a "stealth" event
191191

192192
// adding and removing objects from the radar
193-
void addObject( Object *obj ); ///< add object to radar
194-
void removeObject( Object *obj ); ///< remove object from radar
195-
void examineObject( Object *obj ); ///< re-examine object and resort if needed
193+
virtual bool addObject( Object *obj ); ///< add object to radar
194+
virtual bool removeObject( Object *obj ); ///< remove object from radar
196195

197196
// radar options
198197
void hide( Bool hide ) { m_radarHidden = hide; } ///< hide/unhide the radar

Generals/Code/GameEngine/Include/Common/STLTypedefs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class STLSpecialAlloc;
5858
#include "Common/UnicodeString.h"
5959
#include "Common/GameCommon.h"
6060
#include "Common/GameMemory.h"
61+
#include "Common/STLUtils.h"
6162

6263
//-----------------------------------------------------------------------------
6364

Generals/Code/GameEngine/Source/Common/GameEngine.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,8 @@ void GameEngine::init()
509509

510510
initDisabledMasks();
511511

512-
TheSubsystemList->resetAll();
512+
resetSubsystems();
513+
513514
HideControlBar();
514515
} // end init
515516

@@ -528,7 +529,7 @@ void GameEngine::reset( void )
528529
if (TheGameLogic->isInMultiplayerGame())
529530
deleteNetwork = true;
530531

531-
TheSubsystemList->resetAll();
532+
resetSubsystems();
532533

533534
if (deleteNetwork)
534535
{
@@ -545,6 +546,16 @@ void GameEngine::reset( void )
545546
}
546547
}
547548

549+
/// -----------------------------------------------------------------------------------------------
550+
void GameEngine::resetSubsystems( void )
551+
{
552+
// TheSuperHackers @fix xezon 09/06/2025 Reset GameLogic first to purge all world objects early.
553+
// This avoids potentially catastrophic issues when objects and subsystems have cross dependencies.
554+
TheGameLogic->reset();
555+
556+
TheSubsystemList->resetAll();
557+
}
558+
548559
/// -----------------------------------------------------------------------------------------------
549560
DECLARE_PERF_TIMER(GameEngine_update)
550561

Generals/Code/GameEngine/Source/Common/System/Radar.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -394,13 +394,13 @@ void Radar::newMap( TerrainLogic *terrain )
394394
/** Add an object to the radar list. The object will be sorted in the list to be grouped
395395
* using it's radar priority */
396396
//-------------------------------------------------------------------------------------------------
397-
void Radar::addObject( Object *obj )
397+
bool Radar::addObject( Object *obj )
398398
{
399399

400400
// get the radar priority for this object
401401
RadarPriorityType newPriority = obj->getRadarPriority();
402402
if( isPriorityVisible( newPriority ) == FALSE )
403-
return;
403+
return false;
404404

405405
// if this object is on the radar, remove it in favor of the new add
406406
RadarObject **list;
@@ -544,6 +544,7 @@ void Radar::addObject( Object *obj )
544544

545545
} // end else
546546

547+
return true;
547548
} // end addObject
548549

549550
//-------------------------------------------------------------------------------------------------
@@ -590,24 +591,24 @@ Bool Radar::deleteFromList( Object *obj, RadarObject **list )
590591
//-------------------------------------------------------------------------------------------------
591592
/** Remove an object from the radar, the object may reside in any list */
592593
//-------------------------------------------------------------------------------------------------
593-
void Radar::removeObject( Object *obj )
594+
bool Radar::removeObject( Object *obj )
594595
{
595596

596597
// sanity
597598
if( obj->friend_getRadarData() == NULL )
598-
return;
599+
return false;
599600

600601
if( deleteFromList( obj, &m_localObjectList ) == TRUE )
601-
return;
602+
return true;
602603
else if( deleteFromList( obj, &m_objectList ) == TRUE )
603-
return;
604+
return true;
604605
else
605606
{
606607

607608
// sanity
608609
DEBUG_ASSERTCRASH( 0, ("Radar: Tried to remove object '%s' which was not found\n",
609610
obj->getTemplate()->getName().str()) );
610-
611+
return false;
611612
} // end else
612613

613614
} // end removeObject

Generals/Code/GameEngineDevice/Include/W3DDevice/Common/W3DRadar.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class W3DRadar : public Radar
5959
virtual void update( void ); ///< subsystem update
6060
virtual void reset( void ); ///< subsystem reset
6161

62+
virtual bool addObject( Object *obj ); ///< add object to radar
63+
virtual bool removeObject( Object *obj ); ///< remove object from radar
64+
6265
virtual void newMap( TerrainLogic *terrain ); ///< reset radar for new map
6366

6467
void draw( Int pixelX, Int pixelY, Int width, Int height ); ///< draw the radar
@@ -80,7 +83,7 @@ class W3DRadar : public Radar
8083
void drawViewBox( Int pixelX, Int pixelY, Int width, Int height ); ///< draw view box
8184
void buildTerrainTexture( TerrainLogic *terrain ); ///< create the terrain texture of the radar
8285
void drawIcons( Int pixelX, Int pixelY, Int width, Int height ); ///< draw all of the radar icons
83-
void renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero = FALSE ); ///< render an object list to the texture
86+
void renderObjectList( const RadarObject *listHead, TextureClass *texture ); ///< render an object list to the texture
8487
void interpolateColorForHeight( RGBColor *color,
8588
Real height,
8689
Real hiZ,
@@ -118,7 +121,7 @@ class W3DRadar : public Radar
118121
Real m_viewZoom; ///< camera zoom used for the view box we have
119122
ICoord2D m_viewBox[ 4 ]; ///< radar cell points for the 4 corners of view box
120123

121-
std::list<const Coord3D *> m_cachedHeroPosList; //< cache of hero positions for drawing icons in radar overlay
124+
std::vector<const Object *> m_cachedHeroObjectList; //< cache of hero objects for drawing icons in radar overlay
122125
};
123126

124127

Generals/Code/GameEngineDevice/Source/W3DDevice/Common/System/W3DRadar.cpp

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -608,18 +608,18 @@ void W3DRadar::drawEvents( Int pixelX, Int pixelY, Int width, Int height )
608608
void W3DRadar::drawIcons( Int pixelX, Int pixelY, Int width, Int height )
609609
{
610610
// draw the hero icons
611-
std::list<const Coord3D *>::const_iterator iter = m_cachedHeroPosList.begin();
612-
while (iter != m_cachedHeroPosList.end())
611+
std::vector<const Object *>::const_iterator iter = m_cachedHeroObjectList.begin();
612+
while (iter != m_cachedHeroObjectList.end())
613613
{
614-
drawHeroIcon( pixelX, pixelY, width, height, (*iter) );
614+
drawHeroIcon( pixelX, pixelY, width, height, (*iter)->getPosition() );
615615
++iter;
616616
}
617617
}
618618

619619
//-------------------------------------------------------------------------------------------------
620620
/** Render an object list into the texture passed in */
621621
//-------------------------------------------------------------------------------------------------
622-
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero )
622+
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture )
623623
{
624624

625625
// sanity
@@ -637,12 +637,6 @@ void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *text
637637
if (player)
638638
playerIndex=player->getPlayerIndex();
639639

640-
if( calcHero )
641-
{
642-
// clear all entries from the cached hero object list
643-
m_cachedHeroPosList.clear();
644-
}
645-
646640
for( const RadarObject *rObj = listHead; rObj; rObj = rObj->friend_getNext() )
647641
{
648642

@@ -652,11 +646,6 @@ void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *text
652646
// get object
653647
const Object *obj = rObj->friend_getObject();
654648

655-
// cache hero object positions for drawing in icon layer
656-
if( calcHero && obj->isHero() )
657-
{
658-
m_cachedHeroPosList.push_back(obj->getPosition());
659-
}
660649
Bool skip = FALSE;
661650

662651
// check for shrouded status
@@ -1001,6 +990,34 @@ void W3DRadar::update( void )
1001990

1002991
} // end update
1003992

993+
//-------------------------------------------------------------------------------------------------
994+
bool W3DRadar::addObject( Object *obj )
995+
{
996+
if (Radar::addObject(obj))
997+
{
998+
if (obj->isHero())
999+
{
1000+
m_cachedHeroObjectList.push_back(obj);
1001+
}
1002+
return true;
1003+
}
1004+
return false;
1005+
}
1006+
1007+
//-------------------------------------------------------------------------------------------------
1008+
bool W3DRadar::removeObject( Object *obj )
1009+
{
1010+
if (Radar::removeObject(obj))
1011+
{
1012+
if (obj->isHero())
1013+
{
1014+
stl::find_and_erase_unordered(m_cachedHeroObjectList, obj);
1015+
}
1016+
return true;
1017+
}
1018+
return false;
1019+
}
1020+
10041021
//-------------------------------------------------------------------------------------------------
10051022
/** Reset the radar for the new map data being given to it */
10061023
//-------------------------------------------------------------------------------------------------
@@ -1416,7 +1433,7 @@ void W3DRadar::draw( Int pixelX, Int pixelY, Int width, Int height )
14161433

14171434
// rebuild the object overlay
14181435
renderObjectList( getObjectList(), m_overlayTexture );
1419-
renderObjectList( getLocalObjectList(), m_overlayTexture, TRUE );
1436+
renderObjectList( getLocalObjectList(), m_overlayTexture );
14201437

14211438
} // end if
14221439

@@ -1475,7 +1492,7 @@ void W3DRadar::refreshTerrain( TerrainLogic *terrain )
14751492

14761493
/*
14771494
*
1478-
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero )
1495+
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture )
14791496
{
14801497
14811498
// sanity
@@ -1495,12 +1512,6 @@ void W3DRadar::refreshTerrain( TerrainLogic *terrain )
14951512
14961513
UnsignedByte minAlpha = 8;
14971514
1498-
if( calcHero )
1499-
{
1500-
// clear all entries from the cached hero object list
1501-
m_cachedHeroPosList.clear();
1502-
}
1503-
15041515
for( const RadarObject *rObj = listHead; rObj; rObj = rObj->friend_getNext() )
15051516
{
15061517
UnsignedByte h = (UnsignedByte)(rObj->isTemporarilyHidden());
@@ -1513,12 +1524,6 @@ void W3DRadar::refreshTerrain( TerrainLogic *terrain )
15131524
const Object *obj = rObj->friend_getObject();
15141525
UnsignedByte r = 1; // all decoys
15151526
1516-
// cache hero object positions for drawing in icon layer
1517-
if( calcHero && obj->isHero() )
1518-
{
1519-
m_cachedHeroPosList.push_back(obj->getPosition());
1520-
}
1521-
15221527
// get the color we're going to draw in
15231528
UnsignedInt c = 0xfe000000;// this is a decoy
15241529
c |= (UnsignedInt)( obj->testStatus( OBJECT_STATUS_STEALTHED ) );//so is this

0 commit comments

Comments
 (0)