[Bugfix] EntityCollection.append() skips gridstate resize when entity has existing gridstate #258

Closed
opened 2026-03-07 23:19:12 +00:00 by john · 0 comments
Owner

Summary

UIEntityCollection::append() only initializes gridstate when gridstate.size() == 0. When an entity moves between grids of different sizes (e.g., 10×10 → 50×50), the existing gridstate keeps its old size. Subsequent updateVisibility() writes past the end of the undersized vector, causing heap corruption.

Root Cause

UIEntityCollection.cpp:582:

// Initialize gridstate if not already done
if (entity->data->gridstate.size() == 0 && self->grid) {
    entity->data->gridstate.resize(self->grid->grid_w * self->grid->grid_h);
    ...
}

The size() == 0 guard skips the resize when the entity already has gridstate from a previous grid. This is the same class of bug that was fixed in UIEntity::set_grid() — the fix there was to always resize to match the new grid dimensions.

Reproduction

import mcrfpy

small_grid = mcrfpy.Grid(grid_size=(10, 10))
large_grid = mcrfpy.Grid(grid_size=(50, 50))

entity = mcrfpy.Entity((5, 5))
small_grid.entities.append(entity)
entity.update_visibility()  # initializes gridstate to 100 entries

large_grid.entities.append(entity)  # BUG: gridstate stays at 100
entity.update_visibility()  # writes indices 0-2499, heap overflow at index 100+

Fix

Replace the size() == 0 guard with unconditional resize to match the new grid:

if (self->grid) {
    size_t new_size = self->grid->grid_w * self->grid->grid_h;
    if (entity->data->gridstate.size() != new_size) {
        entity->data->gridstate.resize(new_size);
        for (auto& state : entity->data->gridstate) {
            state.visible = false;
            state.discovered = false;
        }
    }
}

Severity

Critical — heap buffer overflow leading to nondeterministic crashes (segfault in PythonObjectCache rehash, UIFrame::render, etc.)

Same class of bug as the UIEntity::set_grid() fix applied during 7DRL 2026. See also bugs in .extend() and .insert() with the same pattern.

## Summary `UIEntityCollection::append()` only initializes `gridstate` when `gridstate.size() == 0`. When an entity moves between grids of different sizes (e.g., 10×10 → 50×50), the existing gridstate keeps its old size. Subsequent `updateVisibility()` writes past the end of the undersized vector, causing heap corruption. ## Root Cause `UIEntityCollection.cpp:582`: ```cpp // Initialize gridstate if not already done if (entity->data->gridstate.size() == 0 && self->grid) { entity->data->gridstate.resize(self->grid->grid_w * self->grid->grid_h); ... } ``` The `size() == 0` guard skips the resize when the entity already has gridstate from a previous grid. This is the same class of bug that was fixed in `UIEntity::set_grid()` — the fix there was to always resize to match the new grid dimensions. ## Reproduction ```python import mcrfpy small_grid = mcrfpy.Grid(grid_size=(10, 10)) large_grid = mcrfpy.Grid(grid_size=(50, 50)) entity = mcrfpy.Entity((5, 5)) small_grid.entities.append(entity) entity.update_visibility() # initializes gridstate to 100 entries large_grid.entities.append(entity) # BUG: gridstate stays at 100 entity.update_visibility() # writes indices 0-2499, heap overflow at index 100+ ``` ## Fix Replace the `size() == 0` guard with unconditional resize to match the new grid: ```cpp if (self->grid) { size_t new_size = self->grid->grid_w * self->grid->grid_h; if (entity->data->gridstate.size() != new_size) { entity->data->gridstate.resize(new_size); for (auto& state : entity->data->gridstate) { state.visible = false; state.discovered = false; } } } ``` ## Severity **Critical** — heap buffer overflow leading to nondeterministic crashes (segfault in PythonObjectCache rehash, UIFrame::render, etc.) ## Related Same class of bug as the `UIEntity::set_grid()` fix applied during 7DRL 2026. See also bugs in `.extend()` and `.insert()` with the same pattern.
john closed this issue 2026-03-14 06:25:15 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
john/McRogueFace#258
No description provided.