Skip to content

Return -1 instead of raw index when ConvertIndex lookup fails - #1234

Open
acato wants to merge 1 commit into
We-the-People-civ4col-mod:developfrom
acato:fix/savegame-convertindex-passthrough
Open

Return -1 instead of raw index when ConvertIndex lookup fails#1234
acato wants to merge 1 commit into
We-the-People-civ4col-mod:developfrom
acato:fix/savegame-convertindex-passthrough

Conversation

@acato

@acato acato commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Changes ConvertIndex() to return -1 (the established "removed entry" sentinel) when a savegame enum index exceeds the conversion table size, instead of passing through the raw attacker-controlled index
  • Without this, an out-of-range index passes through to EnumMap::set() where the bounds check (FAssert) is stripped in Release builds, enabling out-of-bounds writes into game object memory

Fixes #1231

Test plan

  • Load an existing savegame — verify it loads identically to before
  • Load a savegame from an older version with different XML content — verify conversion still works correctly for valid indices
  • Save and reload mid-game — verify no data loss
  • Confirm the fix compiles cleanly in Assert, Release, and FinalRelease targets

🤖 Generated with Claude Code

When a savegame enum index exceeds the conversion table size,
the raw attacker-controlled index was passed through unchanged
to EnumMap::set(), where bounds checks (FAssert) are stripped in
Release builds. This enabled out-of-bounds writes into EnumMap
storage, corrupting adjacent fields in game objects.

Returning -1 (the established "removed entry" sentinel) causes
callers to safely skip the value instead of writing out-of-bounds.

Fixes We-the-People-civ4col-mod#1231

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Security: ConvertIndex passthrough enables out-of-bounds EnumMap writes via crafted savegame

1 participant