bugfix(contain): Add workaround for use-after-free bug that may crash the game#2747
bugfix(contain): Add workaround for use-after-free bug that may crash the game#2747Caball009 wants to merge 6 commits into
Conversation
|
| Filename | Overview |
|---|---|
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp | Core implementation of the workaround; onContainedBy, onRemovedFrom, onDestroy, and xfer all updated correctly; previously flagged xfer-save re-derivation issue is now guarded. |
| Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp | Mirrors the GeneralsMD changes; all four modified functions are consistent with the Zero Hour copy. |
| GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h | Member renamed from m_xferContainedByID to m_containedByID with updated documentation comment reflecting its broadened purpose. |
| Generals/Code/GameEngine/Include/GameLogic/Object.h | Same header rename as GeneralsMD; no logic changes. |
Sequence Diagram
sequenceDiagram
participant A as Object A (Container)
participant B as Object B (Contained)
participant GL as GameLogic
participant OC as OpenContain (A's module)
Note over A,B: Normal containment setup
A->>B: onContainedBy(A)
B->>B: "m_containedBy = A, m_containedByID = A.getID()"
Note over A,OC: Container destruction begins (bug scenario)
GL->>A: destroyObject(A)
A->>OC: onDelete() — m_containList already freed!
OC->>B: "onContainedBy(A) where A.isDestroyed()==true"
B->>B: "m_containedBy = A (dangling!), m_containedByID = INVALID_ID"
Note over B,GL: Contained object is also destroyed
GL->>B: destroyObject(B)
B->>B: onDestroy()
B->>B: "m_containedBy != null — enter block"
alt "m_containedByID == INVALID_ID (container already destroyed)"
B->>B: DEBUG_CRASH / skip removeFromContain (safe)
else ID valid (normal path)
B->>GL: findObjectByID(m_containedByID)
GL-->>B: returns m_containedBy (confirmed alive)
B->>OC: removeFromContain(B) — safe
end
Reviews (7): Last reviewed commit: "Replicated in Generals." | Re-trigger Greptile
3c40642 to
69cfc9b
Compare
da36906 to
1af8f31
Compare
|
I just did a test in multiplayer with local instances and noticed 6 places where the game would access the container object after destruction. I noticed because the game would crash if the memory is overwritten on deallocation, as is the case with The following 5 could be rewritten like so: An early return if |
Is this for a follow up or what do we do with this? |
Nothing as far as I'm concerned. It's just an explicit acknowledgement that the 'damage' for this bug is so extensive that I don't see how it can be fixed with retail compatibility, but at least it shouldn't crash. |
7635f14 to
756de02
Compare
Resolved merge conflicts in 'Object.cpp'.
|
Replicated in Generals. |
This PR provides a retail specific workaround for use-after-free bug that could crash the game under very specific conditions (see issue and related PR).
The actual crash would happen when e.g.
OpenContain::m_containListis accessed after it's been destructed. STLPort and MSVC implementations forstd::listrely on dynamically allocated memory, so accessing the begin / end iterator after the destruction of the list is not just undefined behavior, it crashes the game. See minimal crash reproduction: https://godbolt.org/z/T9sEb4MsKTODO: