gh-126316: Make grp.getgrall() thread-safe: add a mutex (#127055)

grpmodule.c is no longer built with the limited C API, since PyMutex
is excluded from the limited C API.
This commit is contained in:
Victor Stinner 2024-11-21 15:47:24 +01:00 committed by GitHub
parent 60ec854bc2
commit 3c2bd66e21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 107 additions and 22 deletions

View File

@ -982,6 +982,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hi)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hi));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hook)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hook));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hour)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hour));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(id));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ident)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ident));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(identity_hint)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(identity_hint));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ignore)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ignore));

View File

@ -471,6 +471,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(hi) STRUCT_FOR_ID(hi)
STRUCT_FOR_ID(hook) STRUCT_FOR_ID(hook)
STRUCT_FOR_ID(hour) STRUCT_FOR_ID(hour)
STRUCT_FOR_ID(id)
STRUCT_FOR_ID(ident) STRUCT_FOR_ID(ident)
STRUCT_FOR_ID(identity_hint) STRUCT_FOR_ID(identity_hint)
STRUCT_FOR_ID(ignore) STRUCT_FOR_ID(ignore)

View File

@ -980,6 +980,7 @@ extern "C" {
INIT_ID(hi), \ INIT_ID(hi), \
INIT_ID(hook), \ INIT_ID(hook), \
INIT_ID(hour), \ INIT_ID(hour), \
INIT_ID(id), \
INIT_ID(ident), \ INIT_ID(ident), \
INIT_ID(identity_hint), \ INIT_ID(identity_hint), \
INIT_ID(ignore), \ INIT_ID(ignore), \

View File

@ -1680,6 +1680,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1); assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(id);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(ident); string = &_Py_ID(ident);
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));

View File

@ -0,0 +1,2 @@
:mod:`grp`: Make :func:`grp.getgrall` thread-safe by adding a mutex. Patch
by Victor Stinner.

View File

@ -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(grp_getgrgid__doc__, PyDoc_STRVAR(grp_getgrgid__doc__,
"getgrgid($module, /, id)\n" "getgrgid($module, /, id)\n"
"--\n" "--\n"
@ -11,21 +17,49 @@ PyDoc_STRVAR(grp_getgrgid__doc__,
"If id is not valid, raise KeyError."); "If id is not valid, raise KeyError.");
#define GRP_GETGRGID_METHODDEF \ #define GRP_GETGRGID_METHODDEF \
{"getgrgid", (PyCFunction)(void(*)(void))grp_getgrgid, METH_VARARGS|METH_KEYWORDS, grp_getgrgid__doc__}, {"getgrgid", _PyCFunction_CAST(grp_getgrgid), METH_FASTCALL|METH_KEYWORDS, grp_getgrgid__doc__},
static PyObject * static PyObject *
grp_getgrgid_impl(PyObject *module, PyObject *id); grp_getgrgid_impl(PyObject *module, PyObject *id);
static PyObject * static PyObject *
grp_getgrgid(PyObject *module, PyObject *args, PyObject *kwargs) grp_getgrgid(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
static char *_keywords[] = {"id", NULL}; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 1
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(id), },
};
#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[] = {"id", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "getgrgid",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[1];
PyObject *id; PyObject *id;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:getgrgid", _keywords, args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
&id)) /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
if (!args) {
goto exit; goto exit;
}
id = args[0];
return_value = grp_getgrgid_impl(module, id); return_value = grp_getgrgid_impl(module, id);
exit: exit:
@ -41,21 +75,53 @@ PyDoc_STRVAR(grp_getgrnam__doc__,
"If name is not valid, raise KeyError."); "If name is not valid, raise KeyError.");
#define GRP_GETGRNAM_METHODDEF \ #define GRP_GETGRNAM_METHODDEF \
{"getgrnam", (PyCFunction)(void(*)(void))grp_getgrnam, METH_VARARGS|METH_KEYWORDS, grp_getgrnam__doc__}, {"getgrnam", _PyCFunction_CAST(grp_getgrnam), METH_FASTCALL|METH_KEYWORDS, grp_getgrnam__doc__},
static PyObject * static PyObject *
grp_getgrnam_impl(PyObject *module, PyObject *name); grp_getgrnam_impl(PyObject *module, PyObject *name);
static PyObject * static PyObject *
grp_getgrnam(PyObject *module, PyObject *args, PyObject *kwargs) grp_getgrnam(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
static char *_keywords[] = {"name", NULL}; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 1
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(name), },
};
#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[] = {"name", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "getgrnam",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[1];
PyObject *name; PyObject *name;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "U:getgrnam", _keywords, args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
&name)) /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
if (!args) {
goto exit; goto exit;
}
if (!PyUnicode_Check(args[0])) {
_PyArg_BadArgument("getgrnam", "argument 'name'", "str", args[0]);
goto exit;
}
name = args[0];
return_value = grp_getgrnam_impl(module, name); return_value = grp_getgrnam_impl(module, name);
exit: exit:
@ -82,4 +148,4 @@ grp_getgrall(PyObject *module, PyObject *Py_UNUSED(ignored))
{ {
return grp_getgrall_impl(module); return grp_getgrall_impl(module);
} }
/*[clinic end generated code: output=81f180beb67fc585 input=a9049054013a1b77]*/ /*[clinic end generated code: output=2154194308dab038 input=a9049054013a1b77]*/

