mirror of
https://github.com/python/cpython.git
synced 2025-11-25 04:34:37 +00:00
gh-116738: Make pwd module thread-safe (#136695)
Some checks are pending
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / (push) Blocked by required conditions
Tests / Windows MSI (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Ubuntu SSL tests with AWS-LC (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / Undefined behavior sanitizer (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
mypy / Run mypy on Lib/_pyrepl (push) Waiting to run
mypy / Run mypy on Lib/test/libregrtest (push) Waiting to run
mypy / Run mypy on Lib/tomllib (push) Waiting to run
mypy / Run mypy on Tools/build (push) Waiting to run
mypy / Run mypy on Tools/cases_generator (push) Waiting to run
mypy / Run mypy on Tools/clinic (push) Waiting to run
mypy / Run mypy on Tools/jit (push) Waiting to run
mypy / Run mypy on Tools/peg_generator (push) Waiting to run
Some checks are pending
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / (push) Blocked by required conditions
Tests / Windows MSI (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Ubuntu SSL tests with AWS-LC (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / Undefined behavior sanitizer (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
mypy / Run mypy on Lib/_pyrepl (push) Waiting to run
mypy / Run mypy on Lib/test/libregrtest (push) Waiting to run
mypy / Run mypy on Lib/tomllib (push) Waiting to run
mypy / Run mypy on Tools/build (push) Waiting to run
mypy / Run mypy on Tools/cases_generator (push) Waiting to run
mypy / Run mypy on Tools/clinic (push) Waiting to run
mypy / Run mypy on Tools/jit (push) Waiting to run
mypy / Run mypy on Tools/peg_generator (push) Waiting to run
Make the pwd module functions getpwuid(), getpwnam(), and getpwall() thread-safe. These changes apply to scenarios where the GIL is disabled or in subinterpreter use cases.
This commit is contained in:
parent
22af5d35a6
commit
eddc8c0a1d
5 changed files with 66 additions and 15 deletions
33
Lib/test/test_free_threading/test_pwd.py
Normal file
33
Lib/test/test_free_threading/test_pwd.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import unittest
|
||||
|
||||
from test.support import threading_helper
|
||||
from test.support.threading_helper import run_concurrently
|
||||
|
||||
from test import test_pwd
|
||||
|
||||
|
||||
NTHREADS = 10
|
||||
|
||||
|
||||
@threading_helper.requires_working_threading()
|
||||
class TestPwd(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.test_pwd = test_pwd.PwdTest()
|
||||
|
||||
def test_racing_test_values(self):
|
||||
# test_pwd.test_values() calls pwd.getpwall() and checks the entries
|
||||
run_concurrently(
|
||||
worker_func=self.test_pwd.test_values, nthreads=NTHREADS
|
||||
)
|
||||
|
||||
def test_racing_test_values_extended(self):
|
||||
# test_pwd.test_values_extended() calls pwd.getpwall(), pwd.getpwnam(),
|
||||
# pwd.getpwduid() and checks the entries
|
||||
run_concurrently(
|
||||
worker_func=self.test_pwd.test_values_extended,
|
||||
nthreads=NTHREADS,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
@ -0,0 +1 @@
|
|||
Make functions in :mod:`pwd` thread-safe on the :term:`free threaded <free threading>` build.
|
||||
6
Modules/clinic/pwdmodule.c.h
generated
6
Modules/clinic/pwdmodule.c.h
generated
|
|
@ -2,6 +2,8 @@
|
|||
preserve
|
||||
[clinic start generated code]*/
|
||||
|
||||
#include "pycore_modsupport.h" // _PyArg_BadArgument()
|
||||
|
||||
PyDoc_STRVAR(pwd_getpwuid__doc__,
|
||||
"getpwuid($module, uidobj, /)\n"
|
||||
"--\n"
|
||||
|
|
@ -34,7 +36,7 @@ pwd_getpwnam(PyObject *module, PyObject *arg)
|
|||
PyObject *name;
|
||||
|
||||
if (!PyUnicode_Check(arg)) {
|
||||
PyErr_Format(PyExc_TypeError, "getpwnam() argument must be str, not %T", arg);
|
||||
_PyArg_BadArgument("getpwnam", "argument", "str", arg);
|
||||
goto exit;
|
||||
}
|
||||
name = arg;
|
||||
|
|
@ -71,4 +73,4 @@ pwd_getpwall(PyObject *module, PyObject *Py_UNUSED(ignored))
|
|||
#ifndef PWD_GETPWALL_METHODDEF
|
||||
#define PWD_GETPWALL_METHODDEF
|
||||
#endif /* !defined(PWD_GETPWALL_METHODDEF) */
|
||||
/*[clinic end generated code: output=dac88d500f6d6f49 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=5a8fb12939ff4ea3 input=a9049054013a1b77]*/
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
|
||||
/* UNIX password file access module */
|
||||
|
||||
// Need limited C API version 3.13 for PyMem_RawRealloc()
|
||||
#include "pyconfig.h" // Py_GIL_DISABLED
|
||||
#ifndef Py_GIL_DISABLED
|
||||
# define Py_LIMITED_API 0x030d0000
|
||||
#endif
|
||||
|
||||
#include "Python.h"
|
||||
#include "posixmodule.h"
|
||||
|
||||
|
|
@ -69,6 +63,11 @@ get_pwd_state(PyObject *module)
|
|||
|
||||
static struct PyModuleDef pwdmodule;
|
||||
|
||||
/* Mutex to protect calls to getpwuid(), getpwnam(), and getpwent().
|
||||
* These functions return pointer to static data structure, which
|
||||
* may be overwritten by any subsequent calls. */
|
||||
static PyMutex pwd_db_mutex = {0};
|
||||
|
||||
#define DEFAULT_BUFFER_SIZE 1024
|
||||
|
||||
static PyObject *
|
||||
|
|
@ -182,9 +181,15 @@ pwd_getpwuid(PyObject *module, PyObject *uidobj)
|
|||
|
||||
Py_END_ALLOW_THREADS
|
||||
#else
|
||||
PyMutex_Lock(&pwd_db_mutex);
|
||||
// The getpwuid() function is not required to be thread-safe.
|
||||
// https://pubs.opengroup.org/onlinepubs/009604499/functions/getpwuid.html
|
||||
p = getpwuid(uid);
|
||||
#endif
|
||||
if (p == NULL) {
|
||||
#ifndef HAVE_GETPWUID_R
|
||||
PyMutex_Unlock(&pwd_db_mutex);
|
||||
#endif
|
||||
PyMem_RawFree(buf);
|
||||
if (nomem == 1) {
|
||||
return PyErr_NoMemory();
|
||||
|
|
@ -200,6 +205,8 @@ pwd_getpwuid(PyObject *module, PyObject *uidobj)
|
|||
retval = mkpwent(module, p);
|
||||
#ifdef HAVE_GETPWUID_R
|
||||
PyMem_RawFree(buf);
|
||||
#else
|
||||
PyMutex_Unlock(&pwd_db_mutex);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -265,9 +272,15 @@ pwd_getpwnam_impl(PyObject *module, PyObject *name)
|
|||
|
||||
Py_END_ALLOW_THREADS
|
||||
#else
|
||||
PyMutex_Lock(&pwd_db_mutex);
|
||||
// The getpwnam() function is not required to be thread-safe.
|
||||
// https://pubs.opengroup.org/onlinepubs/009604599/functions/getpwnam.html
|
||||
p = getpwnam(name_chars);
|
||||
#endif
|
||||
if (p == NULL) {
|
||||
#ifndef HAVE_GETPWNAM_R
|
||||
PyMutex_Unlock(&pwd_db_mutex);
|
||||
#endif
|
||||
if (nomem == 1) {
|
||||
PyErr_NoMemory();
|
||||
}
|
||||
|
|
@ -278,6 +291,9 @@ pwd_getpwnam_impl(PyObject *module, PyObject *name)
|
|||
goto out;
|
||||
}
|
||||
retval = mkpwent(module, p);
|
||||
#ifndef HAVE_GETPWNAM_R
|
||||
PyMutex_Unlock(&pwd_db_mutex);
|
||||
#endif
|
||||
out:
|
||||
PyMem_RawFree(buf);
|
||||
Py_DECREF(bytes);
|
||||
|
|
@ -302,12 +318,12 @@ pwd_getpwall_impl(PyObject *module)
|
|||
if ((d = PyList_New(0)) == NULL)
|
||||
return NULL;
|
||||
|
||||
#ifdef Py_GIL_DISABLED
|
||||
static PyMutex getpwall_mutex = {0};
|
||||
PyMutex_Lock(&getpwall_mutex);
|
||||
#endif
|
||||
PyMutex_Lock(&pwd_db_mutex);
|
||||
int failure = 0;
|
||||
PyObject *v = NULL;
|
||||
// The setpwent(), getpwent() and endpwent() functions are not required to
|
||||
// be thread-safe.
|
||||
// https://pubs.opengroup.org/onlinepubs/009696799/functions/setpwent.html
|
||||
setpwent();
|
||||
while ((p = getpwent()) != NULL) {
|
||||
v = mkpwent(module, p);
|
||||
|
|
@ -321,9 +337,7 @@ pwd_getpwall_impl(PyObject *module)
|
|||
|
||||
done:
|
||||
endpwent();
|
||||
#ifdef Py_GIL_DISABLED
|
||||
PyMutex_Unlock(&getpwall_mutex);
|
||||
#endif
|
||||
PyMutex_Unlock(&pwd_db_mutex);
|
||||
if (failure) {
|
||||
Py_XDECREF(v);
|
||||
Py_CLEAR(d);
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ Python/sysmodule.c - _preinit_xoptions -
|
|||
Modules/faulthandler.c faulthandler_dump_traceback reentrant -
|
||||
Modules/faulthandler.c faulthandler_dump_c_stack reentrant -
|
||||
Modules/grpmodule.c - group_db_mutex -
|
||||
Modules/pwdmodule.c - pwd_db_mutex -
|
||||
Python/pylifecycle.c _Py_FatalErrorFormat reentrant -
|
||||
Python/pylifecycle.c fatal_error reentrant -
|
||||
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 4.
|
Loading…
Add table
Add a link
Reference in a new issue