mirror of
https://github.com/python/cpython
synced 2024-10-14 11:03:18 +00:00
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:
parent
7218bac8c8
commit
a6465605c1
|
@ -0,0 +1 @@
|
||||||
|
Make hashlib related modules thread-safe without the GIL
|
|
@ -17,6 +17,7 @@
|
||||||
# define Py_BUILD_CORE_MODULE 1
|
# define Py_BUILD_CORE_MODULE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "pycore_strhex.h" // _Py_strhex()
|
#include "pycore_strhex.h" // _Py_strhex()
|
||||||
|
|
||||||
|
@ -42,7 +43,8 @@ typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
blake2b_param param;
|
blake2b_param param;
|
||||||
blake2b_state state;
|
blake2b_state state;
|
||||||
PyThread_type_lock lock;
|
bool use_mutex;
|
||||||
|
PyMutex mutex;
|
||||||
} BLAKE2bObject;
|
} BLAKE2bObject;
|
||||||
|
|
||||||
#include "clinic/blake2b_impl.c.h"
|
#include "clinic/blake2b_impl.c.h"
|
||||||
|
@ -59,9 +61,11 @@ new_BLAKE2bObject(PyTypeObject *type)
|
||||||
{
|
{
|
||||||
BLAKE2bObject *self;
|
BLAKE2bObject *self;
|
||||||
self = (BLAKE2bObject *)type->tp_alloc(type, 0);
|
self = (BLAKE2bObject *)type->tp_alloc(type, 0);
|
||||||
if (self != NULL) {
|
if (self == NULL) {
|
||||||
self->lock = NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
HASHLIB_INIT_MUTEX(self);
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,18 +282,19 @@ _blake2_blake2b_update(BLAKE2bObject *self, PyObject *data)
|
||||||
|
|
||||||
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
|
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
|
||||||
|
|
||||||
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE)
|
if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
|
||||||
self->lock = PyThread_allocate_lock();
|
self->use_mutex = true;
|
||||||
|
}
|
||||||
if (self->lock != NULL) {
|
if (self->use_mutex) {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
PyThread_acquire_lock(self->lock, 1);
|
PyMutex_Lock(&self->mutex);
|
||||||
blake2b_update(&self->state, buf.buf, buf.len);
|
blake2b_update(&self->state, buf.buf, buf.len);
|
||||||
PyThread_release_lock(self->lock);
|
PyMutex_Unlock(&self->mutex);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} else {
|
} else {
|
||||||
blake2b_update(&self->state, buf.buf, buf.len);
|
blake2b_update(&self->state, buf.buf, buf.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyBuffer_Release(&buf);
|
PyBuffer_Release(&buf);
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
@ -389,10 +394,6 @@ py_blake2b_dealloc(PyObject *self)
|
||||||
/* Try not to leave state in memory. */
|
/* Try not to leave state in memory. */
|
||||||
secure_zero_memory(&obj->param, sizeof(obj->param));
|
secure_zero_memory(&obj->param, sizeof(obj->param));
|
||||||
secure_zero_memory(&obj->state, sizeof(obj->state));
|
secure_zero_memory(&obj->state, sizeof(obj->state));
|
||||||
if (obj->lock) {
|
|
||||||
PyThread_free_lock(obj->lock);
|
|
||||||
obj->lock = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject *type = Py_TYPE(self);
|
PyTypeObject *type = Py_TYPE(self);
|
||||||
PyObject_Free(self);
|
PyObject_Free(self);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
# define Py_BUILD_CORE_MODULE 1
|
# define Py_BUILD_CORE_MODULE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "pycore_strhex.h" // _Py_strhex()
|
#include "pycore_strhex.h" // _Py_strhex()
|
||||||
|
|
||||||
|
@ -42,7 +43,8 @@ typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
blake2s_param param;
|
blake2s_param param;
|
||||||
blake2s_state state;
|
blake2s_state state;
|
||||||
PyThread_type_lock lock;
|
bool use_mutex;
|
||||||
|
PyMutex mutex;
|
||||||
} BLAKE2sObject;
|
} BLAKE2sObject;
|
||||||
|
|
||||||
#include "clinic/blake2s_impl.c.h"
|
#include "clinic/blake2s_impl.c.h"
|
||||||
|
@ -59,9 +61,11 @@ new_BLAKE2sObject(PyTypeObject *type)
|
||||||
{
|
{
|
||||||
BLAKE2sObject *self;
|
BLAKE2sObject *self;
|
||||||
self = (BLAKE2sObject *)type->tp_alloc(type, 0);
|
self = (BLAKE2sObject *)type->tp_alloc(type, 0);
|
||||||
if (self != NULL) {
|
if (self == NULL) {
|
||||||
self->lock = NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
HASHLIB_INIT_MUTEX(self);
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,18 +282,19 @@ _blake2_blake2s_update(BLAKE2sObject *self, PyObject *data)
|
||||||
|
|
||||||
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
|
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
|
||||||
|
|
||||||
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE)
|
if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
|
||||||
self->lock = PyThread_allocate_lock();
|
self->use_mutex = true;
|
||||||
|
}
|
||||||
if (self->lock != NULL) {
|
if (self->use_mutex) {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
PyThread_acquire_lock(self->lock, 1);
|
PyMutex_Lock(&self->mutex);
|
||||||
blake2s_update(&self->state, buf.buf, buf.len);
|
blake2s_update(&self->state, buf.buf, buf.len);
|
||||||
PyThread_release_lock(self->lock);
|
PyMutex_Unlock(&self->mutex);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} else {
|
} else {
|
||||||
blake2s_update(&self->state, buf.buf, buf.len);
|
blake2s_update(&self->state, buf.buf, buf.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyBuffer_Release(&buf);
|
PyBuffer_Release(&buf);
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
@ -389,10 +394,6 @@ py_blake2s_dealloc(PyObject *self)
|
||||||
/* Try not to leave state in memory. */
|
/* Try not to leave state in memory. */
|
||||||
secure_zero_memory(&obj->param, sizeof(obj->param));
|
secure_zero_memory(&obj->param, sizeof(obj->param));
|
||||||
secure_zero_memory(&obj->state, sizeof(obj->state));
|
secure_zero_memory(&obj->state, sizeof(obj->state));
|
||||||
if (obj->lock) {
|
|
||||||
PyThread_free_lock(obj->lock);
|
|
||||||
obj->lock = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject *type = Py_TYPE(self);
|
PyTypeObject *type = Py_TYPE(self);
|
||||||
PyObject_Free(self);
|
PyObject_Free(self);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
# define Py_BUILD_CORE_MODULE 1
|
# define Py_BUILD_CORE_MODULE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "pycore_hashtable.h"
|
#include "pycore_hashtable.h"
|
||||||
#include "pycore_pyhash.h" // _Py_HashBytes()
|
#include "pycore_pyhash.h" // _Py_HashBytes()
|
||||||
|
@ -227,16 +228,16 @@ typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
EVP_MD_CTX *ctx; /* OpenSSL message digest context */
|
EVP_MD_CTX *ctx; /* OpenSSL message digest context */
|
||||||
// Prevents undefined behavior via multiple threads entering the C API.
|
// Prevents undefined behavior via multiple threads entering the C API.
|
||||||
// The lock will be NULL before threaded access has been enabled.
|
bool use_mutex;
|
||||||
PyThread_type_lock lock; /* OpenSSL context lock */
|
PyMutex mutex; /* OpenSSL context lock */
|
||||||
} EVPobject;
|
} EVPobject;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
HMAC_CTX *ctx; /* OpenSSL hmac context */
|
HMAC_CTX *ctx; /* OpenSSL hmac context */
|
||||||
// Prevents undefined behavior via multiple threads entering the C API.
|
// Prevents undefined behavior via multiple threads entering the C API.
|
||||||
// The lock will be NULL before threaded access has been enabled.
|
bool use_mutex;
|
||||||
PyThread_type_lock lock; /* HMAC context lock */
|
PyMutex mutex; /* HMAC context lock */
|
||||||
} HMACobject;
|
} HMACobject;
|
||||||
|
|
||||||
#include "clinic/_hashopenssl.c.h"
|
#include "clinic/_hashopenssl.c.h"
|
||||||
|
@ -414,8 +415,7 @@ newEVPobject(PyTypeObject *type)
|
||||||
if (retval == NULL) {
|
if (retval == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
HASHLIB_INIT_MUTEX(retval);
|
||||||
retval->lock = NULL;
|
|
||||||
|
|
||||||
retval->ctx = EVP_MD_CTX_new();
|
retval->ctx = EVP_MD_CTX_new();
|
||||||
if (retval->ctx == NULL) {
|
if (retval->ctx == NULL) {
|
||||||
|
@ -453,8 +453,6 @@ static void
|
||||||
EVP_dealloc(EVPobject *self)
|
EVP_dealloc(EVPobject *self)
|
||||||
{
|
{
|
||||||
PyTypeObject *tp = Py_TYPE(self);
|
PyTypeObject *tp = Py_TYPE(self);
|
||||||
if (self->lock != NULL)
|
|
||||||
PyThread_free_lock(self->lock);
|
|
||||||
EVP_MD_CTX_free(self->ctx);
|
EVP_MD_CTX_free(self->ctx);
|
||||||
PyObject_Free(self);
|
PyObject_Free(self);
|
||||||
Py_DECREF(tp);
|
Py_DECREF(tp);
|
||||||
|
@ -582,16 +580,14 @@ EVP_update(EVPobject *self, PyObject *obj)
|
||||||
|
|
||||||
GET_BUFFER_VIEW_OR_ERROUT(obj, &view);
|
GET_BUFFER_VIEW_OR_ERROUT(obj, &view);
|
||||||
|
|
||||||
if (self->lock == NULL && view.len >= HASHLIB_GIL_MINSIZE) {
|
if (!self->use_mutex && view.len >= HASHLIB_GIL_MINSIZE) {
|
||||||
self->lock = PyThread_allocate_lock();
|
self->use_mutex = true;
|
||||||
/* fail? lock = NULL and we fail over to non-threaded code. */
|
|
||||||
}
|
}
|
||||||
|
if (self->use_mutex) {
|
||||||
if (self->lock != NULL) {
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
PyThread_acquire_lock(self->lock, 1);
|
PyMutex_Lock(&self->mutex);
|
||||||
result = EVP_hash(self, view.buf, view.len);
|
result = EVP_hash(self, view.buf, view.len);
|
||||||
PyThread_release_lock(self->lock);
|
PyMutex_Unlock(&self->mutex);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} else {
|
} else {
|
||||||
result = EVP_hash(self, view.buf, view.len);
|
result = EVP_hash(self, view.buf, view.len);
|
||||||
|
@ -1540,7 +1536,7 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj,
|
||||||
}
|
}
|
||||||
|
|
||||||
self->ctx = ctx;
|
self->ctx = ctx;
|
||||||
self->lock = NULL;
|
HASHLIB_INIT_MUTEX(self);
|
||||||
|
|
||||||
if ((msg_obj != NULL) && (msg_obj != Py_None)) {
|
if ((msg_obj != NULL) && (msg_obj != Py_None)) {
|
||||||
if (!_hmac_update(self, msg_obj))
|
if (!_hmac_update(self, msg_obj))
|
||||||
|
@ -1582,16 +1578,14 @@ _hmac_update(HMACobject *self, PyObject *obj)
|
||||||
|
|
||||||
GET_BUFFER_VIEW_OR_ERROR(obj, &view, return 0);
|
GET_BUFFER_VIEW_OR_ERROR(obj, &view, return 0);
|
||||||
|
|
||||||
if (self->lock == NULL && view.len >= HASHLIB_GIL_MINSIZE) {
|
if (!self->use_mutex && view.len >= HASHLIB_GIL_MINSIZE) {
|
||||||
self->lock = PyThread_allocate_lock();
|
self->use_mutex = true;
|
||||||
/* fail? lock = NULL and we fail over to non-threaded code. */
|
|
||||||
}
|
}
|
||||||
|
if (self->use_mutex) {
|
||||||
if (self->lock != NULL) {
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
PyThread_acquire_lock(self->lock, 1);
|
PyMutex_Lock(&self->mutex);
|
||||||
r = HMAC_Update(self->ctx, (const unsigned char*)view.buf, view.len);
|
r = HMAC_Update(self->ctx, (const unsigned char*)view.buf, view.len);
|
||||||
PyThread_release_lock(self->lock);
|
PyMutex_Unlock(&self->mutex);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} else {
|
} else {
|
||||||
r = HMAC_Update(self->ctx, (const unsigned char*)view.buf, view.len);
|
r = HMAC_Update(self->ctx, (const unsigned char*)view.buf, view.len);
|
||||||
|
@ -1633,7 +1627,7 @@ _hashlib_HMAC_copy_impl(HMACobject *self)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
retval->ctx = ctx;
|
retval->ctx = ctx;
|
||||||
retval->lock = NULL;
|
HASHLIB_INIT_MUTEX(retval);
|
||||||
|
|
||||||
return (PyObject *)retval;
|
return (PyObject *)retval;
|
||||||
}
|
}
|
||||||
|
@ -1642,9 +1636,6 @@ static void
|
||||||
_hmac_dealloc(HMACobject *self)
|
_hmac_dealloc(HMACobject *self)
|
||||||
{
|
{
|
||||||
PyTypeObject *tp = Py_TYPE(self);
|
PyTypeObject *tp = Py_TYPE(self);
|
||||||
if (self->lock != NULL) {
|
|
||||||
PyThread_free_lock(self->lock);
|
|
||||||
}
|
|
||||||
HMAC_CTX_free(self->ctx);
|
HMAC_CTX_free(self->ctx);
|
||||||
PyObject_Free(self);
|
PyObject_Free(self);
|
||||||
Py_DECREF(tp);
|
Py_DECREF(tp);
|
||||||
|
|
65
Modules/clinic/md5module.c.h
generated
65
Modules/clinic/md5module.c.h
generated
|
@ -2,6 +2,12 @@
|
||||||
preserve
|
preserve
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||||
|
# include "pycore_gc.h" // PyGC_Head
|
||||||
|
# include "pycore_runtime.h" // _Py_ID()
|
||||||
|
#endif
|
||||||
|
#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
|
||||||
|
|
||||||
PyDoc_STRVAR(MD5Type_copy__doc__,
|
PyDoc_STRVAR(MD5Type_copy__doc__,
|
||||||
"copy($self, /)\n"
|
"copy($self, /)\n"
|
||||||
"--\n"
|
"--\n"
|
||||||
|
@ -9,7 +15,7 @@ PyDoc_STRVAR(MD5Type_copy__doc__,
|
||||||
"Return a copy of the hash object.");
|
"Return a copy of the hash object.");
|
||||||
|
|
||||||
#define MD5TYPE_COPY_METHODDEF \
|
#define MD5TYPE_COPY_METHODDEF \
|
||||||
{"copy", (PyCFunction)(void(*)(void))MD5Type_copy, METH_METHOD|METH_FASTCALL|METH_KEYWORDS, MD5Type_copy__doc__},
|
{"copy", _PyCFunction_CAST(MD5Type_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, MD5Type_copy__doc__},
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
MD5Type_copy_impl(MD5object *self, PyTypeObject *cls);
|
MD5Type_copy_impl(MD5object *self, PyTypeObject *cls);
|
||||||
|
@ -76,25 +82,70 @@ PyDoc_STRVAR(_md5_md5__doc__,
|
||||||
"Return a new MD5 hash object; optionally initialized with a string.");
|
"Return a new MD5 hash object; optionally initialized with a string.");
|
||||||
|
|
||||||
#define _MD5_MD5_METHODDEF \
|
#define _MD5_MD5_METHODDEF \
|
||||||
{"md5", (PyCFunction)(void(*)(void))_md5_md5, METH_VARARGS|METH_KEYWORDS, _md5_md5__doc__},
|
{"md5", _PyCFunction_CAST(_md5_md5), METH_FASTCALL|METH_KEYWORDS, _md5_md5__doc__},
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_md5_md5_impl(PyObject *module, PyObject *string, int usedforsecurity);
|
_md5_md5_impl(PyObject *module, PyObject *string, int usedforsecurity);
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_md5_md5(PyObject *module, PyObject *args, PyObject *kwargs)
|
_md5_md5(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
{
|
{
|
||||||
PyObject *return_value = NULL;
|
PyObject *return_value = NULL;
|
||||||
static char *_keywords[] = {"string", "usedforsecurity", NULL};
|
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||||
|
|
||||||
|
#define NUM_KEYWORDS 2
|
||||||
|
static struct {
|
||||||
|
PyGC_Head _this_is_not_used;
|
||||||
|
PyObject_VAR_HEAD
|
||||||
|
PyObject *ob_item[NUM_KEYWORDS];
|
||||||
|
} _kwtuple = {
|
||||||
|
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
|
||||||
|
.ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), },
|
||||||
|
};
|
||||||
|
#undef NUM_KEYWORDS
|
||||||
|
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
|
||||||
|
|
||||||
|
#else // !Py_BUILD_CORE
|
||||||
|
# define KWTUPLE NULL
|
||||||
|
#endif // !Py_BUILD_CORE
|
||||||
|
|
||||||
|
static const char * const _keywords[] = {"string", "usedforsecurity", NULL};
|
||||||
|
static _PyArg_Parser _parser = {
|
||||||
|
.keywords = _keywords,
|
||||||
|
.fname = "md5",
|
||||||
|
.kwtuple = KWTUPLE,
|
||||||
|
};
|
||||||
|
#undef KWTUPLE
|
||||||
|
PyObject *argsbuf[2];
|
||||||
|
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
|
||||||
PyObject *string = NULL;
|
PyObject *string = NULL;
|
||||||
int usedforsecurity = 1;
|
int usedforsecurity = 1;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O$p:md5", _keywords,
|
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
|
||||||
&string, &usedforsecurity))
|
if (!args) {
|
||||||
goto exit;
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!noptargs) {
|
||||||
|
goto skip_optional_pos;
|
||||||
|
}
|
||||||
|
if (args[0]) {
|
||||||
|
string = args[0];
|
||||||
|
if (!--noptargs) {
|
||||||
|
goto skip_optional_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skip_optional_pos:
|
||||||
|
if (!noptargs) {
|
||||||
|
goto skip_optional_kwonly;
|
||||||
|
}
|
||||||
|
usedforsecurity = PyObject_IsTrue(args[1]);
|
||||||
|
if (usedforsecurity < 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
skip_optional_kwonly:
|
||||||
return_value = _md5_md5_impl(module, string, usedforsecurity);
|
return_value = _md5_md5_impl(module, string, usedforsecurity);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
/*[clinic end generated code: output=81702ec915f36236 input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=bfadda44914804a8 input=a9049054013a1b77]*/
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
/* Common code for use by all hashlib related modules. */
|
/* Common code for use by all hashlib related modules. */
|
||||||
|
|
||||||
|
#include "pycore_lock.h" // PyMutex
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a PyObject* obj, fill in the Py_buffer* viewp with the result
|
* Given a PyObject* obj, fill in the Py_buffer* viewp with the result
|
||||||
* of PyObject_GetBuffer. Sets an exception and issues the erraction
|
* of PyObject_GetBuffer. Sets an exception and issues the erraction
|
||||||
|
@ -48,18 +50,28 @@
|
||||||
|
|
||||||
#include "pythread.h"
|
#include "pythread.h"
|
||||||
#define ENTER_HASHLIB(obj) \
|
#define ENTER_HASHLIB(obj) \
|
||||||
if ((obj)->lock) { \
|
if ((obj)->use_mutex) { \
|
||||||
if (!PyThread_acquire_lock((obj)->lock, 0)) { \
|
PyMutex_Lock(&(obj)->mutex); \
|
||||||
Py_BEGIN_ALLOW_THREADS \
|
|
||||||
PyThread_acquire_lock((obj)->lock, 1); \
|
|
||||||
Py_END_ALLOW_THREADS \
|
|
||||||
} \
|
|
||||||
}
|
}
|
||||||
#define LEAVE_HASHLIB(obj) \
|
#define LEAVE_HASHLIB(obj) \
|
||||||
if ((obj)->lock) { \
|
if ((obj)->use_mutex) { \
|
||||||
PyThread_release_lock((obj)->lock); \
|
PyMutex_Unlock(&(obj)->mutex); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Py_NOGIL
|
||||||
|
#define HASHLIB_INIT_MUTEX(obj) \
|
||||||
|
do { \
|
||||||
|
(obj)->mutex = (PyMutex){0}; \
|
||||||
|
(obj)->use_mutex = true; \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define HASHLIB_INIT_MUTEX(obj) \
|
||||||
|
do { \
|
||||||
|
(obj)->mutex = (PyMutex){0}; \
|
||||||
|
(obj)->use_mutex = false; \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
/* TODO(gpshead): We should make this a module or class attribute
|
/* TODO(gpshead): We should make this a module or class attribute
|
||||||
* to allow the user to optimize based on the platform they're using. */
|
* to allow the user to optimize based on the platform they're using. */
|
||||||
#define HASHLIB_GIL_MINSIZE 2048
|
#define HASHLIB_GIL_MINSIZE 2048
|
||||||
|
|
|
@ -16,13 +16,8 @@
|
||||||
|
|
||||||
/* MD5 objects */
|
/* MD5 objects */
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef Py_BUILD_CORE_BUILTIN
|
||||||
#include "pyconfig.h" // Py_NOGIL
|
# define Py_BUILD_CORE_MODULE 1
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef Py_NOGIL
|
|
||||||
// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED
|
|
||||||
#define Py_LIMITED_API 0x030c0000
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
@ -54,8 +49,8 @@ typedef long long MD5_INT64; /* 64-bit integer */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
// Prevents undefined behavior via multiple threads entering the C API.
|
// Prevents undefined behavior via multiple threads entering the C API.
|
||||||
// The lock will be NULL before threaded access has been enabled.
|
bool use_mutex;
|
||||||
PyThread_type_lock lock;
|
PyMutex mutex;
|
||||||
Hacl_Streaming_MD5_state *hash_state;
|
Hacl_Streaming_MD5_state *hash_state;
|
||||||
} MD5object;
|
} MD5object;
|
||||||
|
|
||||||
|
@ -78,7 +73,11 @@ static MD5object *
|
||||||
newMD5object(MD5State * st)
|
newMD5object(MD5State * st)
|
||||||
{
|
{
|
||||||
MD5object *md5 = (MD5object *)PyObject_GC_New(MD5object, st->md5_type);
|
MD5object *md5 = (MD5object *)PyObject_GC_New(MD5object, st->md5_type);
|
||||||
md5->lock = NULL;
|
if (!md5) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
HASHLIB_INIT_MUTEX(md5);
|
||||||
|
|
||||||
PyObject_GC_Track(md5);
|
PyObject_GC_Track(md5);
|
||||||
return md5;
|
return md5;
|
||||||
}
|
}
|
||||||
|
@ -95,9 +94,6 @@ static void
|
||||||
MD5_dealloc(MD5object *ptr)
|
MD5_dealloc(MD5object *ptr)
|
||||||
{
|
{
|
||||||
Hacl_Streaming_MD5_legacy_free(ptr->hash_state);
|
Hacl_Streaming_MD5_legacy_free(ptr->hash_state);
|
||||||
if (ptr->lock != NULL) {
|
|
||||||
PyThread_free_lock(ptr->lock);
|
|
||||||
}
|
|
||||||
PyTypeObject *tp = Py_TYPE((PyObject*)ptr);
|
PyTypeObject *tp = Py_TYPE((PyObject*)ptr);
|
||||||
PyObject_GC_UnTrack(ptr);
|
PyObject_GC_UnTrack(ptr);
|
||||||
PyObject_GC_Del(ptr);
|
PyObject_GC_Del(ptr);
|
||||||
|
@ -202,14 +198,14 @@ MD5Type_update(MD5object *self, PyObject *obj)
|
||||||
|
|
||||||
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
|
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
|
||||||
|
|
||||||
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
|
if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
|
||||||
self->lock = PyThread_allocate_lock();
|
self->use_mutex = true;
|
||||||
}
|
}
|
||||||
if (self->lock != NULL) {
|
if (self->use_mutex) {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
PyThread_acquire_lock(self->lock, 1);
|
PyMutex_Lock(&self->mutex);
|
||||||
update(self->hash_state, buf.buf, buf.len);
|
update(self->hash_state, buf.buf, buf.len);
|
||||||
PyThread_release_lock(self->lock);
|
PyMutex_Unlock(&self->mutex);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} else {
|
} else {
|
||||||
update(self->hash_state, buf.buf, buf.len);
|
update(self->hash_state, buf.buf, buf.len);
|
||||||
|
|
|
@ -49,7 +49,8 @@ typedef long long SHA1_INT64; /* 64-bit integer */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
// Prevents undefined behavior via multiple threads entering the C API.
|
// Prevents undefined behavior via multiple threads entering the C API.
|
||||||
// The lock will be NULL before threaded access has been enabled.
|
bool use_mutex;
|
||||||
|
PyMutex mutex;
|
||||||
PyThread_type_lock lock;
|
PyThread_type_lock lock;
|
||||||
Hacl_Streaming_SHA1_state *hash_state;
|
Hacl_Streaming_SHA1_state *hash_state;
|
||||||
} SHA1object;
|
} SHA1object;
|
||||||
|
@ -76,7 +77,8 @@ newSHA1object(SHA1State *st)
|
||||||
if (sha == NULL) {
|
if (sha == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
sha->lock = NULL;
|
HASHLIB_INIT_MUTEX(sha);
|
||||||
|
|
||||||
PyObject_GC_Track(sha);
|
PyObject_GC_Track(sha);
|
||||||
return sha;
|
return sha;
|
||||||
}
|
}
|
||||||
|
@ -94,9 +96,6 @@ static void
|
||||||
SHA1_dealloc(SHA1object *ptr)
|
SHA1_dealloc(SHA1object *ptr)
|
||||||
{
|
{
|
||||||
Hacl_Streaming_SHA1_legacy_free(ptr->hash_state);
|
Hacl_Streaming_SHA1_legacy_free(ptr->hash_state);
|
||||||
if (ptr->lock != NULL) {
|
|
||||||
PyThread_free_lock(ptr->lock);
|
|
||||||
}
|
|
||||||
PyTypeObject *tp = Py_TYPE(ptr);
|
PyTypeObject *tp = Py_TYPE(ptr);
|
||||||
PyObject_GC_UnTrack(ptr);
|
PyObject_GC_UnTrack(ptr);
|
||||||
PyObject_GC_Del(ptr);
|
PyObject_GC_Del(ptr);
|
||||||
|
@ -192,14 +191,14 @@ SHA1Type_update(SHA1object *self, PyObject *obj)
|
||||||
|
|
||||||
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
|
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
|
||||||
|
|
||||||
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
|
if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
|
||||||
self->lock = PyThread_allocate_lock();
|
self->use_mutex = true;
|
||||||
}
|
}
|
||||||
if (self->lock != NULL) {
|
if (self->use_mutex) {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
PyThread_acquire_lock(self->lock, 1);
|
PyMutex_Lock(&self->mutex);
|
||||||
update(self->hash_state, buf.buf, buf.len);
|
update(self->hash_state, buf.buf, buf.len);
|
||||||
PyThread_release_lock(self->lock);
|
PyMutex_Unlock(&self->mutex);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} else {
|
} else {
|
||||||
update(self->hash_state, buf.buf, buf.len);
|
update(self->hash_state, buf.buf, buf.len);
|
||||||
|
|
|
@ -53,8 +53,8 @@ typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
int digestsize;
|
int digestsize;
|
||||||
// Prevents undefined behavior via multiple threads entering the C API.
|
// Prevents undefined behavior via multiple threads entering the C API.
|
||||||
// The lock will be NULL before threaded access has been enabled.
|
bool use_mutex;
|
||||||
PyThread_type_lock lock;
|
PyMutex mutex;
|
||||||
Hacl_Streaming_SHA2_state_sha2_256 *state;
|
Hacl_Streaming_SHA2_state_sha2_256 *state;
|
||||||
} SHA256object;
|
} SHA256object;
|
||||||
|
|
||||||
|
@ -62,8 +62,8 @@ typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
int digestsize;
|
int digestsize;
|
||||||
// Prevents undefined behavior via multiple threads entering the C API.
|
// Prevents undefined behavior via multiple threads entering the C API.
|
||||||
// The lock will be NULL before threaded access has been enabled.
|
bool use_mutex;
|
||||||
PyThread_type_lock lock;
|
PyMutex mutex;
|
||||||
Hacl_Streaming_SHA2_state_sha2_512 *state;
|
Hacl_Streaming_SHA2_state_sha2_512 *state;
|
||||||
} SHA512object;
|
} SHA512object;
|
||||||
|
|
||||||
|
@ -106,7 +106,8 @@ newSHA224object(sha2_state *state)
|
||||||
if (!sha) {
|
if (!sha) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
sha->lock = NULL;
|
HASHLIB_INIT_MUTEX(sha);
|
||||||
|
|
||||||
PyObject_GC_Track(sha);
|
PyObject_GC_Track(sha);
|
||||||
return sha;
|
return sha;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +120,8 @@ newSHA256object(sha2_state *state)
|
||||||
if (!sha) {
|
if (!sha) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
sha->lock = NULL;
|
HASHLIB_INIT_MUTEX(sha);
|
||||||
|
|
||||||
PyObject_GC_Track(sha);
|
PyObject_GC_Track(sha);
|
||||||
return sha;
|
return sha;
|
||||||
}
|
}
|
||||||
|
@ -132,7 +134,8 @@ newSHA384object(sha2_state *state)
|
||||||
if (!sha) {
|
if (!sha) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
sha->lock = NULL;
|
HASHLIB_INIT_MUTEX(sha);
|
||||||
|
|
||||||
PyObject_GC_Track(sha);
|
PyObject_GC_Track(sha);
|
||||||
return sha;
|
return sha;
|
||||||
}
|
}
|
||||||
|
@ -145,7 +148,8 @@ newSHA512object(sha2_state *state)
|
||||||
if (!sha) {
|
if (!sha) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
sha->lock = NULL;
|
HASHLIB_INIT_MUTEX(sha);
|
||||||
|
|
||||||
PyObject_GC_Track(sha);
|
PyObject_GC_Track(sha);
|
||||||
return sha;
|
return sha;
|
||||||
}
|
}
|
||||||
|
@ -163,9 +167,6 @@ static void
|
||||||
SHA256_dealloc(SHA256object *ptr)
|
SHA256_dealloc(SHA256object *ptr)
|
||||||
{
|
{
|
||||||
Hacl_Streaming_SHA2_free_256(ptr->state);
|
Hacl_Streaming_SHA2_free_256(ptr->state);
|
||||||
if (ptr->lock != NULL) {
|
|
||||||
PyThread_free_lock(ptr->lock);
|
|
||||||
}
|
|
||||||
PyTypeObject *tp = Py_TYPE(ptr);
|
PyTypeObject *tp = Py_TYPE(ptr);
|
||||||
PyObject_GC_UnTrack(ptr);
|
PyObject_GC_UnTrack(ptr);
|
||||||
PyObject_GC_Del(ptr);
|
PyObject_GC_Del(ptr);
|
||||||
|
@ -176,9 +177,6 @@ static void
|
||||||
SHA512_dealloc(SHA512object *ptr)
|
SHA512_dealloc(SHA512object *ptr)
|
||||||
{
|
{
|
||||||
Hacl_Streaming_SHA2_free_512(ptr->state);
|
Hacl_Streaming_SHA2_free_512(ptr->state);
|
||||||
if (ptr->lock != NULL) {
|
|
||||||
PyThread_free_lock(ptr->lock);
|
|
||||||
}
|
|
||||||
PyTypeObject *tp = Py_TYPE(ptr);
|
PyTypeObject *tp = Py_TYPE(ptr);
|
||||||
PyObject_GC_UnTrack(ptr);
|
PyObject_GC_UnTrack(ptr);
|
||||||
PyObject_GC_Del(ptr);
|
PyObject_GC_Del(ptr);
|
||||||
|
@ -376,14 +374,14 @@ SHA256Type_update(SHA256object *self, PyObject *obj)
|
||||||
|
|
||||||
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
|
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
|
||||||
|
|
||||||
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
|
if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
|
||||||
self->lock = PyThread_allocate_lock();
|
self->use_mutex = true;
|
||||||
}
|
}
|
||||||
if (self->lock != NULL) {
|
if (self->use_mutex) {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
PyThread_acquire_lock(self->lock, 1);
|
PyMutex_Lock(&self->mutex);
|
||||||
update_256(self->state, buf.buf, buf.len);
|
update_256(self->state, buf.buf, buf.len);
|
||||||
PyThread_release_lock(self->lock);
|
PyMutex_Unlock(&self->mutex);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} else {
|
} else {
|
||||||
update_256(self->state, buf.buf, buf.len);
|
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);
|
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
|
||||||
|
|
||||||
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
|
if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
|
||||||
self->lock = PyThread_allocate_lock();
|
self->use_mutex = true;
|
||||||
}
|
}
|
||||||
if (self->lock != NULL) {
|
if (self->use_mutex) {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
PyThread_acquire_lock(self->lock, 1);
|
PyMutex_Lock(&self->mutex);
|
||||||
update_512(self->state, buf.buf, buf.len);
|
update_512(self->state, buf.buf, buf.len);
|
||||||
PyThread_release_lock(self->lock);
|
PyMutex_Unlock(&self->mutex);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} else {
|
} else {
|
||||||
update_512(self->state, buf.buf, buf.len);
|
update_512(self->state, buf.buf, buf.len);
|
||||||
|
|
|
@ -61,8 +61,8 @@ class _sha3.shake_256 "SHA3object *" "&SHAKE256type"
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
// Prevents undefined behavior via multiple threads entering the C API.
|
// Prevents undefined behavior via multiple threads entering the C API.
|
||||||
// The lock will be NULL before threaded access has been enabled.
|
bool use_mutex;
|
||||||
PyThread_type_lock lock;
|
PyMutex mutex;
|
||||||
Hacl_Streaming_Keccak_state *hash_state;
|
Hacl_Streaming_Keccak_state *hash_state;
|
||||||
} SHA3object;
|
} SHA3object;
|
||||||
|
|
||||||
|
@ -76,7 +76,8 @@ newSHA3object(PyTypeObject *type)
|
||||||
if (newobj == NULL) {
|
if (newobj == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
newobj->lock = NULL;
|
HASHLIB_INIT_MUTEX(newobj);
|
||||||
|
|
||||||
return newobj;
|
return newobj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,9 +170,6 @@ static void
|
||||||
SHA3_dealloc(SHA3object *self)
|
SHA3_dealloc(SHA3object *self)
|
||||||
{
|
{
|
||||||
Hacl_Streaming_Keccak_free(self->hash_state);
|
Hacl_Streaming_Keccak_free(self->hash_state);
|
||||||
if (self->lock != NULL) {
|
|
||||||
PyThread_free_lock(self->lock);
|
|
||||||
}
|
|
||||||
PyTypeObject *tp = Py_TYPE(self);
|
PyTypeObject *tp = Py_TYPE(self);
|
||||||
PyObject_Free(self);
|
PyObject_Free(self);
|
||||||
Py_DECREF(tp);
|
Py_DECREF(tp);
|
||||||
|
@ -257,19 +255,22 @@ _sha3_sha3_224_update(SHA3object *self, PyObject *data)
|
||||||
/*[clinic end generated code: output=d3223352286ed357 input=a887f54dcc4ae227]*/
|
/*[clinic end generated code: output=d3223352286ed357 input=a887f54dcc4ae227]*/
|
||||||
{
|
{
|
||||||
Py_buffer buf;
|
Py_buffer buf;
|
||||||
|
|
||||||
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
|
GET_BUFFER_VIEW_OR_ERROUT(data, &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
|
Py_BEGIN_ALLOW_THREADS
|
||||||
PyThread_acquire_lock(self->lock, 1);
|
PyMutex_Lock(&self->mutex);
|
||||||
sha3_update(self->hash_state, buf.buf, buf.len);
|
sha3_update(self->hash_state, buf.buf, buf.len);
|
||||||
PyThread_release_lock(self->lock);
|
PyMutex_Unlock(&self->mutex);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} else {
|
} else {
|
||||||
sha3_update(self->hash_state, buf.buf, buf.len);
|
sha3_update(self->hash_state, buf.buf, buf.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyBuffer_Release(&buf);
|
PyBuffer_Release(&buf);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue