In a Generals network game, surrendering with units inside a Tunnel Network transfers the tunnels and occupants to a living ally. When those tunnels are subsequently sold or destroyed, the occupants retain pointers to the freed tunnel objects. This issue covers the crash when such an occupant is destroyed; the crash during combat is #3316.
During match cleanup in GameLogic::reset, or when an affected occupant is destroyed, Object::onDestroy follows its stale container pointer and calls into the destroyed tunnel. Debug builds crash every time, release builds crash when the memory was reused.
In the reported retail 1.08 replay:
- The GLA player surrenders at frame 58633.
- The transferred tunnels die at frame 60054 under the ally's tunnel tracker.
- A Scorpion and a Rebel retain stale container pointers when reset runs at frame 60058, causing the crash.
The same happens in an automated LAN game recorded on a build of main with freed-memory poisoning: both live clients crash 200 frames after the ally's AI sells the transferred tunnels, and headless playback of that recording crashes at the same point. Recording: surrender-tunnel-ally-poisoned-12800.rep.
PR #3308 mitigates the tested destruction cases. It does not resolve the underlying dangling pointer, which is what #3316 is about.
Reproduction
- Start a Generals LAN team game as GLA with a living ally. Skirmish does not offer Surrender.
- Build a Tunnel Network and place units inside it.
- Surrender, transferring assets to the ally.
- Have the transferred tunnels sold or destroyed.
- End the match, or wait for the transferred units to die.
Cause and compatibility
In retail-compatible Generals, TunnelContain::onCapture is disabled (#3242). After the transfer, a tunnel's death notifies the new owner's tracker, whose count underflows, while the original tracker retains the tunnel registration and its occupants.
A single automated LAN run with RETAIL_COMPATIBLE_CRC disabled completed 27300 frames, including the transfer, tunnel sales and the ally's subsequent surrender, without a crash or client desync. This does not establish a general fix.
Related: #3316, #3242, #3165, and the related stale-container reproduction in #2467.
In a Generals network game, surrendering with units inside a Tunnel Network transfers the tunnels and occupants to a living ally. When those tunnels are subsequently sold or destroyed, the occupants retain pointers to the freed tunnel objects. This issue covers the crash when such an occupant is destroyed; the crash during combat is #3316.
During match cleanup in
GameLogic::reset, or when an affected occupant is destroyed,Object::onDestroyfollows its stale container pointer and calls into the destroyed tunnel. Debug builds crash every time, release builds crash when the memory was reused.In the reported retail 1.08 replay:
The same happens in an automated LAN game recorded on a build of main with freed-memory poisoning: both live clients crash 200 frames after the ally's AI sells the transferred tunnels, and headless playback of that recording crashes at the same point. Recording:
surrender-tunnel-ally-poisoned-12800.rep.PR #3308 mitigates the tested destruction cases. It does not resolve the underlying dangling pointer, which is what #3316 is about.
Reproduction
Cause and compatibility
In retail-compatible Generals,
TunnelContain::onCaptureis disabled (#3242). After the transfer, a tunnel's death notifies the new owner's tracker, whose count underflows, while the original tracker retains the tunnel registration and its occupants.A single automated LAN run with
RETAIL_COMPATIBLE_CRCdisabled completed 27300 frames, including the transfer, tunnel sales and the ally's subsequent surrender, without a crash or client desync. This does not establish a general fix.Related: #3316, #3242, #3165, and the related stale-container reproduction in #2467.