Skip to content

fix(worldbuilder): Handle nulls in boundary lookup and object editing - #3383

Open
OmarAglan wants to merge 3 commits into
TheSuperHackers:mainfrom
OmarAglan:bugfix/worldbuilder-null-boundary-handle
Open

OmarAglan wants to merge 3 commits into
TheSuperHackers:mainfrom
OmarAglan:bugfix/worldbuilder-null-boundary-handle

Conversation

@OmarAglan

@OmarAglan OmarAglan commented Sep 28, 2026 •

Copy link
Copy Markdown

Follow-up to #3368.

Fixes null handling and object editing paths in Generals and Zero Hour WorldBuilder:

  • Allow findBoundaryNear() to receive a null output handle when no boundary matches.
  • Keep duplicate-road traversal within the object list and validate both road endpoints.
  • Handle removeFirstObject() on an empty list.
  • Initialize the object picker leaf name so a null template adds nothing.
  • Allow undoing replacement of an object without a template, restoring its original template and name.

The Generals and Zero Hour implementations remain aligned.

Validation:

  • Passed focused regression fixtures covering boundary lookup, incomplete road pairs, empty lists, null templates, and replacement undo/redo.
  • Passed VC6 compilation checks for affected translation units in both games, Debug/Release, with RETAIL_COMPATIBLE_DATA enabled and disabled.

Code changes and local regression fixtures were generated with Codex.

@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 260b183a-dc2e-4a60-9ed4-df68a662ebac


📥 Commits

Reviewing files that changed from the base of the PR and between 41e4e15 and 16fe4d2.


📒 Files selected for processing (1)
  • Generals/Code/Tools/WorldBuilder/include/WHeightMapEdit.h

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.



Walkthrough

WorldBuilder changes flip-state sizing and blend-tile serialization, adjusts image and blend handling, removes the raw tile data API, and updates boundary editing and lookup.

Changes

Height-Map Data Handling

Layer / File(s) Summary
Size and serialize flip state
Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp, GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
Flip-state row allocation uses rounded-up byte widths. Blend-tile output selects version 7 for the specified Generals retail build and version 8 otherwise, with corresponding cliff-state serialization.
Update blend, image, and raw-tile handling
Generals/Code/Tools/WorldBuilder/include/WHeightMapEdit.h, Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
Image loading copies the filename into the file buffer. Blend flipping uses a copied blend description. The getRawTileData declaration and implementation are removed. Comments and include spacing are also edited.

Boundary Editing and Lookup

Layer / File(s) Summary
Handle boundary selection and insertion
Generals/Code/Tools/WorldBuilder/src/BorderTool.cpp, Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp, GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
BorderTool adds a boundary when no nearby boundary is found. findBoundaryNear sets the no-match handle only when the output pointer is non-null.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: 🟡 Moderate · up to 16fe4

Terrain blending can leave an invalid index when the blend table fills, risking unsafe memory access during later editing. Guard the failed remap before merging; the API-removal concern is resolved.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 16fe4

The terrain-blending change can store a failed allocation result as a valid blend index, exposing later editor operations to out-of-bounds memory access. The demonstrated scope is local WorldBuilder editing. The shared map loader repairs invalid indices, which limits propagation through saved maps.

Retained concerns

  • Medium · security · observed: The new copied-primary blend transition commits the secondary blend before checking whether replacement of the primary succeeds. Blend-table exhaustion can therefore store -1 as the primary identity, which later editor consumers use as an array index. This violates the shared per-cell index invariant and defeats failure containment within the editing lifecycle.

Security review details

Security Blast Radius

  • inferred — The demonstrated attackable scope requires control over local terrain-editing operations with three-way blending enabled. The affected assets are the live Generals WorldBuilder heightmap and potentially its saved blend data. Remote execution, privilege gain, and cross-service exposure are not established; the inspected shared loader repairs invalid indices.

Security Findings and Attack Paths

  • inferred — If a secondary blend succeeds but the required flipped-primary descriptor cannot be allocated, the changed transition stores -1 as the primary index. A subsequent blend accepts that nonzero value and reads outside m_blendedTiles. Blend-table exhaustion is an explicit failure condition; an executable trigger sequence and exploit consequences were not dynamically demonstrated.

Trust Boundaries and Controls

  • observed — The inspected persistence boundary has two distinct controls: version-aware row decoding preserves format compatibility, and post-parse index normalization protects loaded maps. Neither control protects the live editor immediately after the failed remapping transition.

Pre-merge checks | Passed 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly identifies the boundary lookup null-handling fix and related object-editing changes.
Description check Passed The description is related to the reported WorldBuilder null-handling and object-editing changes, and it includes validation details.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: b7d8f3e0-b520-425e-9af7-399518604f30

📥 Commits

Reviewing files that changed from the base of the PR and between b687c07 and 16019dd.

