Skip to content

Commit 55b2afa

Browse files
committed
correctly lock objects
1 parent 923c05f commit 55b2afa

9 files changed

Lines changed: 81 additions & 76 deletions

File tree

Lib/test/test_hashlib.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,17 +1027,23 @@ def test_sha256_gil(self):
10271027
@threading_helper.reap_threads
10281028
@threading_helper.requires_working_threading()
10291029
def test_threaded_hashing(self):
1030+
for constructor in self.hash_constructors:
1031+
if constructor().name not in self.shakes:
1032+
with self.subTest(constructor=constructor):
1033+
self.do_test_threaded_hashing(constructor)
1034+
1035+
def do_test_threaded_hashing(self, constructor):
10301036
# Updating the same hash object from several threads at once
10311037
# using data chunk sizes containing the same byte sequences.
10321038
#
10331039
# If the internal locks are working to prevent multiple
10341040
# updates on the same object from running at once, the resulting
10351041
# hash will be the same as doing it single threaded upfront.
1036-
hasher = hashlib.sha1()
1042+
hasher = constructor()
10371043
num_threads = 5
10381044
smallest_data = b'swineflu'
10391045
data = smallest_data * 200000
1040-
expected_hash = hashlib.sha1(data*num_threads).hexdigest()
1046+
expected_hash = constructor(data*num_threads).hexdigest()
10411047

10421048
def hash_in_chunks(chunk_size):
10431049
index = 0

Modules/_hashopenssl.c

Lines changed: 16 additions & 23 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+
PyObject_HASHLIB_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+
PyObject_HASHLIB_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))
@@ -803,11 +797,10 @@ _hashlib_HASH_update_impl(HASHobject *self, PyObject *obj)
803797
int result;
804798
Py_buffer view;
805799
GET_BUFFER_VIEW_OR_ERROUT(obj, &view);
806-
Py_BEGIN_ALLOW_THREADS
807-
HASHLIB_ACQUIRE_LOCK(self);
808-
result = _hashlib_HASH_hash(self, view.buf, view.len);
809-
HASHLIB_RELEASE_LOCK(self);
810-
Py_END_ALLOW_THREADS
800+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
801+
self, HASHLIB_GIL_MINSIZE,
802+
result = _hashlib_HASH_hash(self, view.buf, view.len)
803+
);
811804
PyBuffer_Release(&view);
812805
return result < 0 ? NULL : Py_None;
813806
}
@@ -1114,9 +1107,10 @@ _hashlib_HASH(PyObject *module, const char *digestname, PyObject *data_obj,
11141107
if (view.buf && view.len) {
11151108
/* Do not use self->mutex here as this is the constructor
11161109
* where it is not yet possible to have concurrent access. */
1117-
Py_BEGIN_ALLOW_THREADS
1118-
result = _hashlib_HASH_hash(self, view.buf, view.len);
1119-
Py_END_ALLOW_THREADS
1110+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
1111+
view.len,
1112+
result = _hashlib_HASH_hash(self, view.buf, view.len)
1113+
);
11201114
if (result == -1) {
11211115
assert(PyErr_Occurred());
11221116
Py_CLEAR(self);
@@ -1810,13 +1804,12 @@ _hmac_update(HMACobject *self, PyObject *obj)
18101804
Py_buffer view = {0};
18111805

18121806
GET_BUFFER_VIEW_OR_ERROR(obj, &view, return 0);
1813-
Py_BEGIN_ALLOW_THREADS
1814-
HASHLIB_ACQUIRE_LOCK(self);
1815-
r = HMAC_Update(self->ctx,
1816-
(const unsigned char *)view.buf,
1817-
(size_t)view.len);
1818-
HASHLIB_RELEASE_LOCK(self);
1819-
Py_END_ALLOW_THREADS
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+
);
18201813
PyBuffer_Release(&view);
18211814

18221815
if (r == 0) {

Modules/blake2module.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -646,10 +646,10 @@ py_blake2_new(PyTypeObject *type, PyObject *data, int digest_size,
646646
GET_BUFFER_VIEW_OR_ERROR(data, &buf, goto error);
647647
/* Do not use self->mutex here as this is the constructor
648648
* where it is not yet possible to have concurrent access. */
649-
HASHLIB_EXTERNAL_INSTRUCTIONS(
649+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
650650
buf.len,
651651
blake2_update_unlocked(self, buf.buf, buf.len)
652-
)
652+
);
653653
PyBuffer_Release(&buf);
654654
}
655655

@@ -822,10 +822,10 @@ _blake2_blake2b_update_impl(Blake2Object *self, PyObject *data)
822822
{
823823
Py_buffer buf;
824824
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
825-
HASHLIB_EXTERNAL_INSTRUCTIONS_WITH_MUTEX(
825+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
826826
self, buf.len,
827827
blake2_update_unlocked(self, buf.buf, buf.len)
828-
)
828+
);
829829
PyBuffer_Release(&buf);
830830
Py_RETURN_NONE;
831831
}

Modules/hashlib.h

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,27 +64,33 @@
6464
} while (0)
6565

