Skip to content

Commit f334383

Browse files
authored
bugfix(contain): Prevent riders from being added to destroyed container object when Reinforcement Pad is destroyed before Troop Crawler drop (TheSuperHackers#2746)
1 parent 7c78a5e commit f334383

4 files changed

Lines changed: 60 additions & 2 deletions

File tree

Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ void OpenContain::addOrRemoveObjFromWorld(Object* obj, Bool add)
234234
}
235235
else
236236
{
237+
DEBUG_ASSERTCRASH(!getObject()->isEffectivelyDead() && !getObject()->isDestroyed(),
238+
("object shouldn't become an occupant of a dead or destroyed container object"));
239+
237240
// remove object from its group (if any)
238241
obj->leaveGroup();
239242

@@ -277,11 +280,26 @@ void OpenContain::addToContain( Object *rider )
277280
if( rider == nullptr )
278281
return;
279282

283+
#if !RETAIL_COMPATIBLE_CRC
284+
// TheSuperHackers @bugfix Caball009 25/05/2026 Ensure the occupant is only added to a non-destroyed
285+
// container to avoid an invalid state and use-after-free bugs when accessing the contained by pointer.
286+
if (getObject()->isDestroyed())
287+
{
288+
DEBUG_CRASH(("'%s' is about to be added to '%s', which is destroyed",
289+
rider->getTemplate()->getName().str(), getObject()->getTemplate()->getName().str()));
290+
return;
291+
}
292+
#endif
293+
280294
// TheSuperHackers @bugfix Stubbjax 06/02/2026 Ensure the rider is not destroyed to prevent a
281295
// likely crash if it enters the container on the same frame. If this occurs with an unpatched
282296
// client present in a match, the game has a small chance to mismatch.
283297
if (rider->isDestroyed())
298+
{
299+
DEBUG_CRASH(("'%s', which is destroyed, is about to be added to '%s'",
300+
rider->getTemplate()->getName().str(), getObject()->getTemplate()->getName().str()));
284301
return;
302+
}
285303

286304
#if defined(RTS_DEBUG)
287305
if( !isValidContainerFor( rider, false ) )

Generals/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,19 @@ UpdateSleepTime TransportContain::update()
362362
{
363363
const TransportContainModuleData *moduleData = getTransportContainModuleData();
364364

365-
if( m_payloadCreated == FALSE )
365+
if (m_payloadCreated == FALSE)
366+
{
367+
#if RETAIL_COMPATIBLE_CRC
366368
createPayload();
369+
#else
370+
// TheSuperHackers @bugfix Caball009 25/05/2026 Don't create payload
371+
// for destroyed object to avoid leaving the payload in an invalid state.
372+
if (!getObject()->isDestroyed())
373+
{
374+
createPayload();
375+
}
376+
#endif
377+
}
367378

368379
if( moduleData && moduleData->m_healthRegen )
369380
{

GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,9 @@ void OpenContain::addOrRemoveObjFromWorld(Object* obj, Bool add)
241241
}
242242
else
243243
{
244+
DEBUG_ASSERTCRASH(!getObject()->isEffectivelyDead() && !getObject()->isDestroyed(),
245+
("object shouldn't become an occupant of a dead or destroyed container object"));
246+
244247
// remove object from its group (if any)
245248
obj->leaveGroup();
246249

@@ -292,11 +295,26 @@ void OpenContain::addToContain( Object *rider )
292295
if( rider == nullptr )
293296
return;
294297

298+
#if !RETAIL_COMPATIBLE_CRC
299+
// TheSuperHackers @bugfix Caball009 25/05/2026 Ensure the occupant is only added to a non-destroyed
300+
// container to avoid an invalid state and use-after-free bugs when accessing the contained by pointer.
301+
if (getObject()->isDestroyed())
302+
{
303+
DEBUG_CRASH(("'%s' is about to be added to '%s', which is destroyed",
304+
rider->getTemplate()->getName().str(), getObject()->getTemplate()->getName().str()));
305+
return;
306+
}
307+
#endif
308+
295309
// TheSuperHackers @bugfix Stubbjax 06/02/2026 Ensure the rider is not destroyed to prevent a
296310
// likely crash if it enters the container on the same frame. If this occurs with an unpatched
297311
// client present in a match, the game has a small chance to mismatch.
298312
if (rider->isDestroyed())
313+
{
314+
DEBUG_CRASH(("'%s', which is destroyed, is about to be added to '%s'",
315+
rider->getTemplate()->getName().str(), getObject()->getTemplate()->getName().str()));
299316
return;
317+
}
300318

301319
Drawable *riderDraw = rider->getDrawable();
302320
Bool wasSelected = FALSE;

GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,19 @@ UpdateSleepTime TransportContain::update()
469469
{
470470
const TransportContainModuleData *moduleData = getTransportContainModuleData();
471471

472-
if( m_payloadCreated == FALSE )
472+
if (m_payloadCreated == FALSE)
473+
{
474+
#if RETAIL_COMPATIBLE_CRC
473475
createPayload();
476+
#else
477+
// TheSuperHackers @bugfix Caball009 25/05/2026 Don't create payload
478+
// for destroyed object to avoid leaving the payload in an invalid state.
479+
if (!getObject()->isDestroyed())
480+
{
481+
createPayload();
482+
}
483+
#endif
484+
}
474485

475486
if( moduleData && moduleData->m_healthRegen )
476487
{

0 commit comments

Comments
 (0)