diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 66cb63daca02ae..bc364e5704ecc7 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1297,6 +1297,10 @@ def testGetServBy(self): self.assertRaises(OverflowError, socket.getservbyport, -1) self.assertRaises(OverflowError, socket.getservbyport, 65536) + def testGetProtoByName(self): + self.assertEqual(socket.getprotobyname('tcp'), 6) + self.assertRaises(OSError, socket.getprotobyname, 'non-existent proto') + def testDefaultTimeout(self): # Testing default timeout # The default timeout should initially be None diff --git a/Misc/NEWS.d/next/Library/2025-04-17-00-14-41.gh-issue-127081.aEM3Hk.rst b/Misc/NEWS.d/next/Library/2025-04-17-00-14-41.gh-issue-127081.aEM3Hk.rst new file mode 100644 index 00000000000000..08b44d3fb6432b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-17-00-14-41.gh-issue-127081.aEM3Hk.rst @@ -0,0 +1,5 @@ +Fix libc thread safety issues with :mod:`socket` (replace ``getservbyname``, +``getservbyport``, ``getprotobyname`` with ``*_r`` re-entrant versions), +:mod:`pwd` (lock access to ``getpwall``), :mod:`dbm` (perform stateful +operations in critical sections), and :mod:`os` (replace ``getlogin`` with +``getlogin_r`` re-entrant version). diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index cc65cbd98d71dc..b293b9ee0b9f6a 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -68,6 +68,7 @@ typedef struct { #include "clinic/_dbmmodule.c.h" +/* NOTE: Must be used within a critical section! */ #define check_dbmobject_open(v, err) \ if ((v)->di_dbm == NULL) { \ PyErr_SetString(err, "DBM object has already been closed"); \ @@ -118,12 +119,15 @@ dbm_dealloc(PyObject *self) static Py_ssize_t dbm_length(PyObject *self) { + Py_ssize_t result = -1; dbmobject *dp = dbmobject_CAST(self); _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); + + Py_BEGIN_CRITICAL_SECTION(self); if (dp->di_dbm == NULL) { PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - return -1; + goto done; } if ( dp->di_size < 0 ) { datum key; @@ -135,39 +139,51 @@ dbm_length(PyObject *self) size++; dp->di_size = size; } - return dp->di_size; + result = dp->di_size; +done:; + Py_END_CRITICAL_SECTION(); + return result; } static int dbm_bool(PyObject *self) { + int result; dbmobject *dp = dbmobject_CAST(self); _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); + Py_BEGIN_CRITICAL_SECTION(self); if (dp->di_dbm == NULL) { PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - return -1; + result = -1; + goto done; } if (dp->di_size > 0) { /* Known non-zero size. */ - return 1; + result = 1; + goto done; } if (dp->di_size == 0) { /* Known zero size. */ - return 0; + result = 0; + goto done; } /* Unknown size. Ensure DBM object has an entry. */ datum key = dbm_firstkey(dp->di_dbm); if (key.dptr == NULL) { /* Empty. Cache this fact. */ - dp->di_size = 0; - return 0; + result = dp->di_size = 0; + goto done; } + /* Non-empty. Don't cache the length since we don't know. */ - return 1; + result = 1; +done:; + Py_END_CRITICAL_SECTION(); + return result; } static PyObject * @@ -175,26 +191,35 @@ dbm_subscript(PyObject *self, PyObject *key) { datum drec, krec; Py_ssize_t tmp_size; + PyObject *result = NULL; dbmobject *dp = dbmobject_CAST(self); _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size)) { return NULL; } - krec.dsize = tmp_size; - check_dbmobject_open(dp, state->dbm_error); + + Py_BEGIN_CRITICAL_SECTION(self); + /* Can't use the macro here as it returns. */ + if (dp->di_dbm == NULL) { + PyErr_SetString(state->dbm_error, "DBM object has already been closed"); + goto done; + } drec = dbm_fetch(dp->di_dbm, krec); if ( drec.dptr == 0 ) { PyErr_SetObject(PyExc_KeyError, key); - return NULL; + goto done; } if ( dbm_error(dp->di_dbm) ) { dbm_clearerr(dp->di_dbm); PyErr_SetString(state->dbm_error, ""); - return NULL; + goto done; } - return PyBytes_FromStringAndSize(drec.dptr, drec.dsize); + result = PyBytes_FromStringAndSize(drec.dptr, drec.dsize); +done:; + Py_END_CRITICAL_SECTION(); + return result; } static int @@ -202,6 +227,7 @@ dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w) { datum krec, drec; Py_ssize_t tmp_size; + int result = -1; dbmobject *dp = dbmobject_CAST(self); if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) { @@ -212,10 +238,13 @@ dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w) _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); krec.dsize = tmp_size; + + Py_BEGIN_CRITICAL_SECTION(self); if (dp->di_dbm == NULL) { - PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - return -1; + PyErr_SetString(state->dbm_error, "DBM object has already been closed"); + goto done; } + dp->di_size = -1; if (w == NULL) { if ( dbm_delete(dp->di_dbm, krec) < 0 ) { @@ -228,31 +257,36 @@ dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w) else { PyErr_SetString(state->dbm_error, "cannot delete item from database"); } - return -1; + goto done; } } else { if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) { PyErr_SetString(PyExc_TypeError, "dbm mappings have bytes or string elements only"); - return -1; + goto done; } drec.dsize = tmp_size; if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { dbm_clearerr(dp->di_dbm); PyErr_SetString(state->dbm_error, "cannot add item to database"); - return -1; + goto done; } } if ( dbm_error(dp->di_dbm) ) { dbm_clearerr(dp->di_dbm); PyErr_SetString(state->dbm_error, ""); - return -1; + goto done; } - return 0; + + result = 0; +done:; + Py_END_CRITICAL_SECTION(); + return result; } /*[clinic input] +@critical_section _dbm.dbm.close Close the database. @@ -260,7 +294,7 @@ 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); @@ -270,6 +304,7 @@ _dbm_dbm_close_impl(dbmobject *self) } /*[clinic input] +@critical_section _dbm.dbm.keys cls: defining_class @@ -279,7 +314,7 @@ Return a list of all keys in the database. 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; @@ -315,35 +350,42 @@ dbm_contains(PyObject *self, PyObject *arg) dbmobject *dp = dbmobject_CAST(self); datum key, val; Py_ssize_t size; + int result = -1; _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); assert(state != NULL); + + Py_BEGIN_CRITICAL_SECTION(self); if ((dp)->di_dbm == NULL) { PyErr_SetString(state->dbm_error, "DBM object has already been closed"); - return -1; + goto done; } if (PyUnicode_Check(arg)) { key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size); key.dsize = size; if (key.dptr == NULL) - return -1; + goto done; } else if (!PyBytes_Check(arg)) { PyErr_Format(PyExc_TypeError, "dbm key must be bytes or string, not %.100s", Py_TYPE(arg)->tp_name); - return -1; + goto done; } else { key.dptr = PyBytes_AS_STRING(arg); key.dsize = PyBytes_GET_SIZE(arg); } val = dbm_fetch(dp->di_dbm, key); - return val.dptr != NULL; + result = val.dptr != NULL; +done:; + Py_END_CRITICAL_SECTION(); + return result; } /*[clinic input] +@critical_section _dbm.dbm.get cls: defining_class key: str(accept={str, robuffer}, zeroes=True) @@ -356,7 +398,7 @@ Return the value for key if present, otherwise default. 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); @@ -373,6 +415,7 @@ _dbm_dbm_get_impl(dbmobject *self, PyTypeObject *cls, const char *key, } /*[clinic input] +@critical_section _dbm.dbm.setdefault cls: defining_class key: str(accept={str, robuffer}, zeroes=True) @@ -387,7 +430,7 @@ If key is not in the database, it is inserted with default as the value. 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; @@ -427,6 +470,7 @@ _dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key, } /*[clinic input] +@critical_section _dbm.dbm.clear cls: defining_class / @@ -436,7 +480,7 @@ Remove all items from the database. 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); diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index ab2ebdba9249bf..7f9afabf652b88 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -80,6 +80,7 @@ typedef struct { #include "clinic/_gdbmmodule.c.h" +/* NOTE: Must be used within a critical section! */ #define check_gdbmobject_open(v, err) \ if ((v)->di_dbm == NULL) { \ PyErr_SetString(err, "GDBM object has already been closed"); \ @@ -144,8 +145,10 @@ gdbm_dealloc(PyObject *op) static Py_ssize_t gdbm_length(PyObject *op) { + Py_ssize_t result = -1; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); + Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); return -1; @@ -161,11 +164,11 @@ gdbm_length(PyObject *op) else { set_gdbm_error(state, "gdbm_count() error"); } - return -1; + goto done; } if (count > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "count exceeds PY_SSIZE_T_MAX"); - return -1; + goto done; } dp->di_size = count; #else @@ -185,37 +188,50 @@ gdbm_length(PyObject *op) dp->di_size = size; #endif } - return dp->di_size; + result = dp->di_size; +done:; + Py_END_CRITICAL_SECTION(); + return result; } static int gdbm_bool(PyObject *op) { + int result; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); + + Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - return -1; + result = -1; + goto done; } if (dp->di_size > 0) { /* Known non-zero size. */ - return 1; + result = 1; + goto done; } if (dp->di_size == 0) { /* Known zero size. */ - return 0; + result = 0; + goto done; } /* Unknown size. Ensure DBM object has an entry. */ datum key = gdbm_firstkey(dp->di_dbm); if (key.dptr == NULL) { /* Empty. Cache this fact. */ - dp->di_size = 0; - return 0; + result = dp->di_size = 0; + goto done; } /* Non-empty. Don't cache the length since we don't know. */ free(key.dptr); - return 1; + result = 1; + +done:; + Py_END_CRITICAL_SECTION(); + return result; } // Wrapper function for PyArg_Parse(o, "s#", &d.dptr, &d.size). @@ -242,7 +258,7 @@ parse_datum(PyObject *o, datum *d, const char *failmsg) static PyObject * gdbm_subscript(PyObject *op, PyObject *key) { - PyObject *v; + PyObject *v = NULL; datum drec, krec; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); @@ -250,18 +266,22 @@ gdbm_subscript(PyObject *op, PyObject *key) if (!parse_datum(key, &krec, NULL)) { return NULL; } + + Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - return NULL; + goto done; } drec = gdbm_fetch(dp->di_dbm, krec); if (drec.dptr == 0) { PyErr_SetObject(PyExc_KeyError, key); - return NULL; + goto done; } v = PyBytes_FromStringAndSize(drec.dptr, drec.dsize); free(drec.dptr); +done:; + Py_END_CRITICAL_SECTION(); return v; } @@ -294,16 +314,19 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) { datum krec, drec; const char *failmsg = "gdbm mappings have bytes or string indices only"; + int result = -1; gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); if (!parse_datum(v, &krec, failmsg)) { return -1; } + + Py_BEGIN_CRITICAL_SECTION(op); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - return -1; + goto done; } dp->di_size = -1; if (w == NULL) { @@ -314,12 +337,12 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) else { set_gdbm_error(state, "gdbm_delete() error"); } - return -1; + goto done; } } else { if (!parse_datum(w, &drec, failmsg)) { - return -1; + goto done; } errno = 0; if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) { @@ -329,13 +352,18 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) else { set_gdbm_error(state, "gdbm_store() error"); } - return -1; + goto done; } } - return 0; + result = 0; + +done:; + Py_END_CRITICAL_SECTION(); + return result; } /*[clinic input] +@critical_section _gdbm.gdbm.setdefault key: object @@ -348,7 +376,7 @@ Get value for key, or set it to default and return default if not present. 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; @@ -363,6 +391,7 @@ _gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key, } /*[clinic input] +@critical_section _gdbm.gdbm.close Close the database. @@ -370,7 +399,7 @@ 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); @@ -381,6 +410,7 @@ _gdbm_gdbm_close_impl(gdbmobject *self) /* XXX Should return a set or a set view */ /*[clinic input] +@critical_section _gdbm.gdbm.keys cls: defining_class @@ -390,7 +420,7 @@ Get a list of all keys in the database. 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; @@ -437,33 +467,40 @@ gdbm_contains(PyObject *self, PyObject *arg) gdbmobject *dp = (gdbmobject *)self; datum key; Py_ssize_t size; + int result = -1; _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); + Py_BEGIN_CRITICAL_SECTION(self); if ((dp)->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); - return -1; + goto done; } if (PyUnicode_Check(arg)) { key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size); key.dsize = size; if (key.dptr == NULL) - return -1; + goto done; } else if (!PyBytes_Check(arg)) { PyErr_Format(PyExc_TypeError, "gdbm key must be bytes or string, not %.100s", Py_TYPE(arg)->tp_name); - return -1; + goto done; } else { key.dptr = PyBytes_AS_STRING(arg); key.dsize = PyBytes_GET_SIZE(arg); } - return gdbm_exists(dp->di_dbm, key); + result = gdbm_exists(dp->di_dbm, key); + +done:; + Py_END_CRITICAL_SECTION(); + return result; } /*[clinic input] +@critical_section _gdbm.gdbm.firstkey cls: defining_class @@ -477,7 +514,7 @@ hash values, and won't be sorted by the key values. 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; @@ -497,6 +534,7 @@ _gdbm_gdbm_firstkey_impl(gdbmobject *self, PyTypeObject *cls) } /*[clinic input] +@critical_section _gdbm.gdbm.nextkey cls: defining_class @@ -517,7 +555,7 @@ to create a list in memory that contains them all: 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; @@ -539,6 +577,7 @@ _gdbm_gdbm_nextkey_impl(gdbmobject *self, PyTypeObject *cls, const char *key, } /*[clinic input] +@critical_section _gdbm.gdbm.reorganize cls: defining_class @@ -554,7 +593,7 @@ kept and reused as new (key,value) pairs are added. 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); @@ -573,6 +612,7 @@ _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls) } /*[clinic input] +@critical_section _gdbm.gdbm.sync cls: defining_class @@ -585,7 +625,7 @@ any unwritten data to be written to the disk. 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); @@ -595,6 +635,7 @@ _gdbm_gdbm_sync_impl(gdbmobject *self, PyTypeObject *cls) } /*[clinic input] +@critical_section _gdbm.gdbm.clear cls: defining_class / @@ -604,7 +645,7 @@ Remove all items from the database. 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); diff --git a/Modules/clinic/_dbmmodule.c.h b/Modules/clinic/_dbmmodule.c.h index 5e503194408776..091ce9edc43d4b 100644 --- a/Modules/clinic/_dbmmodule.c.h +++ b/Modules/clinic/_dbmmodule.c.h @@ -5,6 +5,7 @@ preserve #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__, @@ -22,7 +23,13 @@ _dbm_dbm_close_impl(dbmobject *self); 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__, @@ -40,11 +47,18 @@ _dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls); 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__, @@ -85,7 +99,9 @@ _dbm_dbm_get(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_ &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; @@ -131,7 +147,9 @@ _dbm_dbm_setdefault(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py &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; @@ -152,11 +170,18 @@ _dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls); 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__, @@ -221,4 +246,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=3b456118f231b160 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=279511ea7cda38dd input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_gdbmmodule.c.h b/Modules/clinic/_gdbmmodule.c.h index 00950f18e53541..6fd6aa3da50335 100644 --- a/Modules/clinic/_gdbmmodule.c.h +++ b/Modules/clinic/_gdbmmodule.c.h @@ -5,6 +5,7 @@ preserve #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__, @@ -70,7 +71,9 @@ _gdbm_gdbm_setdefault(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } 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; @@ -91,7 +94,13 @@ _gdbm_gdbm_close_impl(gdbmobject *self); 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__, @@ -109,11 +118,18 @@ _gdbm_gdbm_keys_impl(gdbmobject *self, PyTypeObject *cls); 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__, @@ -135,11 +151,18 @@ _gdbm_gdbm_firstkey_impl(gdbmobject *self, PyTypeObject *cls); 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__, @@ -187,7 +210,9 @@ _gdbm_gdbm_nextkey(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ &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; @@ -214,11 +239,18 @@ _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls); 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__, @@ -239,11 +271,18 @@ _gdbm_gdbm_sync_impl(gdbmobject *self, PyTypeObject *cls); 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__, @@ -261,11 +300,18 @@ _gdbm_gdbm_clear_impl(gdbmobject *self, PyTypeObject *cls); 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__, @@ -343,4 +389,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=d974cb39e4ee5d67 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8bca34ce9d4493dd input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index b7300def8dc75f..165e3c5abd95b8 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9538,6 +9538,18 @@ os_getlogin_impl(PyObject *module) } else result = PyErr_SetFromWindowsErr(GetLastError()); +#elif defined (HAVE_GETLOGIN_R) + /* AFAIK the maximum length should be 32, but this is not checkable */ + char name[64]; + int err = getlogin_r(name, sizeof(name)); + if (err) { + int old_errno = errno; + errno = -err; + posix_error(); + errno = old_errno; + } else { + result = PyUnicode_DecodeFSDefault(name); + } #else char *name; int old_errno = errno; diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index 2240e2078b2d98..dfcd9eb2d863ac 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -301,18 +301,28 @@ pwd_getpwall_impl(PyObject *module) struct passwd *p; if ((d = PyList_New(0)) == NULL) return NULL; + +#ifdef Py_GIL_DISABLED + static PyMutex getpwall_mutex = {0}; + PyMutex_Lock(&getpwall_mutex); +#endif setpwent(); + while ((p = getpwent()) != NULL) { PyObject *v = mkpwent(module, p); if (v == NULL || PyList_Append(d, v) != 0) { Py_XDECREF(v); - Py_DECREF(d); - endpwent(); - return NULL; + Py_CLEAR(d); + goto done; } Py_DECREF(v); } + +done: endpwent(); +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&getpwall_mutex); +#endif return d; } #endif diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 76e16bc3a2d3ec..2305a18dc81776 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -6269,7 +6269,7 @@ Return the true host name, a list of aliases, and a list of IP addresses,\n\ for a host. The host argument is a string giving a host name or IP number."); #endif -#ifdef HAVE_GETSERVBYNAME +#if defined(HAVE_GETSERVBYNAME_R) || defined (HAVE_GETSERVBYNAME) /* Python interface to getservbyname(name). This only returns the port number, since the other info is already known or not useful (like the list of aliases). */ @@ -6279,6 +6279,12 @@ static PyObject * socket_getservbyname(PyObject *self, PyObject *args) { const char *name, *proto=NULL; +#ifdef HAVE_GETSERVBYNAME_R + struct servent entry; + /* TODO: The man page says 1024 is usually enough, start with that and + retry if insufficient? */ + char buf[16384]; +#endif struct servent *sp; if (!PyArg_ParseTuple(args, "s|s:getservbyname", &name, &proto)) return NULL; @@ -6288,7 +6294,11 @@ socket_getservbyname(PyObject *self, PyObject *args) } Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_GETSERVBYNAME_R + getservbyname_r(name, proto, &entry, buf, sizeof(buf), &sp); +#else sp = getservbyname(name, proto); +#endif Py_END_ALLOW_THREADS if (sp == NULL) { PyErr_SetString(PyExc_OSError, "service/proto not found"); @@ -6305,7 +6315,7 @@ The optional protocol name, if given, should be 'tcp' or 'udp',\n\ otherwise any protocol will match."); #endif -#ifdef HAVE_GETSERVBYPORT +#if defined(HAVE_GETSERVBYPORT_R) || defined (HAVE_GETSERVBYPORT) /* Python interface to getservbyport(port). This only returns the service name, since the other info is already known or not useful (like the list of aliases). */ @@ -6316,6 +6326,12 @@ socket_getservbyport(PyObject *self, PyObject *args) { int port; const char *proto=NULL; +#ifdef HAVE_GETSERVBYPORT_R + struct servent entry; + /* TODO: The man page says 1024 is usually enough, start with that and + retry if insufficient? */ + char buf[16384]; +#endif struct servent *sp; if (!PyArg_ParseTuple(args, "i|s:getservbyport", &port, &proto)) return NULL; @@ -6331,7 +6347,11 @@ socket_getservbyport(PyObject *self, PyObject *args) } Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_GETSERVBYPORT_R + getservbyport_r(htons((short)port), proto, &entry, buf, sizeof(buf), &sp); +#else sp = getservbyport(htons((short)port), proto); +#endif Py_END_ALLOW_THREADS if (sp == NULL) { PyErr_SetString(PyExc_OSError, "port/proto not found"); @@ -6348,7 +6368,7 @@ The optional protocol name, if given, should be 'tcp' or 'udp',\n\ otherwise any protocol will match."); #endif -#ifdef HAVE_GETPROTOBYNAME +#if defined(HAVE_GETPROTOBYNAME_R) || defined (HAVE_GETPROTOBYNAME) /* Python interface to getprotobyname(name). This only returns the protocol number, since the other info is already known or not useful (like the list of aliases). */ @@ -6358,11 +6378,21 @@ static PyObject * socket_getprotobyname(PyObject *self, PyObject *args) { const char *name; +#ifdef HAVE_GETPROTOBYNAME_R + struct protoent entry; + /* TODO: The man page says 1024 is usually enough, start with that and + retry if insufficient? */ + char buf[16384]; +#endif struct protoent *sp; if (!PyArg_ParseTuple(args, "s:getprotobyname", &name)) return NULL; Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_GETPROTOBYNAME_R + getprotobyname_r(name, &entry, buf, sizeof(buf), &sp); +#else sp = getprotobyname(name); +#endif Py_END_ALLOW_THREADS if (sp == NULL) { PyErr_SetString(PyExc_OSError, "protocol not found"); @@ -7415,15 +7445,15 @@ static PyMethodDef socket_methods[] = { {"sethostname", socket_sethostname, METH_VARARGS, sethostname_doc}, #endif -#ifdef HAVE_GETSERVBYNAME +#if defined(HAVE_GETSERVBYNAME_R) || defined (HAVE_GETSERVBYNAME) {"getservbyname", socket_getservbyname, METH_VARARGS, getservbyname_doc}, #endif -#ifdef HAVE_GETSERVBYPORT +#if defined(HAVE_GETSERVBYPORT_R) || defined (HAVE_GETSERVBYPORT) {"getservbyport", socket_getservbyport, METH_VARARGS, getservbyport_doc}, #endif -#ifdef HAVE_GETPROTOBYNAME +#if defined (HAVE_GETPROTOBYNAME_R) || defined (HAVE_GETPROTOBYNAME) {"getprotobyname", socket_getprotobyname, METH_VARARGS, getprotobyname_doc}, #endif diff --git a/configure b/configure index decb8f2449d162..8261fe1fb8fdc4 100755 --- a/configure +++ b/configure @@ -19240,6 +19240,12 @@ if test "x$ac_cv_func_getlogin" = xyes then : printf "%s\n" "#define HAVE_GETLOGIN 1" >>confdefs.h +fi +ac_fn_c_check_func "$LINENO" "getlogin_r" "ac_cv_func_getlogin_r" +if test "x$ac_cv_func_getlogin_r" = xyes +then : + printf "%s\n" "#define HAVE_GETLOGIN_R 1" >>confdefs.h + fi ac_fn_c_check_func "$LINENO" "getpeername" "ac_cv_func_getpeername" if test "x$ac_cv_func_getpeername" = xyes @@ -22615,6 +22621,129 @@ fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for getservbyname_r" >&5 +printf %s "checking for getservbyname_r... " >&6; } +if test ${ac_cv_func_getservbyname_r+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main (void) +{ +void *x=getservbyname_r + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_func_getservbyname_r=yes +else case e in #( + e) ac_cv_func_getservbyname_r=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getservbyname_r" >&5 +printf "%s\n" "$ac_cv_func_getservbyname_r" >&6; } + if test "x$ac_cv_func_getservbyname_r" = xyes +then : + +printf "%s\n" "#define HAVE_GETSERVBYNAME_R 1" >>confdefs.h + +fi + + + + + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for getservbyport_r" >&5 +printf %s "checking for getservbyport_r... " >&6; } +if test ${ac_cv_func_getservbyport_r+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main (void) +{ +void *x=getservbyport_r + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_func_getservbyport_r=yes +else case e in #( + e) ac_cv_func_getservbyport_r=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getservbyport_r" >&5 +printf "%s\n" "$ac_cv_func_getservbyport_r" >&6; } + if test "x$ac_cv_func_getservbyport_r" = xyes +then : + +printf "%s\n" "#define HAVE_GETSERVBYPORT_R 1" >>confdefs.h + +fi + + + + + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for getprotobyname_r" >&5 +printf %s "checking for getprotobyname_r... " >&6; } +if test ${ac_cv_func_getprotobyname_r+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main (void) +{ +void *x=getprotobyname_r + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_func_getprotobyname_r=yes +else case e in #( + e) ac_cv_func_getprotobyname_r=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getprotobyname_r" >&5 +printf "%s\n" "$ac_cv_func_getprotobyname_r" >&6; } + if test "x$ac_cv_func_getprotobyname_r" = xyes +then : + +printf "%s\n" "#define HAVE_GETPROTOBYNAME_R 1" >>confdefs.h + +fi + + + + + diff --git a/configure.ac b/configure.ac index 004797b5233c20..d6dccdb31d5596 100644 --- a/configure.ac +++ b/configure.ac @@ -5137,7 +5137,7 @@ AC_CHECK_FUNCS([ \ faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ - getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ + getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin getlogin_r \ getpeername getpgid getpid getppid getpriority _getpty \ getpwent getpwnam_r getpwuid getpwuid_r getresgid getresuid getrusage getsid getspent \ getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \ @@ -5395,6 +5395,9 @@ PY_CHECK_NETDB_FUNC([getservbyport]) PY_CHECK_NETDB_FUNC([gethostbyname]) PY_CHECK_NETDB_FUNC([gethostbyaddr]) PY_CHECK_NETDB_FUNC([getprotobyname]) +PY_CHECK_NETDB_FUNC([getservbyname_r]) +PY_CHECK_NETDB_FUNC([getservbyport_r]) +PY_CHECK_NETDB_FUNC([getprotobyname_r]) dnl PY_CHECK_SOCKET_FUNC(FUNCTION) AC_DEFUN([PY_CHECK_SOCKET_FUNC], [PY_CHECK_FUNC([$1], [ diff --git a/pyconfig.h.in b/pyconfig.h.in index aa086d49e90a5b..a60a180d2c53f0 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -530,6 +530,9 @@ /* Define to 1 if you have the 'getlogin' function. */ #undef HAVE_GETLOGIN +/* Define to 1 if you have the 'getlogin_r' function. */ +#undef HAVE_GETLOGIN_R + /* Define to 1 if you have the 'getnameinfo' function. */ #undef HAVE_GETNAMEINFO @@ -557,6 +560,9 @@ /* Define if you have the 'getprotobyname' function. */ #undef HAVE_GETPROTOBYNAME +/* Define if you have the 'getprotobyname_r' function. */ +#undef HAVE_GETPROTOBYNAME_R + /* Define to 1 if you have the 'getpwent' function. */ #undef HAVE_GETPWENT @@ -587,9 +593,15 @@ /* Define if you have the 'getservbyname' function. */ #undef HAVE_GETSERVBYNAME +/* Define if you have the 'getservbyname_r' function. */ +#undef HAVE_GETSERVBYNAME_R + /* Define if you have the 'getservbyport' function. */ #undef HAVE_GETSERVBYPORT +/* Define if you have the 'getservbyport_r' function. */ +#undef HAVE_GETSERVBYPORT_R + /* Define to 1 if you have the 'getsid' function. */ #undef HAVE_GETSID