summaryrefslogtreecommitdiffstats
path: root/Modules/sha3module.c
diff options
context:
space:
mode:
authorGregory P. Smith <greg@krypto.org>2023-05-23 00:06:41 (GMT)
committerGitHub <noreply@github.com>2023-05-23 00:06:41 (GMT)
commit2e5d8a90aa633ff0bebc9b2b8e21eea389937b19 (patch)
tree15a0de14265a20c453bb5c113625e48fb1b3a23c /Modules/sha3module.c
parent988c1f68ce7f99f43322722c4a4f8f85b40bbcd8 (diff)
downloadcpython-2e5d8a90aa633ff0bebc9b2b8e21eea389937b19.zip
cpython-2e5d8a90aa633ff0bebc9b2b8e21eea389937b19.tar.gz
cpython-2e5d8a90aa633ff0bebc9b2b8e21eea389937b19.tar.bz2
gh-99108: Release the GIL around hashlib built-in computation (#104675)
This matches the GIL releasing behavior of our existing `_hashopenssl` module, extending it to the HACL* built-ins. Includes adding comments to better describe the ENTER/LEAVE macros purpose and explain the lock strategy in both existing and new code.
Diffstat (limited to 'Modules/sha3module.c')
-rw-r--r--Modules/sha3module.c36
1 files changed, 34 insertions, 2 deletions
diff --git a/Modules/sha3module.c b/Modules/sha3module.c
index f051874..558d200 100644
--- a/Modules/sha3module.c
+++ b/Modules/sha3module.c
@@ -60,6 +60,9 @@ class _sha3.shake_256 "SHA3object *" "&SHAKE256type"
typedef struct {
PyObject_HEAD
+ // Prevents undefined behavior via multiple threads entering the C API.
+ // The lock will be NULL before threaded access has been enabled.
+ PyThread_type_lock lock;
Hacl_Streaming_Keccak_state *hash_state;
} SHA3object;
@@ -73,6 +76,7 @@ newSHA3object(PyTypeObject *type)
if (newobj == NULL) {
return NULL;
}
+ newobj->lock = NULL;
return newobj;
}
@@ -133,7 +137,15 @@ py_sha3_new_impl(PyTypeObject *type, PyObject *data, int usedforsecurity)
if (data) {
GET_BUFFER_VIEW_OR_ERROR(data, &buf, goto error);
- sha3_update(self->hash_state, buf.buf, buf.len);
+ if (buf.len >= HASHLIB_GIL_MINSIZE) {
+ /* We do not initialize self->lock here as this is the constructor
+ * where it is not yet possible to have concurrent access. */
+ Py_BEGIN_ALLOW_THREADS
+ sha3_update(self->hash_state, buf.buf, buf.len);
+ Py_END_ALLOW_THREADS
+ } else {
+ sha3_update(self->hash_state, buf.buf, buf.len);
+ }
}
PyBuffer_Release(&buf);
@@ -157,6 +169,9 @@ static void
SHA3_dealloc(SHA3object *self)
{
Hacl_Streaming_Keccak_free(self->hash_state);
+ if (self->lock != NULL) {
+ PyThread_free_lock(self->lock);
+ }
PyTypeObject *tp = Py_TYPE(self);
PyObject_Free(self);
Py_DECREF(tp);
@@ -181,7 +196,9 @@ _sha3_sha3_224_copy_impl(SHA3object *self)
if ((newobj = newSHA3object(Py_TYPE(self))) == NULL) {
return NULL;
}
+ ENTER_HASHLIB(self);
newobj->hash_state = Hacl_Streaming_Keccak_copy(self->hash_state);
+ LEAVE_HASHLIB(self);
return (PyObject *)newobj;
}
@@ -199,7 +216,9 @@ _sha3_sha3_224_digest_impl(SHA3object *self)
unsigned char digest[SHA3_MAX_DIGESTSIZE];
// This function errors out if the algorithm is Shake. Here, we know this
// not to be the case, and therefore do not perform error checking.
+ ENTER_HASHLIB(self);
Hacl_Streaming_Keccak_finish(self->hash_state, digest);
+ LEAVE_HASHLIB(self);
return PyBytes_FromStringAndSize((const char *)digest,
Hacl_Streaming_Keccak_hash_len(self->hash_state));
}
@@ -216,7 +235,9 @@ _sha3_sha3_224_hexdigest_impl(SHA3object *self)
/*[clinic end generated code: output=75ad03257906918d input=2d91bb6e0d114ee3]*/
{
unsigned char digest[SHA3_MAX_DIGESTSIZE];
+ ENTER_HASHLIB(self);
Hacl_Streaming_Keccak_finish(self->hash_state, digest);
+ LEAVE_HASHLIB(self);
return _Py_strhex((const char *)digest,
Hacl_Streaming_Keccak_hash_len(self->hash_state));
}
@@ -237,7 +258,18 @@ _sha3_sha3_224_update(SHA3object *self, PyObject *data)
{
Py_buffer buf;
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
- sha3_update(self->hash_state, buf.buf, buf.len);
+ if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
+ self->lock = PyThread_allocate_lock();
+ }
+ if (self->lock != NULL) {
+ Py_BEGIN_ALLOW_THREADS
+ PyThread_acquire_lock(self->lock, 1);
+ sha3_update(self->hash_state, buf.buf, buf.len);
+ PyThread_release_lock(self->lock);
+ Py_END_ALLOW_THREADS
+ } else {
+ sha3_update(self->hash_state, buf.buf, buf.len);
+ }
PyBuffer_Release(&buf);
Py_RETURN_NONE;
}