6666
#define HASHLIB_GIL_MINSIZE 2048
67-
#define HASHLIB_EXTERNAL_INSTRUCTIONS(SIZE, STATEMENTS) \
68-
if ((SIZE) > HASHLIB_GIL_MINSIZE) { \
69-
Py_BEGIN_ALLOW_THREADS \
70-
STATEMENTS; \
71-
Py_END_ALLOW_THREADS \
72-
} \
73-
else { \
74-
STATEMENTS; \
75-
}
67+
#define HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(SIZE, STATEMENTS) \
68+
do { \
69+
if ((SIZE) > HASHLIB_GIL_MINSIZE) { \
70+
Py_BEGIN_ALLOW_THREADS \
71+
STATEMENTS; \
72+
Py_END_ALLOW_THREADS \
73+
} \
74+
else { \
75+
STATEMENTS; \
76+
} \
77+
} while (0)
7678

77-
#define HASHLIB_EXTERNAL_INSTRUCTIONS_WITH_MUTEX(OBJ, SIZE, STATEMENTS) \
78-
if ((SIZE) > HASHLIB_GIL_MINSIZE) { \
79-
Py_BEGIN_ALLOW_THREADS \
80-
HASHLIB_ACQUIRE_LOCK(OBJ); \
81-
STATEMENTS; \
82-
HASHLIB_RELEASE_LOCK(OBJ); \
83-
Py_END_ALLOW_THREADS \
84-
} \
85-
else { \
86-
STATEMENTS; \
87-
}
79+
#define HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(OBJ, SIZE, STATEMENTS) \
80+
do { \
81+
if ((SIZE) > HASHLIB_GIL_MINSIZE) { \
82+
Py_BEGIN_ALLOW_THREADS \
83+
HASHLIB_ACQUIRE_LOCK(OBJ); \
84+
STATEMENTS; \
85+
HASHLIB_RELEASE_LOCK(OBJ); \
86+
Py_END_ALLOW_THREADS \
87+
} \
88+
else { \
89+
HASHLIB_ACQUIRE_LOCK(OBJ); \
90+
STATEMENTS; \
91+
HASHLIB_RELEASE_LOCK(OBJ); \
92+
} \
93+
} while (0)
8894

8995
static inline int
9096
_Py_hashlib_data_argument(PyObject **res, PyObject *data, PyObject *string)

Modules/hmacmodule.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ _hmac_new_impl(PyObject *module, PyObject *keyobj, PyObject *msgobj,
776776
GET_BUFFER_VIEW_OR_ERROR(msgobj, &msg, goto error);
777777
/* Do not use self->mutex here as this is the constructor
778778
* where it is not yet possible to have concurrent access. */
779-
HASHLIB_EXTERNAL_INSTRUCTIONS(
779+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
780780
msg.len,
781781
_hacl_hmac_state_update(self->state, msg.buf, msg.len)
782782
);
@@ -888,10 +888,10 @@ _hmac_HMAC_update_impl(HMACObject *self, PyObject *msgobj)
888888
int rc = 0;
889889
Py_buffer msg;
890890
GET_BUFFER_VIEW_OR_ERROUT(msgobj, &msg);
891-
HASHLIB_EXTERNAL_INSTRUCTIONS_WITH_MUTEX(
891+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
892892
self, msg.len,
893893
rc = _hacl_hmac_state_update(self->state, msg.buf, msg.len)
894-
)
894+
);
895895
PyBuffer_Release(&msg);
896896
return rc < 0 ? NULL : Py_None;
897897
}

Modules/md5module.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,10 @@ MD5Type_update_impl(MD5object *self, PyObject *obj)
193193
{
194194
Py_buffer buf;
195195
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
196-
HASHLIB_EXTERNAL_INSTRUCTIONS_WITH_MUTEX(
196+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
197197
self, buf.len,
198198
_hacl_md5_state_update(self->hash_state, buf.buf, buf.len)
199-
)
199+
);
200200
PyBuffer_Release(&buf);
201201
Py_RETURN_NONE;
202202
}
@@ -300,10 +300,10 @@ _md5_md5_impl(PyObject *module, PyObject *data, int usedforsecurity,
300300
if (string) {
301301
/* Do not use self->mutex here as this is the constructor
302302
* where it is not yet possible to have concurrent access. */
303-
HASHLIB_EXTERNAL_INSTRUCTIONS(
303+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
304304
buf.len,
305305
_hacl_md5_state_update(new->hash_state, buf.buf, buf.len)
306-
)
306+
);
307307
PyBuffer_Release(&buf);
308308
}
309309

