You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Non-deterministic crashes when loading very large maps (width+height=512) #2219
While testing very large maps, I noticed unstable and inconsistent crashes related to map size. I created maps where width + height = 512. Sometimes the map loads and runs normally without any issues, but other times the game crashes either during loading or shortly after the match starts (20-30 sec).
The behavior is non-deterministic and seems to depend on the game state rather than the map itself. If a large map successfully launches once, it will usually continue working on subsequent launches. If it crashes once, it will usually keep crashing repeatedly. However, launching a different map and then returning to the large map can change the behavior again. This suggests that some initial conditions or memory state inside the game affect whether the map works or fails.
In one case RA2 crashed while Yuri’s Revenge loaded the same map normally. Maps with more terrain detail, height variation, cliffs, and similar features appear to trigger crashes more often.
One possible explanation is that the game writes beyond memory allocated for map cells or related structures, causing memory corruption and unpredictable behavior. Since the maps sometimes work correctly, it feels like the engine is very close to supporting these sizes already, but becomes unstable depending on memory layout.
Here are test maps. The "01" is less prone to errors, while the "12" is very prone. However, both of them can produce errors or not:
Crashes trying to clear map sub-zone tracking vectors on scenario init. It is possible that large enough maps can cause overflows somewhere which result in crashes - even if it works most of the time.
Well that's because of the hardcoded size-limit of game itself.
In MapClass,there is a member called "VectorClass<CellClass*> Cells;" , it's hardcoded to be 262144 DWORD size,can only storage 512512 cell of map.When the game wanna access a Cell with CellStruct,it will count the index of the Cell at this VectorClass array.
Although 512512 cell shouldn't crash the map,but as we all know, WWSB (lmao),try create 511*511 size map or use un-hardcoded this(for example : create CellsMapExt,hook all codes that references the origin Cells in the game)
And some operation of the vanilla game will cause some undefined access,like : try get cell of (128,129) in a 128 x 128 size map,in origin logic,the game will found that (128,129) storages 00000000,comparing the value and skip.But when it turns to 512x512 : it might lead a read access to an undefined memory area,and crash the game.That might be the reason of this.
Static analysis points to a signed-extension bug when packing subzone connection IDs, which closely matches the values in the attached crash log. The instruction-level defect is confirmed in the executable examined; its attribution to this particular crash still needs runtime confirmation.
The addresses below refer to a 32-bit Yuri’s Revenge gamemd.exe database with image base 0x400000. Descriptive function names are reconstructed analysis names, not original debug symbols.
EAX: 0008FFF7
ECX: 2111D024
EBP: 0000FFFF
EDI: 0000812C
ESI: 2135D000
In the examined executable, 0x58241B is inside the connection-processing portion of the subzone rebuilding routine at 0x581F90. More specifically, this instruction accesses a subzone entry while constructing its adjacency information. The faulting instruction itself is not a vector destructor or clearing operation.
A connection record contains two 16-bit endpoint IDs packed into one 32-bit value:
bits 31..16: high endpoint ID
bits 15..0: low endpoint ID
The consumer extracts these IDs as unsigned values and uses them to index the subzone tracking array. Each subzone entry occupies 0x24 bytes, or 36 bytes.
At the crash, the high endpoint is 0xFFFF, and the array base is 0x2111D024. The resulting access is:
This exactly matches the invalid address recorded in the log. The actual instruction is:
0058241B mov eax, [ecx+eax*4+8]
At that point, EAX = 9 * 0xFFFF = 0x0008FFF7, producing the same address calculation.
The likely origin of the 0xFFFF endpoint is in the connection-record producer at 0x582D70. One relevant packing sequence is:
00582FDD movsx esi, word ptr [ecx+eax2] ; endpoint for high half
00583006 movsx ecx, word ptr [edx+eax2] ; endpoint for low half
0058300A mov eax, esi
0058300F shl eax, 10h
00583012 or eax, ecx
The problem is the sign extension of the low-half endpoint before the OR.
For example, consider the low endpoint observed in the crash:
16-bit ID: 0x812C = 33068
After MOVSX to 32 bits: 0xFFFF812C
MOVSX treats bit 15 as a sign bit and copies it into the upper 16 bits. Therefore, any low endpoint in the range 0x8000..0xFFFF produces an extended value whose upper half is all ones.
Suppose the intended high endpoint were 0x1234. Correct packing would produce:
high endpoint << 16: 0x12340000
zero-extended low: 0x0000812C
OR result: 0x1234812C
The observed instruction sequence instead produces:
high endpoint << 16: 0x12340000
sign-extended low: 0xFFFF812C
OR result: 0xFFFF812C
The low endpoint remains 0x812C, but its sign-extension bits overwrite the entire high endpoint with 0xFFFF. This happens regardless of the intended high endpoint’s value.
The consumer then decodes:
low endpoint = 0x812C
high endpoint = 0xFFFF
Those are precisely the endpoint values indicated by EDI and EBP in the crash log. The consumer does not validate the decoded high endpoint against the outer tracking vector’s count before the access at 0x58241B.
A second instance of the same producer pattern is present at 0x5830A0 → 0x5830AC. Consequently, this should be reviewed as a connection-packing issue across the relevant producer paths, rather than treating only the final invalid access as the defect.
The important threshold for this mechanism is 32768 in an endpoint ID, not 65536 subzones. An actual counter wrap to 0xFFFF is unnecessary: sign extension can manufacture that endpoint from an otherwise representable 16-bit ID such as 0x812C.
The crash stack also contains 0xA38E at the location corresponding to the rebuilding routine’s local subzone sequence counter. Interpreted using the examined function layout, that is 41870: above the signed 16-bit boundary but below 65536. This supports investigating signedness, although that stack value is not a direct measurement of the tracking vector’s Count/Capacity and is not established as an operand of the malformed record.
This gives a plausible connection to large or complex maps: if subzone generation assigns sufficiently large IDs, affected connections can cross the signed 16-bit boundary. The supplied maps have Size=0,0,304,208, so their width plus height is indeed 512. However, this defect does not establish 512 as an exact map-size threshold; the relevant condition is the ID assigned to an endpoint processed by the affected packing code.
The intermittent behavior could also be consistent with an invalid array access landing in differently mapped or allocated memory. That remains an explanation to test, not something established by this single log. The delayed crashes and RA2/YR differences have not been traced to this mechanism.
There are two remaining attribution limits:
The log identifies gamemd-spawn.exe, while the database examined is gamemd.exe. Their complete binary equivalence has not been established.
No runtime trace has yet captured the malformed connection being written by this producer and subsequently consumed at the reported crash.
The smallest useful confirmation would be to trace the connection-record writes around 0x583012, record both original 16-bit endpoint IDs, and follow the resulting packed value into the consumer at 0x582395.
The packing operation should preserve both endpoints as independent unsigned 16-bit fields. A bounds check at the consumer would provide additional protection, but would not by itself restore a connection whose high endpoint has already been overwritten. No executable patch or runtime fix has been applied or tested during this analysis.
Activity
Starkku commentedon May 24, 2026
Crash report but not a single crash log of any kind?
CCCP84 commentedon May 25, 2026
what kind of crash log is needed? only the syringe.log file is created after error.
syringe.zip
Starkku commentedon May 25, 2026
Crashes trying to clear map sub-zone tracking vectors on scenario init. It is possible that large enough maps can cause overflows somewhere which result in crashes - even if it works most of the time.
WLChara commentedon Jun 7, 2026
Well that's because of the hardcoded size-limit of game itself.
In MapClass,there is a member called "VectorClass<CellClass*> Cells;" , it's hardcoded to be 262144 DWORD size,can only storage 512512 cell of map.When the game wanna access a Cell with CellStruct,it will count the index of the Cell at this VectorClass array.
Although 512512 cell shouldn't crash the map,but as we all know, WWSB (lmao),try create 511*511 size map or use un-hardcoded this(for example : create CellsMapExt,hook all codes that references the origin Cells in the game)
WLChara commentedon Jun 7, 2026
And some operation of the vanilla game will cause some undefined access,like : try get cell of (128,129) in a 128 x 128 size map,in origin logic,the game will found that (128,129) storages 00000000,comparing the value and skip.But when it turns to 512x512 : it might lead a read access to an undefined memory area,and crash the game.That might be the reason of this.
CCCP84 commentedon Jun 7, 2026
We're talking about 256x256 maps here, but smaller sizes also produce errors on some startups.
WLChara commentedon Oct 2, 2026
Static analysis points to a signed-extension bug when packing subzone connection IDs, which closely matches the values in the attached crash log. The instruction-level defect is confirmed in the executable examined; its attribution to this particular crash still needs runtime confirmation.
The addresses below refer to a 32-bit Yuri’s Revenge
gamemd.exedatabase with image base0x400000. Descriptive function names are reconstructed analysis names, not original debug symbols.The [attached crash log](#2219 (comment)) reports:
Exception: C0000005
EIP: 0058241B
Read from: 2135D008
EAX: 0008FFF7
ECX: 2111D024
EBP: 0000FFFF
EDI: 0000812C
ESI: 2135D000
In the examined executable,
0x58241Bis inside the connection-processing portion of the subzone rebuilding routine at0x581F90. More specifically, this instruction accesses a subzone entry while constructing its adjacency information. The faulting instruction itself is not a vector destructor or clearing operation.A connection record contains two 16-bit endpoint IDs packed into one 32-bit value:
bits 31..16: high endpoint ID
bits 15..0: low endpoint ID
The consumer extracts these IDs as unsigned values and uses them to index the subzone tracking array. Each subzone entry occupies
0x24bytes, or 36 bytes.At the crash, the high endpoint is
0xFFFF, and the array base is0x2111D024. The resulting access is:entry address = 0x2111D024 + 36 * 0xFFFF
= 0x2135D000
field address = 0x2135D000 + 8
= 0x2135D008
This exactly matches the invalid address recorded in the log. The actual instruction is:
0058241B mov eax, [ecx+eax*4+8]
At that point,
EAX = 9 * 0xFFFF = 0x0008FFF7, producing the same address calculation.The likely origin of the
0xFFFFendpoint is in the connection-record producer at0x582D70. One relevant packing sequence is:00582FDD movsx esi, word ptr [ecx+eax2] ; endpoint for high half
00583006 movsx ecx, word ptr [edx+eax2] ; endpoint for low half
0058300A mov eax, esi
0058300F shl eax, 10h
00583012 or eax, ecx
The problem is the sign extension of the low-half endpoint before the OR.
For example, consider the low endpoint observed in the crash:
16-bit ID: 0x812C = 33068
After MOVSX to 32 bits: 0xFFFF812C
MOVSXtreats bit 15 as a sign bit and copies it into the upper 16 bits. Therefore, any low endpoint in the range0x8000..0xFFFFproduces an extended value whose upper half is all ones.Suppose the intended high endpoint were
0x1234. Correct packing would produce:high endpoint << 16: 0x12340000
zero-extended low: 0x0000812C
OR result: 0x1234812C
The observed instruction sequence instead produces:
high endpoint << 16: 0x12340000
sign-extended low: 0xFFFF812C
OR result: 0xFFFF812C
The low endpoint remains
0x812C, but its sign-extension bits overwrite the entire high endpoint with0xFFFF. This happens regardless of the intended high endpoint’s value.The consumer then decodes:
low endpoint = 0x812C
high endpoint = 0xFFFF
Those are precisely the endpoint values indicated by
EDIandEBPin the crash log. The consumer does not validate the decoded high endpoint against the outer tracking vector’s count before the access at0x58241B.A second instance of the same producer pattern is present at
0x5830A0→0x5830AC. Consequently, this should be reviewed as a connection-packing issue across the relevant producer paths, rather than treating only the final invalid access as the defect.The important threshold for this mechanism is 32768 in an endpoint ID, not 65536 subzones. An actual counter wrap to
0xFFFFis unnecessary: sign extension can manufacture that endpoint from an otherwise representable 16-bit ID such as0x812C.The crash stack also contains
0xA38Eat the location corresponding to the rebuilding routine’s local subzone sequence counter. Interpreted using the examined function layout, that is 41870: above the signed 16-bit boundary but below 65536. This supports investigating signedness, although that stack value is not a direct measurement of the tracking vector’s Count/Capacity and is not established as an operand of the malformed record.This gives a plausible connection to large or complex maps: if subzone generation assigns sufficiently large IDs, affected connections can cross the signed 16-bit boundary. The supplied maps have
Size=0,0,304,208, so their width plus height is indeed 512. However, this defect does not establish 512 as an exact map-size threshold; the relevant condition is the ID assigned to an endpoint processed by the affected packing code.The intermittent behavior could also be consistent with an invalid array access landing in differently mapped or allocated memory. That remains an explanation to test, not something established by this single log. The delayed crashes and RA2/YR differences have not been traced to this mechanism.
There are two remaining attribution limits:
gamemd-spawn.exe, while the database examined isgamemd.exe. Their complete binary equivalence has not been established.The smallest useful confirmation would be to trace the connection-record writes around
0x583012, record both original 16-bit endpoint IDs, and follow the resulting packed value into the consumer at0x582395.The packing operation should preserve both endpoints as independent unsigned 16-bit fields. A bounds check at the consumer would provide additional protection, but would not by itself restore a connection whose high endpoint has already been overwritten. No executable patch or runtime fix has been applied or tested during this analysis.