--- /dev/null
+Fix libc thread safety issues with :mod:`dbm` by performing stateful
+operations in critical sections.
#include "clinic/_dbmmodule.c.h"
#define check_dbmobject_open(v, err) \
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED((v)) \
if ((v)->di_dbm == NULL) { \
PyErr_SetString(err, "DBM object has already been closed"); \
return NULL; \
}
static Py_ssize_t
-dbm_length(PyObject *self)
+dbm_length_lock_held(PyObject *self)
{
dbmobject *dp = dbmobject_CAST(self);
_dbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
return dp->di_size;
}
+static Py_ssize_t
+dbm_length(PyObject *self)
+{
+ Py_ssize_t result;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ result = dbm_length_lock_held(self);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
static int
-dbm_bool(PyObject *self)
+dbm_bool_lock_held(PyObject *self)
{
dbmobject *dp = dbmobject_CAST(self);
_dbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
return 1;
}
+static int
+dbm_bool(PyObject *self)
+{
+ int result;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ result = dbm_bool_lock_held(self);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
static PyObject *
-dbm_subscript(PyObject *self, PyObject *key)
+dbm_subscript_lock_held(PyObject *self, PyObject *key)
{
datum drec, krec;
Py_ssize_t tmp_size;
return PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
}
+static PyObject *
+dbm_subscript(PyObject *self, PyObject *key)
+{
+ PyObject *result;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ result = dbm_subscript_lock_held(self, key);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
static int
-dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w)
+dbm_ass_sub_lock_held(PyObject *self, PyObject *v, PyObject *w)
{
datum krec, drec;
Py_ssize_t tmp_size;
return 0;
}
+static int
+dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w)
+{
+ int result;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ result = dbm_ass_sub_lock_held(self, v, w);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
/*[clinic input]
+@critical_section
_dbm.dbm.close
Close the database.
static PyObject *
_dbm_dbm_close_impl(dbmobject *self)
-/*[clinic end generated code: output=c8dc5b6709600b86 input=046db72377d51be8]*/
+/*[clinic end generated code: output=c8dc5b6709600b86 input=4a94f79facbc28ca]*/
{
if (self->di_dbm) {
dbm_close(self->di_dbm);
}
/*[clinic input]
+@critical_section
_dbm.dbm.keys
cls: defining_class
static PyObject *
_dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls)
-/*[clinic end generated code: output=f2a593b3038e5996 input=d3706a28fc051097]*/
+/*[clinic end generated code: output=f2a593b3038e5996 input=6ddefeadf2a80156]*/
{
PyObject *v, *item;
datum key;
}
static int
-dbm_contains(PyObject *self, PyObject *arg)
+dbm_contains_lock_held(PyObject *self, PyObject *arg)
{
dbmobject *dp = dbmobject_CAST(self);
datum key, val;
return val.dptr != NULL;
}
+static int
+dbm_contains(PyObject *self, PyObject *arg)
+{
+ int result;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ result = dbm_contains_lock_held(self, arg);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
/*[clinic input]
+@critical_section
_dbm.dbm.get
cls: defining_class
key: str(accept={str, robuffer}, zeroes=True)
static PyObject *
_dbm_dbm_get_impl(dbmobject *self, PyTypeObject *cls, const char *key,
Py_ssize_t key_length, PyObject *default_value)
-/*[clinic end generated code: output=b4e55f8b6d482bc4 input=66b993b8349fa8c1]*/
+/*[clinic end generated code: output=b4e55f8b6d482bc4 input=1d88a22bb5e55202]*/
{
datum dbm_key, val;
_dbm_state *state = PyType_GetModuleState(cls);
}
/*[clinic input]
+@critical_section
_dbm.dbm.setdefault
cls: defining_class
key: str(accept={str, robuffer}, zeroes=True)
static PyObject *
_dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key,
Py_ssize_t key_length, PyObject *default_value)
-/*[clinic end generated code: output=9c2f6ea6d0fb576c input=126a3ff15c5f8232]*/
+/*[clinic end generated code: output=9c2f6ea6d0fb576c input=c01510ef7571e13b]*/
{
datum dbm_key, val;
Py_ssize_t tmp_size;
}
/*[clinic input]
+@critical_section
_dbm.dbm.clear
cls: defining_class
/
static PyObject *
_dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls)
-/*[clinic end generated code: output=8d126b9e1d01a434 input=43aa6ca1acb7f5f5]*/
+/*[clinic end generated code: output=8d126b9e1d01a434 input=a1aa5d99adfb9656]*/
{
_dbm_state *state = PyType_GetModuleState(cls);
assert(state != NULL);
#include "clinic/_gdbmmodule.c.h"
#define check_gdbmobject_open(v, err) \
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED((v)) \
if ((v)->di_dbm == NULL) { \
PyErr_SetString(err, "GDBM object has already been closed"); \
return NULL; \
}
static Py_ssize_t
-gdbm_length(PyObject *op)
+gdbm_length_lock_held(PyObject *op)
{
gdbmobject *dp = _gdbmobject_CAST(op);
_gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
return dp->di_size;
}
+static Py_ssize_t
+gdbm_length(PyObject *op)
+{
+ Py_ssize_t result;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ result = gdbm_length_lock_held(op);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
static int
-gdbm_bool(PyObject *op)
+gdbm_bool_lock_held(PyObject *op)
{
gdbmobject *dp = _gdbmobject_CAST(op);
_gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
return 1;
}
+static int
+gdbm_bool(PyObject *op)
+{
+ int result;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ result = gdbm_bool_lock_held(op);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
// Wrapper function for PyArg_Parse(o, "s#", &d.dptr, &d.size).
// This function is needed to support PY_SSIZE_T_CLEAN.
// Return 1 on success, same to PyArg_Parse().
}
static PyObject *
-gdbm_subscript(PyObject *op, PyObject *key)
+gdbm_subscript_lock_held(PyObject *op, PyObject *key)
{
PyObject *v;
datum drec, krec;
return v;
}
+static PyObject *
+gdbm_subscript(PyObject *op, PyObject *key)
+{
+ PyObject *result;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ result = gdbm_subscript_lock_held(op, key);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
/*[clinic input]
_gdbm.gdbm.get
}
static int
-gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w)
+gdbm_ass_sub_lock_held(PyObject *op, PyObject *v, PyObject *w)
{
datum krec, drec;
const char *failmsg = "gdbm mappings have bytes or string indices only";
return 0;
}
+static int
+gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w)
+{
+ int result;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ result = gdbm_ass_sub_lock_held(op, v, w);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
/*[clinic input]
+@critical_section
_gdbm.gdbm.setdefault
key: object
static PyObject *
_gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key,
PyObject *default_value)
-/*[clinic end generated code: output=f3246e880509f142 input=0db46b69e9680171]*/
+/*[clinic end generated code: output=f3246e880509f142 input=854374cd81ab51b6]*/
{
PyObject *res;
}
/*[clinic input]
+@critical_section
_gdbm.gdbm.close
Close the database.
static PyObject *
_gdbm_gdbm_close_impl(gdbmobject *self)
-/*[clinic end generated code: output=f5abb4d6bb9e52d5 input=0a203447379b45fd]*/
+/*[clinic end generated code: output=f5abb4d6bb9e52d5 input=56b604f4e77f533d]*/
{
if (self->di_dbm) {
gdbm_close(self->di_dbm);
/* XXX Should return a set or a set view */
/*[clinic input]
+@critical_section
_gdbm.gdbm.keys
cls: defining_class
static PyObject *
_gdbm_gdbm_keys_impl(gdbmobject *self, PyTypeObject *cls)
-/*[clinic end generated code: output=c24b824e81404755 input=1428b7c79703d7d5]*/
+/*[clinic end generated code: output=c24b824e81404755 input=785988b1ea8f77e0]*/
{
PyObject *v, *item;
datum key, nextkey;
}
static int
-gdbm_contains(PyObject *self, PyObject *arg)
+gdbm_contains_lock_held(PyObject *self, PyObject *arg)
{
gdbmobject *dp = (gdbmobject *)self;
datum key;
return gdbm_exists(dp->di_dbm, key);
}
+static int
+gdbm_contains(PyObject *self, PyObject *arg)
+{
+ int result;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ result = gdbm_contains_lock_held(self, arg);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
/*[clinic input]
+@critical_section
_gdbm.gdbm.firstkey
cls: defining_class
static PyObject *
_gdbm_gdbm_firstkey_impl(gdbmobject *self, PyTypeObject *cls)
-/*[clinic end generated code: output=139275e9c8b60827 input=ed8782a029a5d299]*/
+/*[clinic end generated code: output=139275e9c8b60827 input=aad5a7c886c542f5]*/
{
PyObject *v;
datum key;
}
/*[clinic input]
+@critical_section
_gdbm.gdbm.nextkey
cls: defining_class
static PyObject *
_gdbm_gdbm_nextkey_impl(gdbmobject *self, PyTypeObject *cls, const char *key,
Py_ssize_t key_length)
-/*[clinic end generated code: output=c81a69300ef41766 input=365e297bc0b3db48]*/
+/*[clinic end generated code: output=c81a69300ef41766 input=181f1130d5bfeb1e]*/
{
PyObject *v;
datum dbm_key, nextkey;
}
/*[clinic input]
+@critical_section
_gdbm.gdbm.reorganize
cls: defining_class
static PyObject *
_gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls)
-/*[clinic end generated code: output=d77c69e8e3dd644a input=e1359faeef844e46]*/
+/*[clinic end generated code: output=d77c69e8e3dd644a input=3e3ca0d2ea787861]*/
{
_gdbm_state *state = PyType_GetModuleState(cls);
assert(state != NULL);
}
/*[clinic input]
+@critical_section
_gdbm.gdbm.sync
cls: defining_class
static PyObject *
_gdbm_gdbm_sync_impl(gdbmobject *self, PyTypeObject *cls)
-/*[clinic end generated code: output=bb680a2035c3f592 input=3d749235f79b6f2a]*/
+/*[clinic end generated code: output=bb680a2035c3f592 input=6054385b071d238a]*/
{
_gdbm_state *state = PyType_GetModuleState(cls);
assert(state != NULL);
}
/*[clinic input]
+@critical_section
_gdbm.gdbm.clear
cls: defining_class
/
static PyObject *
_gdbm_gdbm_clear_impl(gdbmobject *self, PyTypeObject *cls)
-/*[clinic end generated code: output=673577c573318661 input=34136d52fcdd4210]*/
+/*[clinic end generated code: output=673577c573318661 input=b17467adfe62f23d]*/
{
_gdbm_state *state = PyType_GetModuleState(cls);
assert(state != NULL);
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
# include "pycore_runtime.h" // _Py_SINGLETON()
#endif
+#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
PyDoc_STRVAR(_dbm_dbm_close__doc__,
static PyObject *
_dbm_dbm_close(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- return _dbm_dbm_close_impl((dbmobject *)self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _dbm_dbm_close_impl((dbmobject *)self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
}
PyDoc_STRVAR(_dbm_dbm_keys__doc__,
static PyObject *
_dbm_dbm_keys(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
+ PyObject *return_value = NULL;
+
if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
PyErr_SetString(PyExc_TypeError, "keys() takes no arguments");
- return NULL;
+ goto exit;
}
- return _dbm_dbm_keys_impl((dbmobject *)self, cls);
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _dbm_dbm_keys_impl((dbmobject *)self, cls);
+ Py_END_CRITICAL_SECTION();
+
+exit:
+ return return_value;
}
PyDoc_STRVAR(_dbm_dbm_get__doc__,
&key, &key_length, &default_value)) {
goto exit;
}
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _dbm_dbm_get_impl((dbmobject *)self, cls, key, key_length, default_value);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
&key, &key_length, &default_value)) {
goto exit;
}
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _dbm_dbm_setdefault_impl((dbmobject *)self, cls, key, key_length, default_value);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
static PyObject *
_dbm_dbm_clear(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
+ PyObject *return_value = NULL;
+
if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
PyErr_SetString(PyExc_TypeError, "clear() takes no arguments");
- return NULL;
+ goto exit;
}
- return _dbm_dbm_clear_impl((dbmobject *)self, cls);
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _dbm_dbm_clear_impl((dbmobject *)self, cls);
+ Py_END_CRITICAL_SECTION();
+
+exit:
+ return return_value;
}
PyDoc_STRVAR(dbmopen__doc__,
exit:
return return_value;
}
-/*[clinic end generated code: output=3b456118f231b160 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=279511ea7cda38dd input=a9049054013a1b77]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
# include "pycore_runtime.h" // _Py_SINGLETON()
#endif
+#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(_gdbm_gdbm_get__doc__,
}
default_value = args[1];
skip_optional:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _gdbm_gdbm_setdefault_impl((gdbmobject *)self, key, default_value);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
static PyObject *
_gdbm_gdbm_close(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- return _gdbm_gdbm_close_impl((gdbmobject *)self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _gdbm_gdbm_close_impl((gdbmobject *)self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
}
PyDoc_STRVAR(_gdbm_gdbm_keys__doc__,
static PyObject *
_gdbm_gdbm_keys(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
+ PyObject *return_value = NULL;
+
if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
PyErr_SetString(PyExc_TypeError, "keys() takes no arguments");
- return NULL;
+ goto exit;
}
- return _gdbm_gdbm_keys_impl((gdbmobject *)self, cls);
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _gdbm_gdbm_keys_impl((gdbmobject *)self, cls);
+ Py_END_CRITICAL_SECTION();
+
+exit:
+ return return_value;
}
PyDoc_STRVAR(_gdbm_gdbm_firstkey__doc__,
static PyObject *
_gdbm_gdbm_firstkey(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
+ PyObject *return_value = NULL;
+
if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
PyErr_SetString(PyExc_TypeError, "firstkey() takes no arguments");
- return NULL;
+ goto exit;
}
- return _gdbm_gdbm_firstkey_impl((gdbmobject *)self, cls);
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _gdbm_gdbm_firstkey_impl((gdbmobject *)self, cls);
+ Py_END_CRITICAL_SECTION();
+
+exit:
+ return return_value;
}
PyDoc_STRVAR(_gdbm_gdbm_nextkey__doc__,
&key, &key_length)) {
goto exit;
}
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _gdbm_gdbm_nextkey_impl((gdbmobject *)self, cls, key, key_length);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
static PyObject *
_gdbm_gdbm_reorganize(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
+ PyObject *return_value = NULL;
+
if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
PyErr_SetString(PyExc_TypeError, "reorganize() takes no arguments");
- return NULL;
+ goto exit;
}
- return _gdbm_gdbm_reorganize_impl((gdbmobject *)self, cls);
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _gdbm_gdbm_reorganize_impl((gdbmobject *)self, cls);
+ Py_END_CRITICAL_SECTION();
+
+exit:
+ return return_value;
}
PyDoc_STRVAR(_gdbm_gdbm_sync__doc__,
static PyObject *
_gdbm_gdbm_sync(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
+ PyObject *return_value = NULL;
+
if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
PyErr_SetString(PyExc_TypeError, "sync() takes no arguments");
- return NULL;
+ goto exit;
}
- return _gdbm_gdbm_sync_impl((gdbmobject *)self, cls);
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _gdbm_gdbm_sync_impl((gdbmobject *)self, cls);
+ Py_END_CRITICAL_SECTION();
+
+exit:
+ return return_value;
}
PyDoc_STRVAR(_gdbm_gdbm_clear__doc__,
static PyObject *
_gdbm_gdbm_clear(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
+ PyObject *return_value = NULL;
+
if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
PyErr_SetString(PyExc_TypeError, "clear() takes no arguments");
- return NULL;
+ goto exit;
}
- return _gdbm_gdbm_clear_impl((gdbmobject *)self, cls);
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _gdbm_gdbm_clear_impl((gdbmobject *)self, cls);
+ Py_END_CRITICAL_SECTION();
+
+exit:
+ return return_value;
}
PyDoc_STRVAR(dbmopen__doc__,
exit:
return return_value;
}
-/*[clinic end generated code: output=d974cb39e4ee5d67 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=8bca34ce9d4493dd input=a9049054013a1b77]*/