Skip to content

Commit d9d40c1

Browse files
committed
[ZH] Fix heap-use-after-free in W3DRadar::drawHeroIcon()
1 parent c14b5ae commit d9d40c1

File tree

9 files changed

+127
-43
lines changed

9 files changed

+127
-43
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

GeneralsMD/Code/GameEngine/Include/Common/GameEngine.h

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

8585
protected:
8686

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

GeneralsMD/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

GeneralsMD/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

GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,8 @@ void GameEngine::init()
682682
initDisabledMasks();
683683
initDamageTypeFlags();
684684

685-
TheSubsystemList->resetAll();
685+
resetSubsystems();
686+
686687
HideControlBar();
687688
} // end init
688689

@@ -701,7 +702,7 @@ void GameEngine::reset( void )
701702
if (TheGameLogic->isInMultiplayerGame())
702703
deleteNetwork = true;
703704

704-
TheSubsystemList->resetAll();
705+
resetSubsystems();
705706

706707
if (deleteNetwork)
707708
{
@@ -718,6 +719,16 @@ void GameEngine::reset( void )
718719
}
719720
}
720721

722+
/// -----------------------------------------------------------------------------------------------
723+
void GameEngine::resetSubsystems( void )
724+
{
725+
// TheSuperHackers @fix xezon 09/06/2025 Reset GameLogic first to purge all world objects early.
726+
// This avoids potentially catastrophic issues when objects and subsystems have cross dependencies.
727+
TheGameLogic->reset();
728+
729+
TheSubsystemList->resetAll();
730+
}
731+
721732
/// -----------------------------------------------------------------------------------------------
722733
DECLARE_PERF_TIMER(GameEngine_update)
723734

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

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

403403
// get the radar priority for this object
404404
RadarPriorityType newPriority = obj->getRadarPriority();
405405
if( isPriorityVisible( newPriority ) == FALSE )
406-
return;
406+
return false;
407407

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

548548
} // end else
549549

550+
return true;
550551
} // end addObject
551552

552553
//-------------------------------------------------------------------------------------------------
@@ -593,24 +594,24 @@ Bool Radar::deleteFromList( Object *obj, RadarObject **list )
593594
//-------------------------------------------------------------------------------------------------
594595
/** Remove an object from the radar, the object may reside in any list */
595596
//-------------------------------------------------------------------------------------------------
596-
void Radar::removeObject( Object *obj )
597+
bool Radar::removeObject( Object *obj )
597598
{
598599

599600
// sanity
600601
if( obj->friend_getRadarData() == NULL )
601-
return;
602+
return false;
602603

603604
if( deleteFromList( obj, &m_localObjectList ) == TRUE )
604-
return;
605+
return true;
605606
else if( deleteFromList( obj, &m_objectList ) == TRUE )
606-
return;
607+
return true;
607608
else
608609
{
609610

610611
// sanity
611612
DEBUG_ASSERTCRASH( 0, ("Radar: Tried to remove object '%s' which was not found\n",
612613
obj->getTemplate()->getName().str()) );
613-
614+
return false;
614615
} // end else
615616

616617
} // end removeObject

GeneralsMD/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

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

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

617617
//-------------------------------------------------------------------------------------------------
618618
/** Render an object list into the texture passed in */
619619
//-------------------------------------------------------------------------------------------------
620-
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero )
620+
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture )
621621
{
622622

623623
// sanity
@@ -635,12 +635,6 @@ void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *text
635635
if (player)
636636
playerIndex=player->getPlayerIndex();
637637

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

@@ -650,11 +644,6 @@ void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *text
650644
// get object
651645
const Object *obj = rObj->friend_getObject();
652646

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

660649
// check for shrouded status
@@ -995,6 +984,34 @@ void W3DRadar::update( void )
995984

996985
} // end update
997986

987+
//-------------------------------------------------------------------------------------------------
988+
bool W3DRadar::addObject( Object *obj )
989+
{
990+
if (Radar::addObject(obj))
991+
{
992+
if (obj->isHero())
993+
{
994+
m_cachedHeroObjectList.push_back(obj);
995+
}
996+
return true;
997+
}
998+
return false;
999+
}
1000+
1001+
//-------------------------------------------------------------------------------------------------
1002+
bool W3DRadar::removeObject( Object *obj )
1003+
{
1004+
if (Radar::removeObject(obj))
1005+
{
1006+
if (obj->isHero())
1007+
{
1008+
stl::find_and_erase_unordered(m_cachedHeroObjectList, obj);
1009+
}
1010+
return true;
1011+
}
1012+
return false;
1013+
}
1014+
9981015
//-------------------------------------------------------------------------------------------------
9991016
/** Reset the radar for the new map data being given to it */
10001017
//-------------------------------------------------------------------------------------------------
@@ -1404,7 +1421,7 @@ void W3DRadar::draw( Int pixelX, Int pixelY, Int width, Int height )
14041421

14051422
// rebuild the object overlay
14061423
renderObjectList( getObjectList(), m_overlayTexture );
1407-
renderObjectList( getLocalObjectList(), m_overlayTexture, TRUE );
1424+
renderObjectList( getLocalObjectList(), m_overlayTexture );
14081425

14091426
} // end if
14101427

@@ -1463,7 +1480,7 @@ void W3DRadar::refreshTerrain( TerrainLogic *terrain )
14631480

14641481
/*
14651482
*
1466-
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero )
1483+
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture )
14671484
{
14681485
14691486
// sanity
@@ -1483,12 +1500,6 @@ void W3DRadar::refreshTerrain( TerrainLogic *terrain )
14831500
14841501
UnsignedByte minAlpha = 8;
14851502
1486-
if( calcHero )
1487-
{
1488-
// clear all entries from the cached hero object list
1489-
m_cachedHeroPosList.clear();
1490-
}
1491-
14921503
for( const RadarObject *rObj = listHead; rObj; rObj = rObj->friend_getNext() )
14931504
{
14941505
UnsignedByte h = (UnsignedByte)(rObj->isTemporarilyHidden());
@@ -1501,12 +1512,6 @@ void W3DRadar::refreshTerrain( TerrainLogic *terrain )
15011512
const Object *obj = rObj->friend_getObject();
15021513
UnsignedByte r = 1; // all decoys
15031514
1504-
// cache hero object positions for drawing in icon layer
1505-
if( calcHero && obj->isHero() )
1506-
{
1507-
m_cachedHeroPosList.push_back(obj->getPosition());
1508-
}
1509-
15101515
// get the color we're going to draw in
15111516
UnsignedInt c = 0xfe000000;// this is a decoy
15121517
c |= (UnsignedInt)( obj->testStatus( OBJECT_STATUS_STEALTHED ) );//so is this

0 commit comments

Comments
 (0)