Skip to content

Commit 42448db

Browse files
committed
Merge branch 'perf/hashlib/mutex-135239' into refactor/crypto/hacl-based-135532
2 parents 2ccccf0 + 4f9729e commit 42448db

9 files changed

Lines changed: 306 additions & 552 deletions

File tree

Lib/test/test_hashlib.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,22 +1027,30 @@ def test_sha256_gil(self):
10271027
@threading_helper.reap_threads
10281028
@threading_helper.requires_working_threading()
10291029
def test_threaded_hashing(self):
1030+
for algorithm, constructors in self.constructors_to_test.items():
1031+
is_shake = algorithm in self.shakes
1032+
for constructor in constructors:
1033+
with self.subTest(constructor=constructor, is_shake=is_shake):
1034+
self.do_test_threaded_hashing(constructor, is_shake)
1035+
1036+
def do_test_threaded_hashing(self, constructor, is_shake):
10301037
# Updating the same hash object from several threads at once
10311038
# using data chunk sizes containing the same byte sequences.
10321039
#
10331040
# If the internal locks are working to prevent multiple
10341041
# updates on the same object from running at once, the resulting
10351042
# hash will be the same as doing it single threaded upfront.
1036-
hasher = hashlib.sha1()
10371043
num_threads = 5
1038-
smallest_data = b'swineflu'
1044+
smallest_data = os.urandom(16)
10391045
data = smallest_data * 200000
1040-
expected_hash = hashlib.sha1(data*num_threads).hexdigest()
1046+
1047+
h1 = constructor()
1048+
h2 = constructor(data * num_threads)
10411049

10421050
def hash_in_chunks(chunk_size):
10431051
index = 0
10441052
while index < len(data):
1045-
hasher.update(data[index:index + chunk_size])
1053+
h1.update(data[index:index + chunk_size])
10461054
index += chunk_size
10471055

10481056
threads = []
@@ -1059,7 +1067,10 @@ def hash_in_chunks(chunk_size):
10591067
for thread in threads:
10601068
thread.join()
10611069

1062-
self.assertEqual(expected_hash, hasher.hexdigest())
1070+
if is_shake:
1071+
self.assertEqual(h1.hexdigest(16), h2.hexdigest(16))
1072+
else:
1073+
self.assertEqual(h1.hexdigest(), h2.hexdigest())
10631074

10641075
def test_get_fips_mode(self):
10651076
fips_mode = self.is_fips_mode

Modules/_hashopenssl.c

Lines changed: 23 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -278,21 +278,15 @@ get_hashlib_state(PyObject *module)
278278
}
279279

280280
typedef struct {
281-
PyObject_HEAD
281+
HASHLIB_OBJECT_HEAD
282282
EVP_MD_CTX *ctx; /* OpenSSL message digest context */
283-
// Prevents undefined behavior via multiple threads entering the C API.
284-
bool use_mutex;
285-
PyMutex mutex; /* OpenSSL context lock */
286283
} HASHobject;
287284

288285
#define HASHobject_CAST(op) ((HASHobject *)(op))
289286

290287
typedef struct {
291-
PyObject_HEAD
288+
HASHLIB_OBJECT_HEAD
292289
HMAC_CTX *ctx; /* OpenSSL hmac context */
293-
// Prevents undefined behavior via multiple threads entering the C API.
294-
bool use_mutex;
295-
PyMutex mutex; /* HMAC context lock */
296290
} HMACobject;
297291

