From df69e75edcc08475bc9a57a5a76df8a45bfc3c34 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 25 Sep 2019 23:03:30 +0200 Subject: [PATCH] bpo-38142: Updated _hashopenssl.c to be PEP 384 compliant (#16071) * Updated _hashopenssl.c to be PEP 384 compliant * Remove refleak test from test_hashlib. The updated type no longer accepts random arguments to __init__. --- Lib/test/test_hashlib.py | 10 -- .../2019-09-12-18-41-35.bpo-38142.1I0Ch0.rst | 1 + Modules/_hashopenssl.c | 122 ++++++++++-------- 3 files changed, 69 insertions(+), 64 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-09-12-18-41-35.bpo-38142.1I0Ch0.rst diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 9204b44bf4f9e1..d55de02f91645d 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -167,16 +167,6 @@ def hash_constructors(self): constructors = self.constructors_to_test.values() return itertools.chain.from_iterable(constructors) - @support.refcount_test - @unittest.skipIf(c_hashlib is None, 'Require _hashlib module') - def test_refleaks_in_hash___init__(self): - gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount') - sha1_hash = c_hashlib.new('sha1') - refs_before = gettotalrefcount() - for i in range(100): - sha1_hash.__init__('sha1') - self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) - def test_hash_array(self): a = array.array("b", range(10)) for cons in self.hash_constructors: diff --git a/Misc/NEWS.d/next/Library/2019-09-12-18-41-35.bpo-38142.1I0Ch0.rst b/Misc/NEWS.d/next/Library/2019-09-12-18-41-35.bpo-38142.1I0Ch0.rst new file mode 100644 index 00000000000000..740b6f1cf096d2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-09-12-18-41-35.bpo-38142.1I0Ch0.rst @@ -0,0 +1 @@ +The _hashlib OpenSSL wrapper extension module is now PEP-384 compliant. diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index d963c7a57221bc..48511f72bc6350 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -46,6 +46,16 @@ #define PY_OPENSSL_HAS_BLAKE2 1 #endif +static PyModuleDef _hashlibmodule; + +typedef struct { + PyTypeObject *EVPtype; +} _hashlibstate; + +#define _hashlibstate(o) ((_hashlibstate *)PyModule_GetState(o)) +#define _hashlibstate_global ((_hashlibstate *)PyModule_GetState(PyState_FindModule(&_hashlibmodule))) + + typedef struct { PyObject_HEAD EVP_MD_CTX *ctx; /* OpenSSL message digest context */ @@ -53,14 +63,12 @@ typedef struct { } EVPobject; -static PyTypeObject EVPtype; - #include "clinic/_hashopenssl.c.h" /*[clinic input] module _hashlib -class _hashlib.HASH "EVPobject *" "&EVPtype" +class _hashlib.HASH "EVPobject *" "((_hashlibstate *)PyModule_GetState(module))->EVPtype" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a881a5092eecad28]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1adf85e8eb2ab979]*/ /* LCOV_EXCL_START */ @@ -231,7 +239,9 @@ py_digest_by_name(const char *name) static EVPobject * newEVPobject(void) { - EVPobject *retval = (EVPobject *)PyObject_New(EVPobject, &EVPtype); + EVPobject *retval = (EVPobject *)PyObject_New( + EVPobject, _hashlibstate_global->EVPtype + ); if (retval == NULL) { return NULL; } @@ -273,10 +283,12 @@ EVP_hash(EVPobject *self, const void *vp, Py_ssize_t len) static void EVP_dealloc(EVPobject *self) { + PyTypeObject *tp = Py_TYPE(self); if (self->lock != NULL) PyThread_free_lock(self->lock); EVP_MD_CTX_free(self->ctx); PyObject_Del(self); + Py_DECREF(tp); } static int @@ -501,46 +513,23 @@ PyDoc_STRVAR(hashtype_doc, "name -- the hash algorithm being used by this object\n" "digest_size -- number of bytes in this hashes output"); -static PyTypeObject EVPtype = { - PyVarObject_HEAD_INIT(NULL, 0) +static PyType_Slot EVPtype_slots[] = { + {Py_tp_dealloc, EVP_dealloc}, + {Py_tp_repr, EVP_repr}, + {Py_tp_doc, (char *)hashtype_doc}, + {Py_tp_methods, EVP_methods}, + {Py_tp_getset, EVP_getseters}, + {0, 0}, +}; + +static PyType_Spec EVPtype_spec = { "_hashlib.HASH", /*tp_name*/ sizeof(EVPobject), /*tp_basicsize*/ 0, /*tp_itemsize*/ - /* methods */ - (destructor)EVP_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - (reprfunc)EVP_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - hashtype_doc, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - EVP_methods, /* tp_methods */ - NULL, /* tp_members */ - EVP_getseters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + EVPtype_slots }; -\ static PyObject * EVPnew(const EVP_MD *digest, const unsigned char *cp, Py_ssize_t len, int usedforsecurity) @@ -1120,17 +1109,39 @@ static struct PyMethodDef EVP_functions[] = { /* Initialize this module. */ +static int +hashlib_traverse(PyObject *m, visitproc visit, void *arg) +{ + _hashlibstate *state = _hashlibstate(m); + Py_VISIT(state->EVPtype); + return 0; +} + +static int +hashlib_clear(PyObject *m) +{ + _hashlibstate *state = _hashlibstate(m); + Py_CLEAR(state->EVPtype); + return 0; +} + +static void +hashlib_free(void *m) +{ + hashlib_clear((PyObject *)m); +} + static struct PyModuleDef _hashlibmodule = { PyModuleDef_HEAD_INIT, "_hashlib", NULL, - -1, + sizeof(_hashlibstate), EVP_functions, NULL, - NULL, - NULL, - NULL + hashlib_traverse, + hashlib_clear, + hashlib_free }; PyMODINIT_FUNC @@ -1144,19 +1155,21 @@ PyInit__hashlib(void) ERR_load_crypto_strings(); #endif - /* TODO build EVP_functions openssl_* entries dynamically based - * on what hashes are supported rather than listing many - * but having some be unsupported. Only init appropriate - * constants. */ - - Py_TYPE(&EVPtype) = &PyType_Type; - if (PyType_Ready(&EVPtype) < 0) - return NULL; + m = PyState_FindModule(&_hashlibmodule); + if (m != NULL) { + Py_INCREF(m); + return m; + } m = PyModule_Create(&_hashlibmodule); if (m == NULL) return NULL; + PyTypeObject *EVPtype = (PyTypeObject *)PyType_FromSpec(&EVPtype_spec); + if (EVPtype == NULL) + return NULL; + _hashlibstate(m)->EVPtype = EVPtype; + openssl_md_meth_names = generate_hash_name_list(); if (openssl_md_meth_names == NULL) { Py_DECREF(m); @@ -1167,8 +1180,9 @@ PyInit__hashlib(void) return NULL; } - Py_INCREF((PyObject *)&EVPtype); - PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype); + Py_INCREF((PyObject *)_hashlibstate(m)->EVPtype); + PyModule_AddObject(m, "HASH", (PyObject *)_hashlibstate(m)->EVPtype); + PyState_AddModule(m, &_hashlibmodule); return m; }