📒 Files selected for processing (13)
  • Core/Tools/CMakeLists.txt
  • Core/Tools/WorldBuilder/CMakeLists.txt
  • Core/Tools/WorldBuilder/include/BorderTool.h
  • Core/Tools/WorldBuilder/include/WHeightMapEdit.h
  • Core/Tools/WorldBuilder/src/BorderTool.cpp
  • Core/Tools/WorldBuilder/src/WHeightMapEdit.cpp
  • Generals/Code/Tools/WorldBuilder/CMakeLists.txt
  • Generals/Code/Tools/WorldBuilder/include/BorderTool.h
  • Generals/Code/Tools/WorldBuilder/include/WHeightMapEdit.h
  • Generals/Code/Tools/WorldBuilder/src/BorderTool.cpp
  • Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/CMakeLists.txt
  • scripts/cpp/unify_move_files.py
💤 Files with no reviewable changes (3)
  • Generals/Code/Tools/WorldBuilder/include/BorderTool.h
  • Generals/Code/Tools/WorldBuilder/include/WHeightMapEdit.h
  • Generals/Code/Tools/WorldBuilder/src/BorderTool.cpp

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp Outdated
@OmarAglan
OmarAglan force-pushed the bugfix/worldbuilder-null-boundary-handle branch from 16019dd to 41e4e15 Compare September 30, 2026 08:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: e8f4db5c-e63f-4bae-add6-3392b599ac21

📥 Commits

Reviewing files that changed from the base of the PR and between 16019dd and 41e4e15.

📒 Files selected for processing (4)
  • Generals/Code/Tools/WorldBuilder/include/WHeightMapEdit.h
  • Generals/Code/Tools/WorldBuilder/src/BorderTool.cpp
  • Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

Comment thread Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
@OmarAglan
OmarAglan force-pushed the bugfix/worldbuilder-null-boundary-handle branch from 41e4e15 to 16fe4d2 Compare September 30, 2026 08:36
@OmarAglan
OmarAglan marked this pull request as ready for review October 4, 2026 08:26
m_blendedTiles[m_blendTileNdxes[ndx]].inverted |= FLIPPED_MASK;
{
//Find a new tile so as not to affect other cells using the base one.
TBlendTileInfo tempBlendTileInfo=m_blendedTiles[m_blendTileNdxes[ndx]];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High src/WHeightMapEdit.cpp:1192

When findOrCreateBlendTile returns -1 because the blend-tile table is full or the source tile is unavailable, this path overwrites the valid base index in m_blendTileNdxes[ndx] with -1; subsequent blend/render lookups then index out of bounds and can crash WorldBuilder. Check the allocation result before remapping the cell.

🤖 Copy this AI Prompt to have your agent fix this:
In file @Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp around line 1192:

When `findOrCreateBlendTile` returns `-1` because the blend-tile table is full or the source tile is unavailable, this path overwrites the valid base index in `m_blendTileNdxes[ndx]` with `-1`; subsequent blend/render lookups then index out of bounds and can crash WorldBuilder. Check the allocation result before remapping the cell.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed separately in #3384. Both required blend records are obtained before changing the cell. If allocation fails, the original cell remains unchanged and any newly allocated unused record is reclaimed by restoring the blend-table count. #3384 now contains only that fix on current main; this PR remains focused on the nullable boundary output.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

@xezon

xezon commented Oct 4, 2026

Copy link
Copy Markdown

Needs rebase.

@greptile-apps

greptile-apps Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium impact] This PR appears safe to merge; the earlier accidental-boundary undo issue remains non-blocking.

Findings

  1. P2 Accidental boundaries cannot be undone ▶

Summary

This PR fixes null handling and object editing in both WorldBuilder variants.

  • Object edits keep the right name when a template is missing.
  • Road duplicate checks compare complete endpoint pairs.
  • Boundary lookup can report a miss without a handle output.
  • Removing the first object safely handles an empty list.

No new actionable issues were found. OmarAglan stated that the earlier blend-index issue is being fixed separately in #3384; that change is outside this PR.

Reviews (7) · Last reviewed commit: "fix(worldbuilder): Use MoveInfo names as..." · Reviewed by Greptile

