Skip to content

Non-deterministic crashes when loading very large maps (width+height=512) #2219

Open

Description

@CCCP84

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:

TEST MAPS.zip

Activity

  1. Starkku commented on May 24, 2026

    @Starkku
    Contributor

    Crash report but not a single crash log of any kind?

  2. CCCP84 commented on May 25, 2026

    @CCCP84
    Author

    what kind of crash log is needed? only the syringe.log file is created after error.

    syringe.zip

  3. Starkku commented on May 25, 2026

    @Starkku
    Contributor

    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.

  4. WLChara commented on Jun 7, 2026

    @WLChara

    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 512
    512 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)

  5. WLChara commented on Jun 7, 2026

    @WLChara

    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.

  6. CCCP84 commented on Jun 7, 2026

    @CCCP84
    Author

    We're talking about 256x256 maps here, but smaller sizes also produce errors on some startups.

  7. WLChara commented on Oct 2, 2026

    @WLChara

    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.

    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, 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:

    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 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+eax
    2] ; 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ❓Vanilla bugVanilla game bugs that are requested to be fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions