How a 20-Year-Old GTA San Andreas Bug Resurfaced in Windows 11 24H2
The Skimmer seaplane vanished from GTA San Andreas after players updated to Windows 11 24H2, launching characters into space at 10 nonillion meters altitude. The culprit turned out to be an uninitialized variable bug from 2004 that went unnoticed for twenty years because previous Windows versions happened to leave the right stack garbage in place.
In late 2024, players running GTA San Andreas on Windows 11 24H2 started reporting a strange problem: the Skimmer seaplane was simply gone. Not just rare — completely absent. You couldn't find it at its usual spawn points, and trying to spawn it with a trainer caused the player character to be teleported to an altitude of 10.3 nonillion meters. The game didn't crash — it just happily placed you in orbit.
I maintain SilentPatch, a mod that fixes bugs in the classic GTA titles. This one landed in my bug tracker. I couldn't reproduce it on Windows 10 22H2 or Windows 11 23H2 — same game files, no problem. The moment I set up a VM with 24H2, the Skimmer disappeared. Time to dig in.
Tracing the Skimmer's Disappearance
The first step was identifying where vehicle objects get loaded. The function CFileLoader::LoadVehicleObject parses entries from vehicles.ide, the game's vehicle definition file. Each line defines a vehicle's properties including its bounding box, model names, and — for wheeled and aircraft vehicles — wheel scale parameters.
The bounding box Z-coordinates for the Skimmer were coming out as approximately negative 4.3 × 10^33. That's not a valid coordinate. Something in the loading pipeline was producing garbage values for this specific vehicle.
The Incomplete Definition
Comparing the Skimmer's entry in vehicles.ide against other aircraft revealed the problem immediately:
460, skimmer, skimmer, plane, SEAPLANE, SKIMMER, null, ignore, 5, 0, 0
Compare with the Rustler fighter plane:
476, rustler, rustler, plane, RUSTLER, RUSTLER, rustler, ignore, 10, 0, 0, -1, 0.6, 0.3, -1
The Skimmer is missing four parameters at the end: wheel model ID, front wheel scale, rear wheel scale, and wheel upgrade class. These parameters exist in the game's loading code for all plane-type vehicles. For the Skimmer, they were simply never written into the data file.
The 20-Year Hidden Bug
The loading function uses sscanf to parse each line. The critical problem: the local variables for the four missing parameters are never initialized before the sscanf call, and the function never checks sscanf's return value to verify that those parameters were actually parsed.
When sscanf doesn't find those parameters in the Skimmer's line, it simply doesn't write to those variables. The variables retain whatever value happened to be on the stack at the time of the function call — classic undefined behavior.
For twenty years, this happened to work. Why? Because the vehicle loaded immediately before the Skimmer in vehicles.ide is the TopFun van, which has wheel scales of 0.7. After the TopFun call completed, those values sat on the stack at exactly the addresses the Skimmer's uninitialized variables would occupy. The Skimmer silently inherited 0.7 as its wheel scales — by accident.
What Windows 11 24H2 Changed
Windows 11 24H2 modified the internal implementation of LeaveCriticalSection. This function is called within fgets, which sscanf uses when reading lines. In 24H2, LeaveCriticalSection now uses significantly more stack space than before — specifically, it now zeroes out or otherwise modifies a larger region of the stack frame.
This overwrote the region of the stack where the Skimmer's uninitialized wheel scale variables were sitting. Instead of inheriting 0.7 from TopFun, the variables now contained whatever LeaveCriticalSection left there — which turned out to be very large floating-point garbage. Those garbage wheel scales propagated into the suspension calculations, corrupted the bounding box, and resulted in the Skimmer being placed at an impossible altitude.
This is not a Windows 11 bug. Microsoft made a legitimate internal implementation change. The contract for LeaveCriticalSection has never guaranteed anything about stack contents after the call. GTA San Andreas was relying on undocumented, accidental behavior that happened to be stable for two decades.
The Fix
SilentPatch wraps the sscanf call with proper default value initialization before it runs:
*wheelModelID = -1;
*frontWheelSize = 0.7f;
*rearWheelSize = 0.7f;
*wheelUpgradeClass = -1;
The choice of 0.7 as the default is deliberate — it matches the values used by other seaplanes in the game (Sea Sparrow and Vortex). Rockstar themselves used 1.0 in their later console ports, but 0.7 produces better visual results for the Skimmer's proportions.
Users without SilentPatch can also fix this manually: open data/vehicles.ide in a text editor and add the four missing parameters to the Skimmer's line:
460, skimmer, skimmer, plane, SEAPLANE, SKIMMER, null, ignore, 5, 0, 0, -1, 0.7, 0.7, -1
The Lesson
This bug is a textbook case of undefined behavior that survived in production for two decades because of a lucky accident in memory layout. Two rules would have caught it immediately:
- Always initialize local variables. Modern compilers warn about this. Enable
-Wall -Wextraand treat warnings as errors. - Always check return values.
sscanfreturns the number of items successfully parsed. If you're expecting 4 items and get 0, something is wrong — handle it.
The broader lesson is about undefined behavior in general: code that relies on it may work correctly for years or decades, across multiple platforms, until one seemingly unrelated change in an OS internal triggers the latent bug. You cannot reason about when this will happen. The only safe approach is to eliminate the undefined behavior at the source.
FAQ
What is this article about in one sentence?
This article explains the core idea in practical terms and focuses on what you can apply in real work.
Who is this article for?
It is written for engineers, technical leaders, and curious readers who want a clear, implementation-focused explanation.
What should I read next?
Use the related articles below to continue with closely connected topics and concrete examples.