Comment thread Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
Comment on lines +149 to +155
else if (motion == -1)
{
// add a boundary
m_addingNewBorder = true;

ICoord2D initialBoundary = { 1, 1 };
pDoc->addBoundary(&initialBoundary);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Accidental boundaries cannot be undone A click that misses every boundary now adds a {1,1} boundary, even if the user releases without dragging. Mouse-up does not register an undo action or discard that boundary, so an accidental miss leaves an unwanted boundary in the map. Make creation undoable or discard an uncompleted boundary.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will push pr fixing this

@OmarAglan
OmarAglan force-pushed the bugfix/worldbuilder-null-boundary-handle branch from 16fe4d2 to 1bb7eff Compare October 4, 2026 08:34
@OmarAglan
OmarAglan force-pushed the bugfix/worldbuilder-null-boundary-handle branch from 1bb7eff to bf1b9b6 Compare October 9, 2026 15:47
@OmarAglan

Copy link
Copy Markdown
Author

Needs rebase.

Rebased

@xezon xezon changed the title bugfix(worldbuilder): Handle null boundary lookup output fix(wheightmapedit): Handle null output handle in WorldHeightMapEdit::findBoundaryNear() Oct 10, 2026
xezon
xezon previously approved these changes Oct 10, 2026

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is not practical issue because no caller passes null, but ok to fix. Will remove complaints from bots. Are there maybe some more untested nulls in this file or related files that want to be tested? I am asking because it is better to bunch 20 together than create 20 commits for 20 null tests.

@xezon xezon added Minor Severity: Minor < Major < Critical < Blocker WorldBuilder Relates to World Builder Fix Is fixing something, but is not user facing labels Oct 10, 2026
@OmarAglan

Copy link
Copy Markdown
Author

Is not practical issue because no caller passes null, but ok to fix. Will remove complaints from bots. Are there maybe some more untested nulls in this file or related files that want to be tested? I am asking because it is better to bunch 20 together than create 20 commits for 20 null tests.

@xezon i did expanded audit across both WorldBuilder versions and the shared heightmap code
there is (probably) 2 additional null dereference paths

  • selectDuplicates() can advance past its stopping point with an incomplete road pair and eventually dereference null.

  • Replacing an object that originally has no ThingTemplate, then undoing, passes null to MapObject::setThingTemplate().

did a test and both reproduce the extracted production functions for gen and zh

there is also two defensive candidates

  • EditObjectParameter::addObject() leaves leafName uninitialized for null input, well i can tell the its current caller excludes null

  • removeFirstObject() assumes a nonempty list but currently has no callers

i can expand this pr if you want or make a follow up pr group these findings

@xezon

xezon commented Oct 10, 2026

Copy link
Copy Markdown

You can append here and then we take a look at them.

@OmarAglan

Copy link
Copy Markdown
Author

You can append here and then we take a look at them.

done

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change title and description need update.

Comment thread Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp Outdated
Comment thread Generals/Code/Tools/WorldBuilder/src/CUndoable.cpp Outdated
@OmarAglan
OmarAglan force-pushed the bugfix/worldbuilder-null-boundary-handle branch from 3c6a603 to 467e43c Compare October 11, 2026 08:56
@OmarAglan OmarAglan changed the title fix(wheightmapedit): Handle null output handle in WorldHeightMapEdit::findBoundaryNear() fix(worldbuilder): Handle nulls in boundary lookup and object editing Oct 11, 2026
m_objectToModify->setThingTemplate(m_oldThing);
}
if (m_newName != m_oldName) {
if (m_newName != m_oldName || m_newThing != m_oldThing) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You wrote:

"UndoMove()" still needs it, as the restoring of the old template can (tested) overwrite the saved object name even when "m_newName == m_oldName"

It is confusing. I think what makes this complicated is there are 2 sources for a name. Can we maybe simplify this logic so that there is only ever one source of a name?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make the names stored in MoveInfo the only source. SetThingTemplate already keeps m_newName in sync and the constructor captures m_oldName, so DoMove and UndoMove can call setName unconditionally after the template call. Claude came up with this:


if (m_newThing != m_oldThing) {
  m_objectToModify->setThingTemplate(m_newThing);
}
m_objectToModify->setName(m_newName);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i applied @bobtista suggestion, so right now the names stored in MoveInfo are now the only source for the object name. DoMove and UndoMove change the template only if it differs, then always call setName with m_newName / m_oldName. The || m_newThing != m_oldThing condition is gone.

MoveInfo::SetThingTemplate still copies the new template name into m_newName, so a replace picks up the new name and undo restores the original one, including for objects that had no template

MapObject::setThingTemplate still writes the name as a side effect

the other callers already call setName themselves, so that could be removed, but it is shared Core code, so I left it out of this PR. I can do it as a follow up if we want that

@xezon
xezon dismissed their stale review October 11, 2026 18:53

Code has changed.

… names

MoveInfo::DoMove and MoveInfo::UndoMove now always apply the name stored
in MoveInfo after the optional template change, instead of deciding with
a compound condition whether the name written by
MapObject::setThingTemplate needs to be overwritten.
@OmarAglan
OmarAglan force-pushed the bugfix/worldbuilder-null-boundary-handle branch from 72126d7 to 8c7652b Compare October 12, 2026 21:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Fix Is fixing something, but is not user facing Minor Severity: Minor < Major < Critical < Blocker WorldBuilder Relates to World Builder

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants