From c35a4b9d73d4550d88ab1bd717c94d0b3288fb9f Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Sat, 29 Aug 2026 12:59:26 +0200 Subject: [PATCH] [cpyrt] Do not decref a constructor result on Windows ExecuteFast() ends with a Windows-only block that drops the result when a Python exception is pending. It is shared by every CPPMethod subclass, including CPPConstructor, whose executor returns the address of the new C++ object cast to PyObject*; decref'ing that corrupts the heap. Ask the method whether its executor hands back a real PyObject* first. The object leaks instead, on the error path of a call that is already failing. --- src/cpyrt/CPPConstructor.h | 1 + src/cpyrt/CPPMethod.cxx | 3 ++- src/cpyrt/CPPMethod.h | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cpyrt/CPPConstructor.h b/src/cpyrt/CPPConstructor.h index 0566df0..d0bc7b2 100644 --- a/src/cpyrt/CPPConstructor.h +++ b/src/cpyrt/CPPConstructor.h @@ -22,6 +22,7 @@ class CPPConstructor : public CPPMethod { protected: bool InitExecutor_(Executor*&, CallContext* ctxt = nullptr) override; + bool ResultIsPyObject() const override { return false; } }; // specialization for multiple inheritance disambiguation diff --git a/src/cpyrt/CPPMethod.cxx b/src/cpyrt/CPPMethod.cxx index 3fe3e37..e9a1d7f 100644 --- a/src/cpyrt/CPPMethod.cxx +++ b/src/cpyrt/CPPMethod.cxx @@ -186,7 +186,8 @@ inline PyObject* cpyrt::CPPMethod::ExecuteFast(void* self, ptrdiff_t offset, // Windows, so instead leaves the error be #ifdef _WIN32 if (PyErr_Occurred()) { - Py_XDECREF(result); + if (ResultIsPyObject()) + Py_XDECREF(result); result = nullptr; } #endif diff --git a/src/cpyrt/CPPMethod.h b/src/cpyrt/CPPMethod.h index 54429a2..8c66a1d 100644 --- a/src/cpyrt/CPPMethod.h +++ b/src/cpyrt/CPPMethod.h @@ -91,6 +91,10 @@ class CPPMethod : public PyCallable { virtual bool InitExecutor_(Executor*&, CallContext* ctxt = nullptr); + // whether fExecutor's result is a real PyObject* (ConstructorExecutor + // returns the new object's address instead) + virtual bool ResultIsPyObject() const { return true; } + private: void Copy_(const CPPMethod&); void Destroy_();