Skip to content

[GEN][ZH] Fix AIGroup memory management and game crash when a player is selected in Replay playback due heap-use-after-free in GameLogic::logicMessageDispatcher() #1004

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

xezon
Copy link

@xezon xezon commented Jun 3, 2025

This change fixes a critical problem with AIGroup, that happens all the time in any match, but only crashes the Replay playback when a player is selected.

The Problem

AIGroup has a fundamental memory management flaw with its AIGroup::remove function. It deletes itself when all members are removed, which means that any non-AIGroup members holding a reference to that AIGroup will dangle when it is deleted.

This happens all the time in GameLogic::logicMessageDispatcher with its currentlySelectedGroup variable. It holds objects in a AIGroup, but that AIGroup can be deleted by external events, for example

void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )
{
...
  AIGroup* currentlySelectedGroup = NULL;

  if (isInGame())
  {
    if (msg->getType() >= GameMessage::MSG_BEGIN_NETWORK_MESSAGES && msg->getType() <= GameMessage::MSG_END_NETWORK_MESSAGES)
    {
      if (msg->getType() != GameMessage::MSG_LOGIC_CRC && msg->getType() != GameMessage::MSG_SET_REPLAY_CAMERA)
      {
        currentlySelectedGroup = TheAI->createGroup(); // can't do this outside a game - it'll cause sync errors galore.
        CRCGEN_LOG(( "Creating AIGroup %d in GameLogic::logicMessageDispatcher()\n", (currentlySelectedGroup)?currentlySelectedGroup->getID():0 ));
        thisPlayer->getCurrentSelectionAsAIGroup(currentlySelectedGroup);

...
  // process the message
  GameMessage::Type msgType = msg->getType();
  switch( msgType )
  {
...
    case GameMessage::MSG_REMOVE_BEACON:
    {
      AIGroup* allSelectedObjects = TheAI->createGroup();
      thisPlayer->getCurrentSelectionAsAIGroup(allSelectedObjects); // <----- xezon: this will delete the AIGroup pointed to by currentlySelectedGroup

There are multiple code paths like this that can delete currentlySelectedGroup.

The Solution

Properly reference count AIGroup so that an AIGroup is only destroyed when all owners let go off it. This fixes all problems, any potential leaks, crashes. It also makes the code simpler in some places.

TODO

  • Test against VC6 Golden Replay
  • Replace RefCountClass with RefCountValue
  • Replicate in Generals
  • Test against more replays

@xezon xezon added this to the Stability fixes milestone Jun 3, 2025
@xezon xezon added Bug Something is not working right, typically is user facing Critical Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour Crash This is a crash labels Jun 3, 2025
@xezon
Copy link
Author

xezon commented Jun 3, 2025

This fix was harder than it looks. I think I spent 15+ hours on this...

@xezon xezon changed the title [GEN][ZH] Improve AIGroup memory management and fix game crash when a player is selected in Replay playback [GEN][ZH] Fix AIGroup memory management and game crash when a player is selected in Replay playback due heap-use-after-free in GameLogic::logicMessageDispatcher() Jun 4, 2025
@xezon xezon force-pushed the xezon/fix-aigroup-2 branch from c819bd4 to a406fe6 Compare June 7, 2025 10:30
@xezon
Copy link
Author

xezon commented Jun 7, 2025

Rebased on main.

@xezon xezon requested a review from a team June 13, 2025 10:20
Copy link

@Mauller Mauller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, i can see how the messaging side would have caused issues.

But i think a few places can be cleaned up a bit due to the way the ref pointers work.

@xezon xezon force-pushed the xezon/fix-aigroup-2 branch from a406fe6 to 55c46d9 Compare June 18, 2025 17:26
@xezon
Copy link
Author

xezon commented Jun 18, 2025

RefCountClass replaced with RefCountValue to not change the size of class AIGroup.

Replicated in Generals.

@xezon
Copy link
Author

xezon commented Jun 18, 2025

Compile error fixed. Forgot to return values.

@xezon
Copy link
Author

xezon commented Jun 19, 2025

@helmutbuhler Can you please test this change against a few more replays?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something is not working right, typically is user facing Crash This is a crash Critical Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Game can crash when clicking on player icon in Replay Mode
3 participants