[Bugfix] bad-free in GridData::computeFOV / ~GridData via ColorLayer.draw_fov (ASan) #321

Open
opened 2026-06-21 20:41:23 +00:00 by john · 0 comments
Owner

Summary

AddressSanitizer reports a bad-free / mismatched-ownership double-free in the FOV map managed by GridData. The FOV map is allocated with malloc and freed with free inside GridData::computeFOV, but GridData::~GridData later calls operator delete on a pointer that was already freed (or a TCOD-owned pointer), corrupting the heap.

Found by the #312 fuzz campaign (fuzz_fov target, new ColorLayer.draw_fov Tier C op). Severity: HIGH (memory corruption, not just UB).

ASan output (abridged)

==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: ... in thread T0
    #0 operator delete(void*)
    #7 GridData::~GridData()                         src/GridData.cpp:59
    #8 UIGrid::~UIGrid()                             src/UIGrid.cpp:447
    #12 UIGridView::~UIGridView()                    src/UIGridView.cpp:32
    #17 mcrfpydef::PyUIGridViewType::lambda(_object*) src/UIGridView.h:153

freed by thread T0 here:
    #0 free
    #4 GridData::computeFOV(...)                     src/GridData.cpp:155
    #5 ColorLayer::drawFOV(...)                      src/GridLayers.cpp:316
    #6 PyGridLayerAPI::ColorLayer_draw_fov(...)      src/GridLayers.cpp:1272

previously allocated by thread T0 here:
    #0 malloc
    #4 GridData::computeFOV(...)                     src/GridData.cpp:155
    #5 ColorLayer::drawFOV(...)                      src/GridLayers.cpp:316
    #6 PyGridLayerAPI::ColorLayer_draw_fov(...)      src/GridLayers.cpp:1272

SUMMARY: AddressSanitizer: bad-free in operator delete(void*)

Analysis

GridData::computeFOV (src/GridData.cpp:155) both allocates and frees a buffer (TCOD FOV map) with C malloc/free. The same/related pointer is then operator delete-d in GridData::~GridData (src/GridData.cpp:59). Either the destructor double-frees a pointer computeFOV already released, or it deletes memory that was malloc-ed (mismatched allocator). The draw_fov path makes the lifetime issue observable.

Repro

This one is iteration-state-dependent — single-input replay does not reliably trigger it; re-run the campaign:

make fuzz-build      # requires symbol-complete __lib_debug/libtcod.so (see #312 notes)
make fuzz-long TARGET=fov SECONDS=60

Minimized crashing input (base64), for reference:

Qyfn5+fn52fn5+f7//////z/s7M=

Suggested labels

Bugfix, system:grid, priority:tier1-active (apply via web — MCP label bug)

Related: #312 (fuzz coverage that found this), #283.

## Summary AddressSanitizer reports a **bad-free / mismatched-ownership double-free** in the FOV map managed by `GridData`. The FOV map is allocated with `malloc` and freed with `free` inside `GridData::computeFOV`, but `GridData::~GridData` later calls `operator delete` on a pointer that was already freed (or a TCOD-owned pointer), corrupting the heap. Found by the #312 fuzz campaign (`fuzz_fov` target, new `ColorLayer.draw_fov` Tier C op). Severity: **HIGH** (memory corruption, not just UB). ## ASan output (abridged) ``` ==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: ... in thread T0 #0 operator delete(void*) #7 GridData::~GridData() src/GridData.cpp:59 #8 UIGrid::~UIGrid() src/UIGrid.cpp:447 #12 UIGridView::~UIGridView() src/UIGridView.cpp:32 #17 mcrfpydef::PyUIGridViewType::lambda(_object*) src/UIGridView.h:153 freed by thread T0 here: #0 free #4 GridData::computeFOV(...) src/GridData.cpp:155 #5 ColorLayer::drawFOV(...) src/GridLayers.cpp:316 #6 PyGridLayerAPI::ColorLayer_draw_fov(...) src/GridLayers.cpp:1272 previously allocated by thread T0 here: #0 malloc #4 GridData::computeFOV(...) src/GridData.cpp:155 #5 ColorLayer::drawFOV(...) src/GridLayers.cpp:316 #6 PyGridLayerAPI::ColorLayer_draw_fov(...) src/GridLayers.cpp:1272 SUMMARY: AddressSanitizer: bad-free in operator delete(void*) ``` ## Analysis `GridData::computeFOV` (src/GridData.cpp:155) both allocates and frees a buffer (TCOD FOV map) with C `malloc`/`free`. The same/related pointer is then `operator delete`-d in `GridData::~GridData` (src/GridData.cpp:59). Either the destructor double-frees a pointer `computeFOV` already released, or it `delete`s memory that was `malloc`-ed (mismatched allocator). The `draw_fov` path makes the lifetime issue observable. ## Repro This one is **iteration-state-dependent** — single-input replay does not reliably trigger it; re-run the campaign: ```sh make fuzz-build # requires symbol-complete __lib_debug/libtcod.so (see #312 notes) make fuzz-long TARGET=fov SECONDS=60 ``` Minimized crashing input (base64), for reference: ``` Qyfn5+fn52fn5+f7//////z/s7M= ``` ## Suggested labels `Bugfix`, `system:grid`, `priority:tier1-active` (apply via web — MCP label bug) Related: #312 (fuzz coverage that found this), #283.
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#321
No description provided.