Skip to content

gh-116738: Make grp module thread-safe #135434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 14, 2025
Prev Previous commit
Next Next commit
gh-116738: Consolidate group function calls under one mutex
  • Loading branch information
yoney committed Jul 7, 2025
commit f51d0d9217caef9c996536f6a932fa9ca35533fb
24 changes: 13 additions & 11 deletions Modules/grpmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ get_grp_state(PyObject *module)

static struct PyModuleDef grpmodule;

/* Mutex to protect calls to getgrgid(), getgrnam(), and getgrent().
* These functions return pointer to static data structure, which
* may be overwritten by any subsequent calls. */
static PyMutex group_db_mutex = {0};

#define DEFAULT_BUFFER_SIZE 1024

static PyObject *
Expand Down Expand Up @@ -168,15 +173,14 @@ grp_getgrgid_impl(PyObject *module, PyObject *id)

Py_END_ALLOW_THREADS
#else
static PyMutex getgrgid_mutex = {0};
PyMutex_Lock(&getgrgid_mutex);
PyMutex_Lock(&group_db_mutex);
// The getgrgid() function need not be thread-safe.
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgrgid.html
p = getgrgid(gid);
#endif
if (p == NULL) {
#ifndef HAVE_GETGRGID_R
PyMutex_Unlock(&getgrgid_mutex);
PyMutex_Unlock(&group_db_mutex);
#endif
PyMem_RawFree(buf);
if (nomem == 1) {
Expand All @@ -193,7 +197,7 @@ grp_getgrgid_impl(PyObject *module, PyObject *id)
#ifdef HAVE_GETGRGID_R
PyMem_RawFree(buf);
#else
PyMutex_Unlock(&getgrgid_mutex);
PyMutex_Unlock(&group_db_mutex);
#endif
return retval;
}
Expand Down Expand Up @@ -258,15 +262,14 @@ grp_getgrnam_impl(PyObject *module, PyObject *name)

Py_END_ALLOW_THREADS
#else
static PyMutex getgrnam_mutex = {0};
PyMutex_Lock(&getgrnam_mutex);
PyMutex_Lock(&group_db_mutex);
// The getgrnam() function need not be thread-safe.
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgrnam.html
p = getgrnam(name_chars);
#endif
if (p == NULL) {
#ifndef HAVE_GETGRNAM_R
PyMutex_Unlock(&getgrnam_mutex);
PyMutex_Unlock(&group_db_mutex);
#endif
if (nomem == 1) {
PyErr_NoMemory();
Expand All @@ -278,7 +281,7 @@ grp_getgrnam_impl(PyObject *module, PyObject *name)
}
retval = mkgrent(module, p);
#ifndef HAVE_GETGRNAM_R
PyMutex_Unlock(&getgrnam_mutex);
PyMutex_Unlock(&group_db_mutex);
#endif
out:
PyMem_RawFree(buf);
Expand All @@ -304,8 +307,7 @@ grp_getgrall_impl(PyObject *module)
return NULL;
}

static PyMutex getgrall_mutex = {0};
PyMutex_Lock(&getgrall_mutex);
PyMutex_Lock(&group_db_mutex);
setgrent();

struct group *p;
Expand All @@ -325,7 +327,7 @@ grp_getgrall_impl(PyObject *module)

done:
endgrent();
PyMutex_Unlock(&getgrall_mutex);
PyMutex_Unlock(&group_db_mutex);
return d;
}

Expand Down
Loading