View File

@ -1,9 +1,8 @@
/* UNIX group file access module */ /* UNIX group file access module */
// Need limited C API version 3.13 for PyMem_RawRealloc() // Argument Clinic uses the internal C API
#include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_BUILD_CORE_BUILTIN
#ifndef Py_GIL_DISABLED # define Py_BUILD_CORE_MODULE 1
#define Py_LIMITED_API 0x030d0000
#endif #endif
#include "Python.h" #include "Python.h"
@ -281,23 +280,33 @@ static PyObject *
grp_getgrall_impl(PyObject *module) grp_getgrall_impl(PyObject *module)
/*[clinic end generated code: output=585dad35e2e763d7 input=d7df76c825c367df]*/ /*[clinic end generated code: output=585dad35e2e763d7 input=d7df76c825c367df]*/
{ {
PyObject *d; PyObject *d = PyList_New(0);
struct group *p; if (d == NULL) {
if ((d = PyList_New(0)) == NULL)
return NULL; return NULL;
}
static PyMutex getgrall_mutex = {0};
PyMutex_Lock(&getgrall_mutex);
setgrent(); setgrent();
struct group *p;
while ((p = getgrent()) != NULL) { while ((p = getgrent()) != NULL) {
// gh-126316: Don't release the mutex around mkgrent() since
// setgrent()/endgrent() are not reentrant / thread-safe. A deadlock
// is unlikely since mkgrent() should not be able to call arbitrary
// Python code.
PyObject *v = mkgrent(module, p); PyObject *v = mkgrent(module, p);
if (v == NULL || PyList_Append(d, v) != 0) { if (v == NULL || PyList_Append(d, v) != 0) {
Py_XDECREF(v); Py_XDECREF(v);
Py_DECREF(d); Py_CLEAR(d);
endgrent(); goto done;
return NULL;
} }
Py_DECREF(v); Py_DECREF(v);
} }
done:
endgrent(); endgrent();
PyMutex_Unlock(&getgrall_mutex);
return d; return d;
} }

View File

@ -739,6 +739,7 @@ Modules/expat/xmlrole.c - declClose -
Modules/expat/xmlrole.c - error - Modules/expat/xmlrole.c - error -
## other ## other
Modules/grpmodule.c grp_getgrall_impl getgrall_mutex -
Modules/_io/_iomodule.c - _PyIO_Module - Modules/_io/_iomodule.c - _PyIO_Module -
Modules/_sqlite/module.c - _sqlite3module - Modules/_sqlite/module.c - _sqlite3module -
Modules/clinic/md5module.c.h _md5_md5 _keywords - Modules/clinic/md5module.c.h _md5_md5 _keywords -

Can't render this file because it has a wrong number of fields in line 4.