[Bugfix] WangSet.terrain_enum() calls Python with a pending error -> _PyErr_Occurred assertion / abort #322

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

Summary

PyWangSet::terrain_enum (src/tiled/PyWangSet.cpp:148) invokes a Python callable via PyObject_Call while a Python exception is already set. In a debug CPython this trips the assertion !_PyErr_Occurred(tstate) in _PyObject_Call and aborts; in release builds it is undefined/erratic behavior (the call proceeds with a stale error pending).

Found by the #312 fuzz campaign (fuzz_import_parsers target) feeding a malformed Tiled .tsx wangset.

Crash output

mcrfpy_fuzz: modules/cpython/Objects/call.c:342: _PyObject_Call: Assertion `!_PyErr_Occurred(tstate)' failed.
==ERROR: libFuzzer: deadly signal
    #9  _PyObject_Call                       modules/cpython/Objects/call.c:342
    #10 PyObject_Call                        modules/cpython/Objects/call.c:373
    #11 PyWangSet::terrain_enum(...)         src/tiled/PyWangSet.cpp:148
    #12 method_vectorcall_NOARGS

Analysis

terrain_enum() builds a Python IntEnum from the wangset's parsed colors. On malformed input some earlier step sets a Python error (e.g. a failed attribute/format on a corrupt color name) that is not cleared, then terrain_enum calls into Python again with the error still pending. The fix is to ensure no error is pending before PyObject_Call (check/propagate or PyErr_Clear at the right boundary), or to bail out cleanly when an error is already set.

Repro

echo 'ADw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+Cjx0aWxlc2V0IHZlcnNpb249IjEuMTAiIHRpbGVkdmVyc2lvbj0iMS4xMC4yIiBuYW1lPSJ0ZXN0X3RpbGVzZXQiIHRpbGV3aWR0aD0iMTYiIHRpbGVoZWlnaHQ9IjE2IiB0aWxlY291bnQ9IjE2IiBjb2x1bW5zPSI0Ij4KIDxpbWFnZSBzb3VyY2U9InRlc3RfdGlsZXNldC5wbmciIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIvPgogPHByb3BlcnRpZXM+CiAgPHByb3BlcnR5IG5hbWU9ImF1dGhvciIgdmFsdWU9InRlc3QiLz4KICA8cHJvcGVydHkgbmFtZT0idmVyc2lvbiIgdHlwZT0iaW50IiB2YWx1ZT0iMSIvPgogPC9wcm9wZXJ0aWVzPgogPHRpbGUgaWQ9IjAiPgogIDxwcm9wZXJ0aWVzPgogICA8cHJvcGVydHkgbmFtZT0idGVycmFpbiIgdmFsdWU9ImdyYXNzIi8+CiAgIDxwcm9wZXJ0eSBuYW1lPSJ3YWxrYWJsZSIgdHlwZT0iYm9vbCIgdmFsdWU9InRydWUiLz4KICA8L3Byb3BlcnRpZXM+CiAgPGFuaW1hdGlvbj4KICAgPGZyYW1lIHRpbGVpZD0iMCIgZHVyYXRpb249IjUwMCIvPgogICA8ZnJhbWUgdGlsZWlkPSI0IiBkdXJhdGlvbj0iNTAwIi8+CiAgPC9hbmltYXRpb24+CiA8L3RpbGU+CiA8dGlsZSBpZD0iMSI+CiAgPHByb3B0aXJlZXM+CiAgIDxwcm9wZXJ0eSBuYW1lPSJ0ZXJyYWluIiB2YWx1ZT0iZGlydCIvPgogIDwvcHJvcGVydGllcz4KIDwvdGlsZT4KIDx3YW5nc2V0cz4KICA8d2FuZ3NldCBuYW1lPSJ0ZXJyYWluIiB0eXBlPSJjb3JuZXIiIHS0bGU9Ii0xIj4KICAgPHdhbmdjb2xvciBuYW1lPSJHtra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tnJhc3MiIGNvbG9yPSIjMDBmZjAwIiB0aWxlPSIwIiBwcm9iYWJpbGl0eT0iMSIvPgogICA8d2FuZ2NvbG9yIG5hbWU9IkRpcnQiIGNvbG9yPSIjOGI0NTEzIiB0aWxlPSIxIiBwcm9iYWJpbGl0eT0iMSIvPgogICA8d2FuZ3RpbGUgdGlsZWlkPSIwIiB3YW5naWQ9IjAsMSwwLDEsMCwxLDAsMSIvPgogICA8d2FuZ3RpbGUgdGlsZWlkPSIxIiB3YW5naWQ9IjAsMiwwLDIsMCwyLDAsMiIvPgogICA8hWFuZ3RpbGUgdGlsZWlkPSI4IiB3YW5naWQ9IjAsMiwwLDEsMCwxLDAsMSIvPgogICA8d2FuZ3RpbGUgdGlsZWlkPSI5IiB3YW5naWQ9IjAsMSwwLDEsMCwxLDAsMiIvPgogICA8d2FuZ3RpbGUgdGlsZWlkPSIxMCIgd2FuZ2lkPSIwLDEsMCwxLDAsMiwwLDEiLz4KICAgPHdhbmd0aWxlIHRpbGVpZD0iMTEiIHdhbmdpZD0iMCwxLDAsMiwwLDEsMCwxIi8+CiAgPC93YW5nc2V0PgogPC93YW5nc2V0cz4KPC90aWxlc2V0Pgo=' | base64 -d > /tmp/crash.bin
cd build-fuzz && MCRF_LIB_DIR=../__lib_debug PYTHONHOME=../__lib/Python MCRF_FUZZ_TARGET=import_parsers ./mcrfpy_fuzz /tmp/crash.bin

Suggested labels

Bugfix, system:python-binding, priority:tier2-foundation (apply via web — MCP label bug)

Related: #312, #283.

## Summary `PyWangSet::terrain_enum` (src/tiled/PyWangSet.cpp:148) invokes a Python callable via `PyObject_Call` while a Python exception is already set. In a debug CPython this trips the assertion `!_PyErr_Occurred(tstate)` in `_PyObject_Call` and aborts; in release builds it is undefined/erratic behavior (the call proceeds with a stale error pending). Found by the #312 fuzz campaign (`fuzz_import_parsers` target) feeding a malformed Tiled `.tsx` wangset. ## Crash output ``` mcrfpy_fuzz: modules/cpython/Objects/call.c:342: _PyObject_Call: Assertion `!_PyErr_Occurred(tstate)' failed. ==ERROR: libFuzzer: deadly signal #9 _PyObject_Call modules/cpython/Objects/call.c:342 #10 PyObject_Call modules/cpython/Objects/call.c:373 #11 PyWangSet::terrain_enum(...) src/tiled/PyWangSet.cpp:148 #12 method_vectorcall_NOARGS ``` ## Analysis `terrain_enum()` builds a Python `IntEnum` from the wangset's parsed colors. On malformed input some earlier step sets a Python error (e.g. a failed attribute/format on a corrupt color name) that is not cleared, then `terrain_enum` calls into Python again with the error still pending. The fix is to ensure no error is pending before `PyObject_Call` (check/propagate or `PyErr_Clear` at the right boundary), or to bail out cleanly when an error is already set. ## Repro ```sh echo 'ADw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+Cjx0aWxlc2V0IHZlcnNpb249IjEuMTAiIHRpbGVkdmVyc2lvbj0iMS4xMC4yIiBuYW1lPSJ0ZXN0X3RpbGVzZXQiIHRpbGV3aWR0aD0iMTYiIHRpbGVoZWlnaHQ9IjE2IiB0aWxlY291bnQ9IjE2IiBjb2x1bW5zPSI0Ij4KIDxpbWFnZSBzb3VyY2U9InRlc3RfdGlsZXNldC5wbmciIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIvPgogPHByb3BlcnRpZXM+CiAgPHByb3BlcnR5IG5hbWU9ImF1dGhvciIgdmFsdWU9InRlc3QiLz4KICA8cHJvcGVydHkgbmFtZT0idmVyc2lvbiIgdHlwZT0iaW50IiB2YWx1ZT0iMSIvPgogPC9wcm9wZXJ0aWVzPgogPHRpbGUgaWQ9IjAiPgogIDxwcm9wZXJ0aWVzPgogICA8cHJvcGVydHkgbmFtZT0idGVycmFpbiIgdmFsdWU9ImdyYXNzIi8+CiAgIDxwcm9wZXJ0eSBuYW1lPSJ3YWxrYWJsZSIgdHlwZT0iYm9vbCIgdmFsdWU9InRydWUiLz4KICA8L3Byb3BlcnRpZXM+CiAgPGFuaW1hdGlvbj4KICAgPGZyYW1lIHRpbGVpZD0iMCIgZHVyYXRpb249IjUwMCIvPgogICA8ZnJhbWUgdGlsZWlkPSI0IiBkdXJhdGlvbj0iNTAwIi8+CiAgPC9hbmltYXRpb24+CiA8L3RpbGU+CiA8dGlsZSBpZD0iMSI+CiAgPHByb3B0aXJlZXM+CiAgIDxwcm9wZXJ0eSBuYW1lPSJ0ZXJyYWluIiB2YWx1ZT0iZGlydCIvPgogIDwvcHJvcGVydGllcz4KIDwvdGlsZT4KIDx3YW5nc2V0cz4KICA8d2FuZ3NldCBuYW1lPSJ0ZXJyYWluIiB0eXBlPSJjb3JuZXIiIHS0bGU9Ii0xIj4KICAgPHdhbmdjb2xvciBuYW1lPSJHtra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tnJhc3MiIGNvbG9yPSIjMDBmZjAwIiB0aWxlPSIwIiBwcm9iYWJpbGl0eT0iMSIvPgogICA8d2FuZ2NvbG9yIG5hbWU9IkRpcnQiIGNvbG9yPSIjOGI0NTEzIiB0aWxlPSIxIiBwcm9iYWJpbGl0eT0iMSIvPgogICA8d2FuZ3RpbGUgdGlsZWlkPSIwIiB3YW5naWQ9IjAsMSwwLDEsMCwxLDAsMSIvPgogICA8d2FuZ3RpbGUgdGlsZWlkPSIxIiB3YW5naWQ9IjAsMiwwLDIsMCwyLDAsMiIvPgogICA8hWFuZ3RpbGUgdGlsZWlkPSI4IiB3YW5naWQ9IjAsMiwwLDEsMCwxLDAsMSIvPgogICA8d2FuZ3RpbGUgdGlsZWlkPSI5IiB3YW5naWQ9IjAsMSwwLDEsMCwxLDAsMiIvPgogICA8d2FuZ3RpbGUgdGlsZWlkPSIxMCIgd2FuZ2lkPSIwLDEsMCwxLDAsMiwwLDEiLz4KICAgPHdhbmd0aWxlIHRpbGVpZD0iMTEiIHdhbmdpZD0iMCwxLDAsMiwwLDEsMCwxIi8+CiAgPC93YW5nc2V0PgogPC93YW5nc2V0cz4KPC90aWxlc2V0Pgo=' | base64 -d > /tmp/crash.bin cd build-fuzz && MCRF_LIB_DIR=../__lib_debug PYTHONHOME=../__lib/Python MCRF_FUZZ_TARGET=import_parsers ./mcrfpy_fuzz /tmp/crash.bin ``` ## Suggested labels `Bugfix`, `system:python-binding`, `priority:tier2-foundation` (apply via web — MCP label bug) Related: #312, #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#322
No description provided.