298292
#define HMACobject_CAST(op) ((HMACobject *)(op))
@@ -700,9 +694,9 @@ static int
700694
_hashlib_HASH_copy_locked(HASHobject *self, EVP_MD_CTX *new_ctx_p)
701695
{
702696
int result;
703-
ENTER_HASHLIB(self);
697+
HASHLIB_ACQUIRE_LOCK(self);
704698
result = EVP_MD_CTX_copy(new_ctx_p, self->ctx);
705-
LEAVE_HASHLIB(self);
699+
HASHLIB_RELEASE_LOCK(self);
706700
if (result == 0) {
707701
notify_smart_ssl_error_occurred_in(Py_STRINGIFY(EVP_MD_CTX_copy));
708702
return -1;
@@ -802,27 +796,13 @@ _hashlib_HASH_update_impl(HASHobject *self, PyObject *obj)
802796
{
803797
int result;
804798
Py_buffer view;
805-
806799
GET_BUFFER_VIEW_OR_ERROUT(obj, &view);
807-
808-
if (!self->use_mutex && view.len >= HASHLIB_GIL_MINSIZE) {
809-
self->use_mutex = true;
810-
}
811-
if (self->use_mutex) {
812-
Py_BEGIN_ALLOW_THREADS
813-
PyMutex_Lock(&self->mutex);
814-
result = _hashlib_HASH_hash(self, view.buf, view.len);
815-
PyMutex_Unlock(&self->mutex);
816-
Py_END_ALLOW_THREADS
817-
} else {
818-
result = _hashlib_HASH_hash(self, view.buf, view.len);
819-
}
820-
800+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
801+
self, HASHLIB_GIL_MINSIZE,
802+
result = _hashlib_HASH_hash(self, view.buf, view.len)
803+
);
821804
PyBuffer_Release(&view);
822-
823-
if (result == -1)
824-
return NULL;
825-
Py_RETURN_NONE;
805+
return result < 0 ? NULL : Py_None;
826806
}
827807

828808
static PyMethodDef HASH_methods[] = {
@@ -1125,15 +1105,12 @@ _hashlib_HASH(PyObject *module, const char *digestname, PyObject *data_obj,
11251105
}
11261106

11271107
if (view.buf && view.len) {
1128-
if (view.len >= HASHLIB_GIL_MINSIZE) {
1129-
/* We do not initialize self->lock here as this is the constructor
1130-
* where it is not yet possible to have concurrent access. */
1131-
Py_BEGIN_ALLOW_THREADS
1132-
result = _hashlib_HASH_hash(self, view.buf, view.len);
1133-
Py_END_ALLOW_THREADS
1134-
} else {
1135-
result = _hashlib_HASH_hash(self, view.buf, view.len);
1136-
}
1108+
/* Do not use self->mutex here as this is the constructor
1109+
* where it is not yet possible to have concurrent access. */
1110+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
1111+
view.len,
1112+
result = _hashlib_HASH_hash(self, view.buf, view.len)
1113+
);
11371114
if (result == -1) {
11381115
assert(PyErr_Occurred());
11391116
Py_CLEAR(self);
@@ -1794,9 +1771,9 @@ static int
17941771
locked_HMAC_CTX_copy(HMAC_CTX *new_ctx_p, HMACobject *self)
17951772
{
17961773
int result;
1797-
ENTER_HASHLIB(self);
1774+
HASHLIB_ACQUIRE_LOCK(self);
17981775
result = HMAC_CTX_copy(new_ctx_p, self->ctx);
1799-
LEAVE_HASHLIB(self);
1776+
HASHLIB_RELEASE_LOCK(self);
18001777
if (result == 0) {
18011778
notify_smart_ssl_error_occurred_in(Py_STRINGIFY(HMAC_CTX_copy));
18021779
return -1;
@@ -1827,24 +1804,12 @@ _hmac_update(HMACobject *self, PyObject *obj)
18271804
Py_buffer view = {0};
18281805

18291806
GET_BUFFER_VIEW_OR_ERROR(obj, &view, return 0);
1830-
1831-
if (!self->use_mutex && view.len >= HASHLIB_GIL_MINSIZE) {
1832-
self->use_mutex = true;
1833-
}
1834-
if (self->use_mutex) {
1835-
Py_BEGIN_ALLOW_THREADS
1836-
PyMutex_Lock(&self->mutex);
1837-
r = HMAC_Update(self->ctx,
1838-
(const unsigned char *)view.buf,
1839-
(size_t)view.len);
1840-
PyMutex_Unlock(&self->mutex);
1841-
Py_END_ALLOW_THREADS
1842-
} else {
1843-
r = HMAC_Update(self->ctx,
1844-
(const unsigned char *)view.buf,
1845-
(size_t)view.len);
1846-
}
1847-
1807+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
1808+
self, view.len,
1809+
r = HMAC_Update(
1810+
self->ctx, (const unsigned char *)view.buf, (size_t)view.len
1811+
)
1812+
);
18481813
PyBuffer_Release(&view);
18491814

18501815
if (r == 0) {

Modules/blake2module.c

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ new_Blake2Object(PyTypeObject *type)
422422
} while (0)
423423

424424
static void
425-
update(Blake2Object *self, uint8_t *buf, Py_ssize_t len)
425+
blake2_update_unlocked(Blake2Object *self, uint8_t *buf, Py_ssize_t len)
426426
{
427427
switch (self->impl) {
428428
// blake2b_256_state and blake2s_128_state must be if'd since
@@ -646,14 +646,12 @@ py_blake2_new(PyTypeObject *type, PyObject *data, int digest_size,
646646
if (data != NULL) {
647647
Py_buffer buf;
648648
GET_BUFFER_VIEW_OR_ERROR(data, &buf, goto error);
649-
if (buf.len >= HASHLIB_GIL_MINSIZE) {
650-
Py_BEGIN_ALLOW_THREADS
651-
update(self, buf.buf, buf.len);
652-
Py_END_ALLOW_THREADS
653-
}
654-
else {
655-
update(self, buf.buf, buf.len);
656-
}
649+
/* Do not use self->mutex here as this is the constructor
650+
* where it is not yet possible to have concurrent access. */
651+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
652+
buf.len,
653+
blake2_update_unlocked(self, buf.buf, buf.len)
654+
);
657655
PyBuffer_Release(&buf);
658656
}
659657

@@ -744,7 +742,7 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data_obj, int digest_size,
744742
}
745743

746744
static int
747-
blake2_blake2b_copy_locked(Blake2Object *self, Blake2Object *cpy)
745+
blake2_blake2b_copy_unlocked(Blake2Object *self, Blake2Object *cpy)
748746
{
749747
assert(cpy != NULL);
750748
#define BLAKE2_COPY(TYPE, STATE_ATTR) \
@@ -801,9 +799,9 @@ _blake2_blake2b_copy_impl(Blake2Object *self)
801799
return NULL;
802800
}
803801

804-
ENTER_HASHLIB(self);
805-
rc = blake2_blake2b_copy_locked(self, cpy);
806-
LEAVE_HASHLIB(self);
802+
HASHLIB_ACQUIRE_LOCK(self);
803+
rc = blake2_blake2b_copy_unlocked(self, cpy);
804+
HASHLIB_RELEASE_LOCK(self);
807805
if (rc < 0) {
808806
Py_DECREF(cpy);
809807
return NULL;
@@ -825,25 +823,12 @@ _blake2_blake2b_update_impl(Blake2Object *self, PyObject *data)
825823
/*[clinic end generated code: output=99330230068e8c99 input=ffc4aa6a6a225d31]*/
826824
{
827825
Py_buffer buf;
828-
829826
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
830-
831-
if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
832-
self->use_mutex = true;
833-
}
834-
if (self->use_mutex) {
835-
Py_BEGIN_ALLOW_THREADS
836-
PyMutex_Lock(&self->mutex);
837-
update(self, buf.buf, buf.len);
838-
PyMutex_Unlock(&self->mutex);
839-
Py_END_ALLOW_THREADS
840-
}
841-
else {
842-
update(self, buf.buf, buf.len);
843-
}
844-
827+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
828+
self, buf.len,
829+
blake2_update_unlocked(self, buf.buf, buf.len)
830+
);
845831
PyBuffer_Release(&buf);
846-
847832
Py_RETURN_NONE;
848833
}
849834

@@ -881,9 +866,9 @@ _blake2_blake2b_digest_impl(Blake2Object *self)
881866
/*[clinic end generated code: output=31ab8ad477f4a2f7 input=7d21659e9c5fff02]*/
882867
{
883868
uint8_t digest_length = 0, digest[HACL_HASH_BLAKE2B_OUT_BYTES];
884-
ENTER_HASHLIB(self);
869+
HASHLIB_ACQUIRE_LOCK(self);
885870
digest_length = blake2_blake2b_compute_digest(self, digest);
886-
LEAVE_HASHLIB(self);
871+
HASHLIB_RELEASE_LOCK(self);
887872
return PyBytes_FromStringAndSize((const char *)digest, digest_length);
888873
}
889874

@@ -898,9 +883,9 @@ _blake2_blake2b_hexdigest_impl(Blake2Object *self)
898883
/*[clinic end generated code: output=5ef54b138db6610a input=76930f6946351f56]*/
899884
{
900885
uint8_t digest_length = 0, digest[HACL_HASH_BLAKE2B_OUT_BYTES];
901-
ENTER_HASHLIB(self);
886+
HASHLIB_ACQUIRE_LOCK(self);
902887
digest_length = blake2_blake2b_compute_digest(self, digest);
903-
LEAVE_HASHLIB(self);
888+
HASHLIB_RELEASE_LOCK(self);
904889
return _Py_strhex((const char *)digest, digest_length);
905890
}
906891

0 commit comments

Comments
 (0)