Fuzzes grid.get_dijkstra_map with random roots/diagonal_cost/collide,
DijkstraMap.distance/path_from/step_from/to_heightmap queries, and
grid.step() with entity behavior callbacks that mutate the entity
list mid-iteration (adjacent to #273).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fuzzes procgen through its standardized data-container interface: direct
HeightMap and DiscreteMap operations (scalar, binary, bitwise, subscript)
plus one-directional conversions NoiseSource.sample -> HeightMap,
BSP.to_heightmap, DiscreteMap.from_heightmap, and dm.to_heightmap.
Treating HM/DM as the unified surface covers every procgen system
without fuzzing each individually.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Targets #269 (PythonObjectCache race), #270 (GridLayer dangling parent),
#275 (UIEntity missing tp_dealloc), #277 (GridChunk dangling parent).
Exercises timer/animation callbacks that mutate scene and drawable
lifetimes across firing boundaries, including scene swap mid-callback
and closure captures that can survive past their target's lifetime.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Targets #267 (PyObject_GetAttrString reference leaks), #268 (sfVector2f
NULL deref), #272 (UniformCollection weak_ptr). Exercises every exposed
property on Frame/Caption/Sprite/Grid/Entity/TileLayer/ColorLayer/Color/
Vector with both correct-type and deliberately-wrong-type values, plus
hot-loop repeated GetAttrString to stress refcount sites.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Random compute_fov/is_in_fov exercises with varying origin, radius,
light_walls, algorithm params including out-of-bounds origins and
extreme radii. Toggles grid.at(x,y).transparent between computes to
stress stale fov-state bugs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Targets #258-#263 (gridstate overflow on entity transfer between
differently-sized grids), #273 (entity.die during iteration), #274
(spatial hash on set_grid). Dispatches 13 operations driven by
ByteStream from fuzz_common.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pivots away from atheris (which lacks Python 3.14 support) to a single
libFuzzer-linked executable that embeds CPython, registers mcrfpy, and
dispatches each iteration to a Python fuzz_one_input(data: bytes) function
loaded from tests/fuzz/fuzz_<target>.py by MCRF_FUZZ_TARGET env var.
libFuzzer instruments the C++ engine code where all #258-#278 bugs live;
Python drives the fuzzing logic via an in-house ByteStream replacement
for atheris.FuzzedDataProvider. Python-level exceptions are caught; only
ASan/UBSan signal real bugs.
CMake
- MCRF_FUZZER=ON builds mcrfpy_fuzz from all src/*.cpp except main.cpp
plus tests/fuzz/fuzz_common.cpp, linked with -fsanitize=fuzzer,address,
undefined. Asset+lib post-build copy added so the embedded interpreter
finds its stdlib and default_font/default_texture load.
Makefile
- fuzz-build builds only mcrfpy_fuzz (fast iterate)
- fuzz loops over six targets setting MCRF_FUZZ_TARGET for each
- fuzz-long TARGET=x SECONDS=n for deep manual runs
- fuzz-repro TARGET=x CRASH=path for crash reproduction
- Shared ASAN_OPTIONS / PYTHONHOME env via FUZZ_ENV define
tests/fuzz
- fuzz_common.cpp: LLVMFuzzerInitialize bootstraps Python, imports target,
resolves fuzz_one_input. LLVMFuzzerTestOneInput wraps bytes as PyBytes,
calls target, swallows Python errors.
- fuzz_common.py: ByteStream byte consumer + safe_reset() + EXPECTED_EXCEPTIONS
- Six target stubs (grid_entity, property_types, anim_timer_scene,
maps_procgen, fov, pathfinding_behavior) to be fleshed out in follow-up
- README with build/run/triage instructions
Verified end-to-end: make fuzz-build produces build-fuzz/mcrfpy_fuzz,
make fuzz FUZZ_SECONDS=3 ran all six targets (~2400-9800 exec/s each,
667-1883 coverage edges), make fuzz-repro loaded and replayed a corpus
input cleanly. No crashes from the stubs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>