cpython icon indicating copy to clipboard operation
cpython copied to clipboard

Crash `msgspec` with Python 3.13.*

Open shadchin opened this issue 4 weeks ago • 5 comments

Bug report

Bug description:

The original problem - https://github.com/jcrist/msgspec/pull/960

In Python 3.13 PyObject_GC_New does not do initialization inline values (aka _PyObject_InitInlineValues). In Python 3.14 PyObject_GC_New does initialization inline values - https://github.com/python/cpython/blob/main/Python/gc.c#L2377-L2379

Are there any reasons why PyObject_GC_New doesn't do this initialization in Python 3.13?

I tried to fix msgspec by adding a call to the private function _PyObject_InitInlineValues, but this causes other problems. Can you tell me how to fix this place more correctly? I just came up with the idea to make a copy of the _PyObject_InitInlineValues function in msgspec for Python 3.13.

CPython versions tested on:

3.13

Operating systems tested on:

Linux

shadchin avatar Dec 13 '25 16:12 shadchin

I think it's because we considered this as a new feature and thus we didn't backport it to 3.13.

cc @markshannon

picnixz avatar Dec 13 '25 16:12 picnixz

I succeeded in fixing msgspec this way type->tp_flags &=~Py_TPFLAGS_INLINE_VALUES;

shadchin avatar Dec 13 '25 16:12 shadchin

I believe Py_TPFLAGS_INLINE_VALUES is supposed to be opt-in and only for a few special types in 3.13, so I'm a little bit confused about what msgspec is doing with the type. Are you inheriting from a type that uses inline values, but calling PyObject_GC_New instead of tp_new?

If so -- don't do that. You always have to call the tp_new slot when inheriting (to invoke the supertype), and risk running into breakages like this if you don't. I think we added the logic to initialize inline values in PyObject_GC_New because we wanted to support more types, but since that was a feature, we didn't backport that logic to 3.13.

ZeroIntensity avatar Dec 15 '25 14:12 ZeroIntensity

I'm not an msgspec developer, so I don't really understand how it works either :)

I updated Python to 3.13 and saw that test with msgspec was segfaulted. We have a static build, so I just added it for us:

--- a/src/msgspec/_core.c
+++ b/src/msgspec/_core.c
@@ -5173,6 +5173,11 @@ Struct_alloc(PyTypeObject *type) {
     if (obj == NULL) return NULL;
     /* Zero out slot fields */
     memset((char *)obj + sizeof(PyObject), '\0', type->tp_basicsize - sizeof(PyObject));
+#if PY313_PLUS
+    if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
+        _PyObject_InitInlineValues(obj, type);
+    }
+#endif
     return obj;
 }

Now I try to help msgspec fix this problem.

shadchin avatar Dec 16 '25 06:12 shadchin

msgspec is incorrectly using PyObject_GC_New for allocating the object, it should use PyType_GenericAlloc which would initialize the inline values.

kumaraditya303 avatar Dec 16 '25 07:12 kumaraditya303