chore(garrisoncontain): Add assert for condition index in GarrisonContain::findClosestFreeGarrisonPointIndex() - #3357
Conversation
…rrison and return `GARRISON_INDEX_INVALID`
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. WalkthroughBoth game variants now compile the ChangesGarrison assertion build guard
Priority: ⬇️ Low Estimated code review effort: 1 (Trivial) | ~5 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to The change adds a guarded diagnostic without altering supported gameplay behavior. No actionable merge-blocking risk is established. Architecture SummaryArchitecture risk: 🔵 Low · up to The change affects 2 systems. Changed systems: Architecture concerns Review detailsSystems and components
Before / after behavior
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
|
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a narrowly scoped defensive fix in the Generals and GeneralsMD garrison implementations. It prevents invalid condition indices from reaching a fixed-size array while preserving existing behavior for valid callers. You can add or adjust custom eligibility rules. Learn more. |
|
|
||
| // sanity | ||
| if( targetPos == nullptr || m_garrisonPointsInUse == MAX_GARRISON_POINTS ) | ||
| if (targetPos == nullptr || m_garrisonPointsInUse == MAX_GARRISON_POINTS || |
There was a problem hiding this comment.
An assert would be better here.
conditionIndex is determined by GarrisonContain::findConditionIndex(). and only has an out of bound value due to a coding issue, not a runtime issue.
| // sanity | ||
| if( targetPos == nullptr || m_garrisonPointsInUse == MAX_GARRISON_POINTS ) | ||
| if (targetPos == nullptr || m_garrisonPointsInUse == MAX_GARRISON_POINTS || | ||
| conditionIndex < 0 || conditionIndex >= MAX_GARRISON_POINT_CONDITIONS) |
There was a problem hiding this comment.
This can never be false on runtime. The function is protected and all callers pass an already validated conditionIndex. Assert.
Of course the review bots are happy anyway...
| DEBUG_ASSERTCRASH(conditionIndex >= 0 && conditionIndex < MAX_GARRISON_POINT_CONDITIONS, | ||
| ("GarrisonContain::findClosestFreeGarrisonPointIndex - Invalid condition index '%d'", conditionIndex)); |
There was a problem hiding this comment.
🟠 High Contain/GarrisonContain.cpp:98
In release builds, an invalid conditionIndex still reaches m_garrisonPoint[conditionIndex][i], causing an out-of-bounds read because DEBUG_ASSERTCRASH expands to a no-op. Add a runtime guard that returns GARRISON_INDEX_INVALID after the assertion.
| DEBUG_ASSERTCRASH(conditionIndex >= 0 && conditionIndex < MAX_GARRISON_POINT_CONDITIONS, | |
| ("GarrisonContain::findClosestFreeGarrisonPointIndex - Invalid condition index '%d'", conditionIndex)); | |
| DEBUG_ASSERTCRASH(conditionIndex >= 0 && conditionIndex < MAX_GARRISON_POINT_CONDITIONS, | |
| ("GarrisonContain::findClosestFreeGarrisonPointIndex - Invalid condition index '%d'", conditionIndex)); | |
| if( conditionIndex < 0 || conditionIndex >= MAX_GARRISON_POINT_CONDITIONS ) | |
| return GARRISON_INDEX_INVALID; |
Also found in 1 other location(s)
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/GarrisonContain.cpp:99
The new check is only a
DEBUG_ASSERTCRASH; in release buildsDEBUG_ASSERTCRASHexpands to((void)0), so an invalidconditionIndexstill reachesm_garrisonPoint[conditionIndex][i]at line 117 and performs the out-of-bounds read this change is intended to prevent. The condition must be handled with a real runtime guard that returnsGARRISON_INDEX_INVALID.
🤖 Copy this AI Prompt to have your agent fix this:
In file @Generals/Code/GameEngine/Source/GameLogic/Object/Contain/GarrisonContain.cpp around lines 98-99:
In release builds, an invalid `conditionIndex` still reaches `m_garrisonPoint[conditionIndex][i]`, causing an out-of-bounds read because `DEBUG_ASSERTCRASH` expands to a no-op. Add a runtime guard that returns `GARRISON_INDEX_INVALID` after the assertion.
Also found in 1 other location(s):
- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/GarrisonContain.cpp:99 -- The new check is only a `DEBUG_ASSERTCRASH`; in release builds `DEBUG_ASSERTCRASH` expands to `((void)0)`, so an invalid `conditionIndex` still reaches `m_garrisonPoint[conditionIndex][i]` at line 117 and performs the out-of-bounds read this change is intended to prevent. The condition must be handled with a real runtime guard that returns `GARRISON_INDEX_INVALID`.
There was a problem hiding this comment.
The runtime guard was deliberately replaced with an assertion following xezon’s and Skyaero42’s reviews. They confirmed that callers supply valid condition indices and that an invalid index indicates a programming error. The assertion documents that invariant.
There was a problem hiding this comment.
Sorry, I'm unable to act on this request because you do not have permissions within this repository.
There was a problem hiding this comment.
The assertion does document the invariant, but it compiles out in retail. findConditionIndex() can return GARRISON_INDEX_INVALID for an unrecognized BodyDamageType, so that path still indexes m_garrisonPoint out of bounds. The runtime guard remains necessary.
| #endif | ||
|
|
||
| // TheSuperHackers @bugfix CryoTheRenegade 06/09/2026 Assert that the condition index supplied by findConditionIndex is valid. | ||
| DEBUG_ASSERTCRASH(conditionIndex >= 0 && conditionIndex < MAX_GARRISON_POINT_CONDITIONS, |
There was a problem hiding this comment.
I think you need to move this into the define block above, because
9/10 Simulating Replay "18-13-02_3v3_Supremac_Loonen_JB_HardAI_HardAI_HardAI.rep"
CRC Mismatch in Frame 6910
| #if defined(RTS_DEBUG) || !RETAIL_COMPATIBLE_CRC | ||
| DEBUG_ASSERTCRASH(m_garrisonPointsInitialized, ("garrisonPoints are not inited")); | ||
|
|
||
| // TheSuperHackers @bugfix CryoTheRenegade 06/09/2026 Assert that the condition index supplied by findConditionIndex is valid. |
There was a problem hiding this comment.
Superfluous comment. Is not a bugfix either.
findClosestFreeGarrisonPointIndex()previously usedconditionIndexto access the garrison point array without checking its bounds.My solution is to return
GARRISON_INDEX_INVALIDwhen the index is negative or at leastMAX_GARRISON_POINT_CONDITIONS. This prevents out-of-bounds reads and lets callers use their existing handling for unavailable garrison points.Found by clang-tidy
Note
Fix out-of-bounds access in
GarrisonContain.findClosestFreeGarrisonPointIndexfor invalid condition indicesAdds bounds checking for
conditionIndexin GarrisonContain.cpp, rejecting values below zero or at/above the maximum supported condition count. The same fix applies to the GeneralsMD copy at GarrisonContain.cpp. When the index is invalid, the method returnsGARRISON_INDEX_INVALIDwithout scanning garrison points.📊 Macroscope summarized 968a1cf. 2 files reviewed, 2 issues evaluated, 1 issue filtered, 1 comment posted
🗂️ Filtered Issues
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/GarrisonContain.cpp — 0 comments posted, 1 evaluated, 1 filtered
DEBUG_ASSERTCRASH; in release buildsDEBUG_ASSERTCRASHexpands to((void)0), so an invalidconditionIndexstill reachesm_garrisonPoint[conditionIndex][i]at line 117 and performs the out-of-bounds read this change is intended to prevent. The condition must be handled with a real runtime guard that returnsGARRISON_INDEX_INVALID. [ Cross-file consolidated ]