From 5c79e139dd6692ddb6ad5e738946d8f695d4edd1 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 13:15:11 +0200 Subject: [PATCH 1/3] fully implement GC protocol for `_hashlib` objects --- Modules/_hashopenssl.c | 49 +++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index a6496d0f04f2d0..04d7efd3670bb7 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -752,7 +752,7 @@ py_wrapper_EVP_MD_CTX_new(void) static HASHobject * new_hash_object(PyTypeObject *type) { - HASHobject *retval = PyObject_New(HASHobject, type); + HASHobject *retval = PyObject_GC_New(HASHobject, type); if (retval == NULL) { return NULL; } @@ -764,6 +764,7 @@ new_hash_object(PyTypeObject *type) return NULL; } + PyObject_GC_Track(retval); return retval; } @@ -792,13 +793,21 @@ _hashlib_HASH_hash(HASHobject *self, const void *vp, Py_ssize_t len) static void _hashlib_HASH_dealloc(PyObject *op) { + PyTypeObject *tp = Py_TYPE(op); + PyObject_GC_UnTrack(op); HASHobject *self = HASHobject_CAST(op); - PyTypeObject *tp = Py_TYPE(self); EVP_MD_CTX_free(self->ctx); - PyObject_Free(self); + PyObject_GC_Del(self); Py_DECREF(tp); } +static int +_hashlib_HASH_traverse(PyObject *op, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(op)); + return 0; +} + static int _hashlib_HASH_copy_locked(HASHobject *self, EVP_MD_CTX *new_ctx_p) { @@ -993,6 +1002,7 @@ PyDoc_STRVAR(HASHobject_type_doc, static PyType_Slot HASHobject_type_slots[] = { {Py_tp_dealloc, _hashlib_HASH_dealloc}, + {Py_tp_traverse, _hashlib_HASH_traverse}, {Py_tp_repr, _hashlib_HASH_repr}, {Py_tp_doc, (char *)HASHobject_type_doc}, {Py_tp_methods, HASH_methods}, @@ -1008,6 +1018,7 @@ static PyType_Spec HASHobject_type_spec = { | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_HAVE_GC ), .slots = HASHobject_type_slots }; @@ -1165,6 +1176,7 @@ PyDoc_STRVAR(HASHXOFobject_type_doc, "digest_size -- number of bytes in this hashes output"); static PyType_Slot HASHXOFobject_type_slots[] = { + /* tp_dealloc and tp_traverse are inherited from _hashlib.HASH */ {Py_tp_doc, (char *)HASHXOFobject_type_doc}, {Py_tp_methods, HASHXOFobject_methods}, {Py_tp_getset, HASHXOFobject_getsets}, @@ -1179,6 +1191,7 @@ static PyType_Spec HASHXOFobject_type_spec = { | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_HAVE_GC ), .slots = HASHXOFobject_type_slots }; @@ -1902,7 +1915,7 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, goto error; } - self = PyObject_New(HMACobject, state->HMAC_type); + self = PyObject_GC_New(HMACobject, state->HMAC_type); if (self == NULL) { goto error; } @@ -1910,6 +1923,7 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, self->ctx = ctx; ctx = NULL; // 'ctx' is now owned by 'self' HASHLIB_INIT_MUTEX(self); + PyObject_GC_Track(self); if ((msg_obj != NULL) && (msg_obj != Py_None)) { if (!_hmac_update(self, msg_obj)) { @@ -2008,7 +2022,7 @@ _hashlib_HMAC_copy_impl(HMACobject *self) return NULL; } - retval = PyObject_New(HMACobject, Py_TYPE(self)); + retval = PyObject_GC_New(HMACobject, Py_TYPE(self)); if (retval == NULL) { HMAC_CTX_free(ctx); return NULL; @@ -2016,22 +2030,31 @@ _hashlib_HMAC_copy_impl(HMACobject *self) retval->ctx = ctx; HASHLIB_INIT_MUTEX(retval); + PyObject_GC_Track(retval); return (PyObject *)retval; } static void _hmac_dealloc(PyObject *op) { + PyTypeObject *tp = Py_TYPE(op); + PyObject_GC_UnTrack(op); HMACobject *self = HMACobject_CAST(op); - PyTypeObject *tp = Py_TYPE(self); if (self->ctx != NULL) { HMAC_CTX_free(self->ctx); self->ctx = NULL; } - PyObject_Free(self); + PyObject_GC_Del(self); Py_DECREF(tp); } +static int +_hashlib_HMAC_traverse(PyObject *op, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(op)); + return 0; +} + static PyObject * _hmac_repr(PyObject *op) { @@ -2198,15 +2221,21 @@ static PyType_Slot HMACtype_slots[] = { {Py_tp_doc, (char *)hmactype_doc}, {Py_tp_repr, _hmac_repr}, {Py_tp_dealloc, _hmac_dealloc}, + {Py_tp_traverse, _hashlib_HMAC_traverse}, {Py_tp_methods, HMAC_methods}, {Py_tp_getset, HMAC_getset}, {0, NULL} }; PyType_Spec HMACtype_spec = { - "_hashlib.HMAC", /* name */ - sizeof(HMACobject), /* basicsize */ - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE, + .name = "_hashlib.HMAC", + .basicsize = sizeof(HMACobject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_HAVE_GC + ), .slots = HMACtype_slots, }; From 119c19e09887110d27f9719b68d932d9b45bddbc 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 13:52:10 +0200 Subject: [PATCH 2/3] fix: `tp_traverse` is not inherited --- Modules/_hashopenssl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 04d7efd3670bb7..80f9393901d2b2 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -1176,7 +1176,8 @@ PyDoc_STRVAR(HASHXOFobject_type_doc, "digest_size -- number of bytes in this hashes output"); static PyType_Slot HASHXOFobject_type_slots[] = { - /* tp_dealloc and tp_traverse are inherited from _hashlib.HASH */ + /* tp_dealloc is inherited from _hashlib.HASH */ + {Py_tp_traverse, _hashlib_HASH_traverse}, {Py_tp_doc, (char *)HASHXOFobject_type_doc}, {Py_tp_methods, HASHXOFobject_methods}, {Py_tp_getset, HASHXOFobject_getsets}, From 23c4d91e4ae9b47fdf20eaaab4c8d77fe40f7097 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:52:40 +0200 Subject: [PATCH 3/3] smash diff --- Modules/_hashopenssl.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 80f9393901d2b2..9d79fc08dcfcac 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -752,7 +752,9 @@ py_wrapper_EVP_MD_CTX_new(void) static HASHobject * new_hash_object(PyTypeObject *type) { - HASHobject *retval = PyObject_GC_New(HASHobject, type); + assert(type != NULL); + assert(type->tp_alloc != NULL); + HASHobject *retval = (HASHobject *)type->tp_alloc(type, 0); if (retval == NULL) { return NULL; } @@ -764,7 +766,6 @@ new_hash_object(PyTypeObject *type) return NULL; } - PyObject_GC_Track(retval); return retval; } @@ -797,7 +798,7 @@ _hashlib_HASH_dealloc(PyObject *op) PyObject_GC_UnTrack(op); HASHobject *self = HASHobject_CAST(op); EVP_MD_CTX_free(self->ctx); - PyObject_GC_Del(self); + tp->tp_free(self); Py_DECREF(tp); } @@ -1176,7 +1177,7 @@ PyDoc_STRVAR(HASHXOFobject_type_doc, "digest_size -- number of bytes in this hashes output"); static PyType_Slot HASHXOFobject_type_slots[] = { - /* tp_dealloc is inherited from _hashlib.HASH */ + {Py_tp_dealloc, _hashlib_HASH_dealloc}, {Py_tp_traverse, _hashlib_HASH_traverse}, {Py_tp_doc, (char *)HASHXOFobject_type_doc}, {Py_tp_methods, HASHXOFobject_methods}, @@ -1916,7 +1917,8 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, goto error; } - self = PyObject_GC_New(HMACobject, state->HMAC_type); + assert(state->HMAC_type != NULL); + self = (HMACobject *)state->HMAC_type->tp_alloc(state->HMAC_type, 0); if (self == NULL) { goto error; } @@ -1924,7 +1926,6 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, self->ctx = ctx; ctx = NULL; // 'ctx' is now owned by 'self' HASHLIB_INIT_MUTEX(self); - PyObject_GC_Track(self); if ((msg_obj != NULL) && (msg_obj != Py_None)) { if (!_hmac_update(self, msg_obj)) { @@ -2023,7 +2024,8 @@ _hashlib_HMAC_copy_impl(HMACobject *self) return NULL; } - retval = PyObject_GC_New(HMACobject, Py_TYPE(self)); + PyTypeObject *type = Py_TYPE(self); + retval = (HMACobject *)type->tp_alloc(type, 0); if (retval == NULL) { HMAC_CTX_free(ctx); return NULL; @@ -2031,7 +2033,6 @@ _hashlib_HMAC_copy_impl(HMACobject *self) retval->ctx = ctx; HASHLIB_INIT_MUTEX(retval); - PyObject_GC_Track(retval); return (PyObject *)retval; } @@ -2045,7 +2046,7 @@ _hmac_dealloc(PyObject *op) HMAC_CTX_free(self->ctx); self->ctx = NULL; } - PyObject_GC_Del(self); + tp->tp_free(self); Py_DECREF(tp); }