Modules/sha1module.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,10 @@ SHA1Type_update_impl(SHA1object *self, PyObject *obj)
196196
{
197197
Py_buffer buf;
198198
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
199-
HASHLIB_EXTERNAL_INSTRUCTIONS_WITH_MUTEX(
199+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
200200
self, buf.len,
201201
_hacl_sha1_state_update(self->hash_state, buf.buf, buf.len)
202-
)
202+
);
203203
PyBuffer_Release(&buf);
204204
Py_RETURN_NONE;
205205
}
@@ -302,10 +302,10 @@ _sha1_sha1_impl(PyObject *module, PyObject *data, int usedforsecurity,
302302
if (string) {
303303
/* Do not use self->mutex here as this is the constructor
304304
* where it is not yet possible to have concurrent access. */
305-
HASHLIB_EXTERNAL_INSTRUCTIONS(
305+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
306306
buf.len,
307307
_hacl_sha1_state_update(new->hash_state, buf.buf, buf.len)
308-
)
308+
);
309309
PyBuffer_Release(&buf);
310310
}
311311

Modules/sha2module.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -406,10 +406,10 @@ SHA256Type_update_impl(SHA256object *self, PyObject *obj)
406406
{
407407
Py_buffer buf;
408408
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
409-
HASHLIB_EXTERNAL_INSTRUCTIONS_WITH_MUTEX(
409+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
410410
self, buf.len,
411411
_hacl_sha2_state_update_256(self->state, buf.buf, buf.len)
412-
)
412+
);
413413
PyBuffer_Release(&buf);
414414
Py_RETURN_NONE;
415415
}
@@ -429,10 +429,10 @@ SHA512Type_update_impl(SHA512object *self, PyObject *obj)
429429
{
430430
Py_buffer buf;
431431
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
432-
HASHLIB_EXTERNAL_INSTRUCTIONS_WITH_MUTEX(
432+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
433433
self, buf.len,
434434
_hacl_sha2_state_update_512(self->state, buf.buf, buf.len)
435-
)
435+
);
436436
PyBuffer_Release(&buf);
437437
Py_RETURN_NONE;
438438
}
@@ -614,10 +614,10 @@ _sha2_sha256_impl(PyObject *module, PyObject *data, int usedforsecurity,
614614
if (string) {
615615
/* Do not use self->mutex here as this is the constructor
616616
* where it is not yet possible to have concurrent access. */
617-
HASHLIB_EXTERNAL_INSTRUCTIONS(
617+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
618618
buf.len,
619619
_hacl_sha2_state_update_256(new->state, buf.buf, buf.len)
620-
)
620+
);
621621
PyBuffer_Release(&buf);
622622
}
623623

@@ -672,10 +672,10 @@ _sha2_sha224_impl(PyObject *module, PyObject *data, int usedforsecurity,
672672
if (string) {
673673
/* Do not use self->mutex here as this is the constructor
674674
* where it is not yet possible to have concurrent access. */
675-
HASHLIB_EXTERNAL_INSTRUCTIONS(
675+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
676676
buf.len,
677677
_hacl_sha2_state_update_256(new->state, buf.buf, buf.len)
678-
)
678+
);
679679
PyBuffer_Release(&buf);
680680
}
681681

@@ -731,10 +731,10 @@ _sha2_sha512_impl(PyObject *module, PyObject *data, int usedforsecurity,
731731
if (string) {
732732
/* Do not use self->mutex here as this is the constructor
733733
* where it is not yet possible to have concurrent access. */
734-
HASHLIB_EXTERNAL_INSTRUCTIONS(
734+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
735735
buf.len,
736736
_hacl_sha2_state_update_512(new->state, buf.buf, buf.len)
737-
)
737+
);
738738
PyBuffer_Release(&buf);
739739
}
740740

@@ -790,10 +790,10 @@ _sha2_sha384_impl(PyObject *module, PyObject *data, int usedforsecurity,
790790
if (string) {
791791
/* Do not use self->mutex here as this is the constructor
792792
* where it is not yet possible to have concurrent access. */
793-
HASHLIB_EXTERNAL_INSTRUCTIONS(
793+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
794794
buf.len,
795795
_hacl_sha2_state_update_512(new->state, buf.buf, buf.len)
796-
)
796+
);
797797
PyBuffer_Release(&buf);
798798
}
799799

Modules/sha3module.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@ py_sha3_new_impl(PyTypeObject *type, PyObject *data_obj, int usedforsecurity,
163163
GET_BUFFER_VIEW_OR_ERROR(data, &buf, goto error);
164164
/* Do not use self->mutex here as this is the constructor
165165
* where it is not yet possible to have concurrent access. */
166-
HASHLIB_EXTERNAL_INSTRUCTIONS(
166+
HASHLIB_EXTERNAL_INSTRUCTIONS_UNLOCKED(
167167
buf.len,
168168
_hacl_sha3_state_update(self->hash_state, buf.buf, buf.len)
169-
)
169+
);
170170
}
171171

172172
PyBuffer_Release(&buf);
@@ -298,10 +298,10 @@ _sha3_sha3_224_update_impl(SHA3object *self, PyObject *data)
298298
{
299299
Py_buffer buf;
300300
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
301-
HASHLIB_EXTERNAL_INSTRUCTIONS_WITH_MUTEX(
301+
HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED(
302302
self, buf.len,
303303
_hacl_sha3_state_update(self->hash_state, buf.buf, buf.len)
304-
)
304+
);
305305
PyBuffer_Release(&buf);
306306
Py_RETURN_NONE;
307307
}

0 commit comments

Comments
 (0)