Crash `msgspec` with Python 3.13.*
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
I think it's because we considered this as a new feature and thus we didn't backport it to 3.13.
cc @markshannon
I succeeded in fixing msgspec this way type->tp_flags &=~Py_TPFLAGS_INLINE_VALUES;
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.
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.
msgspec is incorrectly using PyObject_GC_New for allocating the object, it should use PyType_GenericAlloc which would initialize the inline values.