gh-111916: Make hashlib related modules thread-safe without the GIL (#111981)

Always use an individual lock on hash objects when in free-threaded builds.

Fixes #111916
This commit is contained in:
Tomas R 2023-11-16 00:53:38 +01:00 committed by GitHub
parent 7218bac8c8
commit a6465605c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 188 additions and 137 deletions

View file

@ -53,8 +53,8 @@ typedef struct {
PyObject_HEAD
int digestsize;
// 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;
bool use_mutex;
PyMutex mutex;
Hacl_Streaming_SHA2_state_sha2_256 *state;
} SHA256object;
@ -62,8 +62,8 @@ typedef struct {
PyObject_HEAD
int digestsize;
// 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;
bool use_mutex;
PyMutex mutex;
Hacl_Streaming_SHA2_state_sha2_512 *state;
} SHA512object;
@ -106,7 +106,8 @@ newSHA224object(sha2_state *state)
if (!sha) {
return NULL;
}
sha->lock = NULL;
HASHLIB_INIT_MUTEX(sha);
PyObject_GC_Track(sha);
return sha;
}
@ -119,7 +120,8 @@ newSHA256object(sha2_state *state)
if (!sha) {
return NULL;
}
sha->lock = NULL;
HASHLIB_INIT_MUTEX(sha);
PyObject_GC_Track(sha);
return sha;
}
@ -132,7 +134,8 @@ newSHA384object(sha2_state *state)
if (!sha) {
return NULL;
}
sha->lock = NULL;
HASHLIB_INIT_MUTEX(sha);
PyObject_GC_Track(sha);
return sha;
}
@ -145,7 +148,8 @@ newSHA512object(sha2_state *state)
if (!sha) {
return NULL;
}
sha->lock = NULL;
HASHLIB_INIT_MUTEX(sha);
PyObject_GC_Track(sha);
return sha;
}
@ -163,9 +167,6 @@ static void
SHA256_dealloc(SHA256object *ptr)
{
Hacl_Streaming_SHA2_free_256(ptr->state);
if (ptr->lock != NULL) {
PyThread_free_lock(ptr->lock);
}
PyTypeObject *tp = Py_TYPE(ptr);
PyObject_GC_UnTrack(ptr);
PyObject_GC_Del(ptr);
@ -176,9 +177,6 @@ static void
SHA512_dealloc(SHA512object *ptr)
{
Hacl_Streaming_SHA2_free_512(ptr->state);
if (ptr->lock != NULL) {
PyThread_free_lock(ptr->lock);
}
PyTypeObject *tp = Py_TYPE(ptr);
PyObject_GC_UnTrack(ptr);
PyObject_GC_Del(ptr);
@ -376,14 +374,14 @@ SHA256Type_update(SHA256object *self, PyObject *obj)
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
self->lock = PyThread_allocate_lock();
if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
self->use_mutex = true;
}
if (self->lock != NULL) {
if (self->use_mutex) {
Py_BEGIN_ALLOW_THREADS
PyThread_acquire_lock(self->lock, 1);
PyMutex_Lock(&self->mutex);
update_256(self->state, buf.buf, buf.len);
PyThread_release_lock(self->lock);
PyMutex_Unlock(&self->mutex);
Py_END_ALLOW_THREADS
} else {
update_256(self->state, buf.buf, buf.len);
@ -410,14 +408,14 @@ SHA512Type_update(SHA512object *self, PyObject *obj)
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
self->lock = PyThread_allocate_lock();
if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
self->use_mutex = true;
}
if (self->lock != NULL) {
if (self->use_mutex) {
Py_BEGIN_ALLOW_THREADS
PyThread_acquire_lock(self->lock, 1);
PyMutex_Lock(&self->mutex);
update_512(self->state, buf.buf, buf.len);
PyThread_release_lock(self->lock);
PyMutex_Unlock(&self->mutex);
Py_END_ALLOW_THREADS
} else {
update_512(self->state, buf.buf, buf.len);