UE5.8 PythonScriptPlugin crash during GC: FPyCFunctionWithClosureObject layout omits weakref/vectorcall fields

Summary

On a UE 5.8.3 source build, editor Python can crash during Python garbage collection when Unreal native methods are exposed as bound closures. We reproduced the failure with a minimal script and a project PIE automation run. The crash occurs inside PythonScriptPlugin pre-GC handling, before shutdown, rather than in project gameplay code.

What Type of Bug are you experiencing?

Editor

Steps to Reproduce

  1. Use a Win64 UE 5.8.3 source build with PythonScriptPlugin enabled.
  2. From editor Python, create a Python object or dict cycle that retains a bound Unreal native method/closure produced by PythonScriptPlugin.
  3. Run repeated Python garbage collections (the minimal reproducer runs 500 cycles), or run the same script during a PIE session so the editor pre-GC callback is exercised.
  4. Repeat until the editor reaches PythonScriptPlugin pre-GC cleanup.

Expected Result

Python garbage collection should complete without an access violation, and the editor and PIE session should continue running normally.

Observed Result

The unpatched engine crashes during PythonScriptPlugin pre-GC handling. The call path is FPythonScriptPlugin::OnPreGarbageCollect → PyUtil::CollectGarbage → CPython 3.11 weak-reference cleanup in python311.dll. The failure is intermittent in normal editor use and reliably reproducible with the cyclic minimal test. The observed access violation is consistent with CPython reading the weak-reference slot of an undersized Unreal callable object.

Affects Versions

5.8

Platform(s)

Windows

For crash reports, include your callstack

FPythonScriptPlugin::OnPreGarbageCollect
PyUtil::CollectGarbage
CPython 3.11 weak-reference cleanup (python311.dll)
FPyCFunctionWithClosureObject / PyMethodWithClosure

The crash occurs from the editor pre-GC callback. In the original crash log, the final native frames are in PythonScriptPlugin and the failure occurs while CPython processes weak references.

Additional Notes

Investigation identified a likely engine defect in Engine/Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PyMethodWithClosure.h/.cpp. FPyCFunctionWithClosureObject derives from CPython PyCFunctionObject but the Win64 allocation was 40 bytes while the CPython 3.11 base layout is 56 bytes. CPython therefore reads the weak-reference list slot at offset 40 (and vectorcall at offset 48) past the allocation during GC.

A local engine patch adding the missing WeakRefList and VectorCall fields, setting tp_weaklistoffset, and clearing weakrefs before free-list reuse fixes the issue. Verification passed: 1000 weakref lifetime iterations, 500 minimal cyclic-GC reproductions, and a PIE run with 120 ticks and 119 full Python collections; the editor also shut down cleanly.

A related public report is UE-168225 (Python crash during exit-time Py_Finalize), but this report is a different pre-GC object-layout defect. I can provide the minimal reproducer, source patch, and original crash log details if needed.