From 5d6ee9c34e1a9134541e7d1f4d5e966d665827ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 31 Aug 2025 12:09:46 +0200 Subject: [PATCH 1/4] fully implement GC protocol for `lzma` objects --- Modules/_lzmamodule.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index 0b0b1bc765bbc9..21362570bce197 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -809,11 +809,14 @@ Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return NULL; } - assert(type != NULL && type->tp_alloc != NULL); - self = (Compressor *)type->tp_alloc(type, 0); + assert(type != NULL); + self = PyObject_GC_New(Compressor, type); if (self == NULL) { return NULL; } + /* Initialize the remaining fields (untouched by PyObject_GC_New()). */ + const size_t offset = sizeof(struct { PyObject_HEAD }); + memset((char *)self + offset, 0, sizeof(*self) - offset); self->alloc.opaque = NULL; self->alloc.alloc = PyLzma_Malloc; @@ -827,7 +830,6 @@ Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return NULL; } - self->flushed = 0; switch (format) { case FORMAT_XZ: if (check == -1) { @@ -856,6 +858,7 @@ Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto error; } + PyObject_GC_Track(self); return (PyObject *)self; error: @@ -866,12 +869,13 @@ Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) static void Compressor_dealloc(PyObject *op) { + PyTypeObject *tp = Py_TYPE(op); + PyObject_GC_UnTrack(op); Compressor *self = Compressor_CAST(op); lzma_end(&self->lzs); if (self->lock != NULL) { PyThread_free_lock(self->lock); } - PyTypeObject *tp = Py_TYPE(self); tp->tp_free(self); Py_DECREF(tp); } @@ -933,7 +937,7 @@ static PyType_Spec lzma_compressor_type_spec = { // lzma_compressor_type_spec does not have Py_TPFLAGS_BASETYPE flag // which prevents to create a subclass. // So calling PyType_GetModuleState() in this file is always safe. - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), .slots = lzma_compressor_type_slots, }; @@ -1241,16 +1245,19 @@ _lzma_LZMADecompressor_impl(PyTypeObject *type, int format, return NULL; } - assert(type != NULL && type->tp_alloc != NULL); - self = (Decompressor *)type->tp_alloc(type, 0); + assert(type != NULL); + self = PyObject_GC_New(Decompressor, type); if (self == NULL) { return NULL; } + /* Initialize the remaining fields (untouched by PyObject_GC_New()). */ + const size_t offset = sizeof(struct { PyObject_HEAD }); + memset((char *)self + offset, 0, sizeof(*self) - offset); + self->alloc.opaque = NULL; self->alloc.alloc = PyLzma_Malloc; self->alloc.free = PyLzma_Free; self->lzs.allocator = &self->alloc; - self->lzs.next_in = NULL; self->lock = PyThread_allocate_lock(); if (self->lock == NULL) { @@ -1261,9 +1268,7 @@ _lzma_LZMADecompressor_impl(PyTypeObject *type, int format, self->check = LZMA_CHECK_UNKNOWN; self->needs_input = 1; - self->input_buffer = NULL; - self->input_buffer_size = 0; - Py_XSETREF(self->unused_data, PyBytes_FromStringAndSize(NULL, 0)); + self->unused_data = PyBytes_FromStringAndSize(NULL, 0); if (self->unused_data == NULL) { goto error; } @@ -1304,6 +1309,7 @@ _lzma_LZMADecompressor_impl(PyTypeObject *type, int format, goto error; } + PyObject_GC_Track(self); return (PyObject *)self; error: @@ -1314,6 +1320,8 @@ _lzma_LZMADecompressor_impl(PyTypeObject *type, int format, static void Decompressor_dealloc(PyObject *op) { + PyTypeObject *tp = Py_TYPE(op); + PyObject_GC_UnTrack(op); Decompressor *self = Decompressor_CAST(op); if(self->input_buffer != NULL) PyMem_Free(self->input_buffer); @@ -1323,7 +1331,6 @@ Decompressor_dealloc(PyObject *op) if (self->lock != NULL) { PyThread_free_lock(self->lock); } - PyTypeObject *tp = Py_TYPE(self); tp->tp_free(self); Py_DECREF(tp); } @@ -1381,7 +1388,7 @@ static PyType_Spec lzma_decompressor_type_spec = { // lzma_decompressor_type_spec does not have Py_TPFLAGS_BASETYPE flag // which prevents to create a subclass. // So calling PyType_GetModuleState() in this file is always safe. - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), .slots = lzma_decompressor_type_slots, }; From d32a93a1fb01a842d0061fa7279dd4934f6569d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 31 Aug 2025 14:02:18 +0200 Subject: [PATCH 2/4] use `PyObject_GC_Del` for clarity --- Modules/_lzmamodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index 21362570bce197..17b814455608bb 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -876,7 +876,7 @@ Compressor_dealloc(PyObject *op) if (self->lock != NULL) { PyThread_free_lock(self->lock); } - tp->tp_free(self); + PyObject_GC_Del(self); Py_DECREF(tp); } @@ -1331,7 +1331,7 @@ Decompressor_dealloc(PyObject *op) if (self->lock != NULL) { PyThread_free_lock(self->lock); } - tp->tp_free(self); + PyObject_GC_Del(self); Py_DECREF(tp); } From c13af3cc4b5e3403ac4961fdc1a4b83fb64bed8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 31 Aug 2025 14:16:23 +0200 Subject: [PATCH 3/4] reduce diff for 3.14 and later --- Modules/_lzmamodule.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index 17b814455608bb..1454d157a1cb93 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -809,14 +809,11 @@ Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return NULL; } - assert(type != NULL); - self = PyObject_GC_New(Compressor, type); + assert(type != NULL && type->tp_alloc != NULL); + self = (Compressor *)type->tp_alloc(type, 0); if (self == NULL) { return NULL; } - /* Initialize the remaining fields (untouched by PyObject_GC_New()). */ - const size_t offset = sizeof(struct { PyObject_HEAD }); - memset((char *)self + offset, 0, sizeof(*self) - offset); self->alloc.opaque = NULL; self->alloc.alloc = PyLzma_Malloc; @@ -830,6 +827,7 @@ Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return NULL; } + self->flushed = 0; switch (format) { case FORMAT_XZ: if (check == -1) { @@ -1245,19 +1243,16 @@ _lzma_LZMADecompressor_impl(PyTypeObject *type, int format, return NULL; } - assert(type != NULL); - self = PyObject_GC_New(Decompressor, type); + assert(type != NULL && type->tp_alloc != NULL); + self = (Decompressor *)type->tp_alloc(type, 0); if (self == NULL) { return NULL; } - /* Initialize the remaining fields (untouched by PyObject_GC_New()). */ - const size_t offset = sizeof(struct { PyObject_HEAD }); - memset((char *)self + offset, 0, sizeof(*self) - offset); - self->alloc.opaque = NULL; self->alloc.alloc = PyLzma_Malloc; self->alloc.free = PyLzma_Free; self->lzs.allocator = &self->alloc; + self->lzs.next_in = NULL; self->lock = PyThread_allocate_lock(); if (self->lock == NULL) { @@ -1268,7 +1263,9 @@ _lzma_LZMADecompressor_impl(PyTypeObject *type, int format, self->check = LZMA_CHECK_UNKNOWN; self->needs_input = 1; - self->unused_data = PyBytes_FromStringAndSize(NULL, 0); + self->input_buffer = NULL; + self->input_buffer_size = 0; + Py_XSETREF(self->unused_data, PyBytes_FromStringAndSize(NULL, 0)); if (self->unused_data == NULL) { goto error; } From a76e4bcb06a5f28dc084e07466deb8e407676654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 31 Aug 2025 14:45:41 +0200 Subject: [PATCH 4/4] smash diff --- Modules/_lzmamodule.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index 1454d157a1cb93..bcc9ea7a7bba68 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -856,7 +856,6 @@ Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto error; } - PyObject_GC_Track(self); return (PyObject *)self; error: @@ -874,7 +873,7 @@ Compressor_dealloc(PyObject *op) if (self->lock != NULL) { PyThread_free_lock(self->lock); } - PyObject_GC_Del(self); + tp->tp_free(self); Py_DECREF(tp); } @@ -1306,7 +1305,6 @@ _lzma_LZMADecompressor_impl(PyTypeObject *type, int format, goto error; } - PyObject_GC_Track(self); return (PyObject *)self; error: @@ -1328,7 +1326,7 @@ Decompressor_dealloc(PyObject *op) if (self->lock != NULL) { PyThread_free_lock(self->lock); } - PyObject_GC_Del(self); + tp->tp_free(self); Py_DECREF(tp); }