bugfix(object): Avoid potential crash due to dangling contain module when Troop Crawler is destroyed before reaching Reinforcement Pad - #3165
Conversation
PR Summary by QodoPrevent crash from dangling containment modules during destruction
AI Description
Diagram
High-Level Assessment
Files changed (6)
|
Code Review by Qodo
1. onDestroy() dereferences dangling m_containedBy
|
4d14036 to
831ff73
Compare
|
Code review by qodo was updated up to the latest commit 831ff73 |
|
It would be nice to merge this with 2 commits: 1 for revert and 1 for fixes |
I think that would be confusing during the review process. I can do that at the end, though. |
432b87a to
9cae392
Compare
9cae392 to
2a46621
Compare
Do you have a suggestion for the first commit name? I intend to use the PR title as second commit name. I assume both commit names should have the pull request link included. |
2a46621 to
41228ae
Compare
|
Maybe "Revert previous fix for crash with dangling contain module in Object::onDestroy() when Reinforcement Pad is destroyed before Troop Crawler drop" Is quite a bit long :) |
…aching Reinforcement Pad (TheSuperHackers#3165)
… before reaching Reinforcement Pad (TheSuperHackers#3165) Due to use-after-free bug resulting in dangling contain module in OpenContain::removeFromContain()
41228ae to
6b9b409
Compare
|
| Filename | Overview |
|---|---|
| Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp | Removes the retail dangling-pointer validity guard and exposes a freed container dereference during occupant destruction. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp | Mirrors the same unsafe Object::onDestroy containment lookup in Zero Hour. |
| Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp | Clears the list and checks empty state after destruction, but this guard is reached only after the unsafe container lookup. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp | Applies the same post-destruction list workaround to Zero Hour. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp | Reverts retail-compatible contained-by ID bookkeeping for portable structures without an independently established new defect. |
| Generals/Code/GameEngine/Include/GameLogic/Object.h | Renames the serialized containment ID field to clarify its transfer-only role. |
| GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h | Mirrors the transfer-only containment ID rename for Zero Hour. |
Sequence Diagram
sequenceDiagram
participant C as Troop Crawler
participant O as Occupant
participant G as GameLogic
C->>G: Destroy en route
G->>C: Delete Object and OpenContain
Note over O: m_containedBy still references C
G->>O: Destroy during later cleanup
O->>C: "m_containedBy->getContain()"
Note over C,O: Freed Object is accessed before empty-list guard
Prompt To Fix All With AI
### Issue 1
Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp:696
**Dangling container dereferenced first**
When an occupant retains `m_containedBy` after its en-route Troop Crawler is deleted, this condition calls `getContain()` through the freed object before reaching the new empty-list check, causing the same use-after-free crash during occupant destruction.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "bugfix(object): Avoid potential crash wh..." | Re-trigger Greptile
|
I made the commit titles a bit shorter.
Just for the record: the issue has nothing to do with the Reinforcement Pad, just that the (transport plane with the) Troop Crawler gets destroyed while en route to the pad. Ready for rebase and merge. |
The purpose of the two pull requests was to avoid a crash while retaining retail compatibility, but the fix is rather convoluted. Additionally, this is the second time I came across one or more replays that mismatch because of those changes. So the purpose of this PR is to revert the previous two and fix the crash with a different implementation.
Here's a replay that mismatches with the current implementation. It mismatches because it relied on use-after-free bugs, which was intentionally prevented to avoid potential crashes:
18-11-57_2v4_0_24_HardAI_HardAI_HardAI_HardAI.zip
If a troop crawler that's en route (by plane) to the reinforcement pad gets destroyed, the occupants are left in a state of limbo (see issue). They hold on to the pointer of the destroyed troop crawler, which leads to use-after-free bugs. When the occupants' objects eventually get destroyed (e.g. when a player surrenders or the game ends), the use-after-free bug may crash the game when
OpenContain::m_containListis accessed after its destruction.GeneralsGameCode/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Line 437 in a40cdb6
Here's a minimal crash reproduction that shows why the crash happens during the call to
std::find:https://godbolt.org/z/znjGGrTvG
https://godbolt.org/z/MzYz6eeMb
See commits for cleaner diff.
TODO: