Add native libFuzzer fuzz harness for Python API, addresses #283
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>
This commit is contained in:
parent
1ce38b587b
commit
90a2945a9f
18 changed files with 602 additions and 18 deletions
|
|
@ -338,9 +338,57 @@ if(MCRF_FUZZER)
|
|||
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
message(FATAL_ERROR "MCRF_FUZZER=ON requires Clang. Invoke with CC=clang-18 CXX=clang++-18.")
|
||||
endif()
|
||||
message(STATUS "libFuzzer coverage instrumentation enabled (atheris harness)")
|
||||
target_compile_options(mcrogueface PRIVATE -fsanitize=fuzzer-no-link)
|
||||
target_link_options(mcrogueface PRIVATE -fsanitize=fuzzer-no-link)
|
||||
message(STATUS "Building mcrfpy_fuzz harness (libFuzzer + ASan + UBSan)")
|
||||
|
||||
set(MCRF_FUZZ_SOURCES ${SOURCES})
|
||||
list(REMOVE_ITEM MCRF_FUZZ_SOURCES ${CMAKE_SOURCE_DIR}/src/main.cpp)
|
||||
list(APPEND MCRF_FUZZ_SOURCES ${CMAKE_SOURCE_DIR}/tests/fuzz/fuzz_common.cpp)
|
||||
|
||||
add_executable(mcrfpy_fuzz ${MCRF_FUZZ_SOURCES})
|
||||
target_compile_definitions(mcrfpy_fuzz PRIVATE NO_SDL MCRF_FUZZ_HARNESS)
|
||||
if(MCRF_DEBUG_PYTHON OR MCRF_FREE_THREADED_PYTHON)
|
||||
target_compile_definitions(mcrfpy_fuzz PRIVATE Py_DEBUG)
|
||||
endif()
|
||||
if(MCRF_FREE_THREADED_PYTHON)
|
||||
target_compile_definitions(mcrfpy_fuzz PRIVATE Py_GIL_DISABLED)
|
||||
endif()
|
||||
if(MCRF_HEADLESS)
|
||||
target_compile_definitions(mcrfpy_fuzz PRIVATE MCRF_HEADLESS)
|
||||
endif()
|
||||
if(MCRF_SDL2)
|
||||
target_compile_definitions(mcrfpy_fuzz PRIVATE MCRF_SDL2)
|
||||
endif()
|
||||
target_include_directories(mcrfpy_fuzz PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/tests/fuzz)
|
||||
target_compile_options(mcrfpy_fuzz PRIVATE
|
||||
-fsanitize=fuzzer-no-link,address,undefined
|
||||
-fno-sanitize=function,vptr
|
||||
-fno-omit-frame-pointer -g -O1)
|
||||
target_link_options(mcrfpy_fuzz PRIVATE
|
||||
-fsanitize=fuzzer,address,undefined
|
||||
-fno-sanitize=function,vptr)
|
||||
target_link_libraries(mcrfpy_fuzz ${LINK_LIBS})
|
||||
|
||||
# Copy Python runtime + assets next to mcrfpy_fuzz so the embedded
|
||||
# interpreter finds the stdlib and default_font/default_texture load.
|
||||
add_custom_command(TARGET mcrfpy_fuzz POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/__lib $<TARGET_FILE_DIR:mcrfpy_fuzz>/lib
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/assets $<TARGET_FILE_DIR:mcrfpy_fuzz>/assets)
|
||||
if(MCRF_DEBUG_PYTHON)
|
||||
add_custom_command(TARGET mcrfpy_fuzz POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_SOURCE_DIR}/__lib_debug/libpython3.14.so.1.0
|
||||
$<TARGET_FILE_DIR:mcrfpy_fuzz>/lib/libpython3.14.so.1.0
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_SOURCE_DIR}/__lib_debug/libpython3.14.so.1.0
|
||||
$<TARGET_FILE_DIR:mcrfpy_fuzz>/lib/libpython3.14d.so.1.0
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
libpython3.14d.so.1.0
|
||||
$<TARGET_FILE_DIR:mcrfpy_fuzz>/lib/libpython3.14d.so)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Enable Py_DEBUG when linking against debug CPython (matches pydebug ABI)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue