From 434c1a02dff2d48649962650f1c6d6aa682b060b Mon Sep 17 00:00:00 2001 From: Duane Griffin Date: Thu, 17 Apr 2025 14:14:10 +1200 Subject: [PATCH 1/3] gh-127081: use getlogin_r if available The getlogin function is not thread-safe: replace with getlogin_r where available. Note that this function is untested (unit test is skipped with a note it caused CI failures as behaviour differs between NIX environments). --- .../2025-04-21-01-03-15.gh-issue-127081.WXRliX.rst | 2 ++ Modules/posixmodule.c | 12 ++++++++++++ configure | 6 ++++++ configure.ac | 2 +- pyconfig.h.in | 3 +++ 5 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-04-21-01-03-15.gh-issue-127081.WXRliX.rst diff --git a/Misc/NEWS.d/next/Library/2025-04-21-01-03-15.gh-issue-127081.WXRliX.rst b/Misc/NEWS.d/next/Library/2025-04-21-01-03-15.gh-issue-127081.WXRliX.rst new file mode 100644 index 00000000000000..63fed60ced03c5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-21-01-03-15.gh-issue-127081.WXRliX.rst @@ -0,0 +1,2 @@ +Fix libc thread safety issues with :mod:`os` by replacing ``getlogin`` with +``getlogin_r`` re-entrant version. 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/configure b/configure index decb8f2449d162..0c0e3028b08f39 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 diff --git a/configure.ac b/configure.ac index 004797b5233c20..69168683eedf2d 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 \ diff --git a/pyconfig.h.in b/pyconfig.h.in index aa086d49e90a5b..79262196b8a37b 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 From 0e9313876bb6be7cc8c3f43ee8da0fd74fa07236 Mon Sep 17 00:00:00 2001 From: Duane Griffin Date: Tue, 22 Apr 2025 11:39:38 +1200 Subject: [PATCH 2/3] Check for MAXLOGNAME and UT_NAMESIZE in configure and use them (in that order) if available, falling back to a hard-coded maximum login name length of 255 if neither is available. --- Modules/posixmodule.c | 12 +++++++++--- configure | 27 +++++++++++++++++++++++++++ configure.ac | 12 ++++++++++++ pyconfig.h.in | 6 ++++++ 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 165e3c5abd95b8..58289d1d526fe7 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9539,15 +9539,21 @@ 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]; +# if defined (HAVE_MAXLOGNAME) + char name[MAXLOGNAME + 1]; +# elif defined (HAVE_UT_NAMESIZE) + char name[UT_NAMESIZE + 1]; +# else + char name[256]; +# endif int err = getlogin_r(name, sizeof(name)); if (err) { int old_errno = errno; errno = -err; posix_error(); errno = old_errno; - } else { + } + else { result = PyUnicode_DecodeFSDefault(name); } #else diff --git a/configure b/configure index 0c0e3028b08f39..6149296d89d84f 100755 --- a/configure +++ b/configure @@ -23269,6 +23269,33 @@ fi +ac_fn_check_decl "$LINENO" "MAXLOGNAME" "ac_cv_have_decl_MAXLOGNAME" "#include +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl_MAXLOGNAME" = xyes +then : + +printf "%s\n" "#define HAVE_MAXLOGNAME 1" >>confdefs.h + +fi + +ac_fn_check_decl "$LINENO" "UT_NAMESIZE" "ac_cv_have_decl_UT_NAMESIZE" "#include +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl_UT_NAMESIZE" = xyes +then : + ac_have_decl=1 +else case e in #( + e) ac_have_decl=0 ;; +esac +fi +printf "%s\n" "#define HAVE_DECL_UT_NAMESIZE $ac_have_decl" >>confdefs.h +if test $ac_have_decl = 1 +then : + +printf "%s\n" "#define HAVE_UT_NAMESIZE 1" >>confdefs.h + +fi + + # check for openpty, login_tty, and forkpty diff --git a/configure.ac b/configure.ac index 69168683eedf2d..82df0b54c304c5 100644 --- a/configure.ac +++ b/configure.ac @@ -5427,6 +5427,18 @@ PY_CHECK_FUNC([setgroups], [ #endif ]) +AC_CHECK_DECL([MAXLOGNAME], + [AC_DEFINE([HAVE_MAXLOGNAME], [1], + [Define if you have the 'MAXLOGNAME' constant.])], + [], + [@%:@include ]) + +AC_CHECK_DECLS([UT_NAMESIZE], + [AC_DEFINE([HAVE_UT_NAMESIZE], [1], + [Define if you have the 'HAVE_UT_NAMESIZE' constant.])], + [], + [@%:@include ]) + # check for openpty, login_tty, and forkpty AC_CHECK_FUNCS([openpty], [], diff --git a/pyconfig.h.in b/pyconfig.h.in index 79262196b8a37b..fd8aa1c75b87ac 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1633,6 +1633,12 @@ /* Define to 1 if the system has the type '__uint128_t'. */ #undef HAVE___UINT128_T +/* Define to 1 if you have the MAXLOGNAME constant. */ +#undef HAVE_MAXLOGNAME + +/* Define to 1 if you have the UT_NAMESIZE constant. */ +#undef HAVE_UT_NAMESIZE + /* Define to 1 if 'major', 'minor', and 'makedev' are declared in . */ #undef MAJOR_IN_MKDEV From 675342cf59ffe53337d92af989b97dad687a10ea Mon Sep 17 00:00:00 2001 From: Duane Griffin Date: Tue, 22 Apr 2025 12:42:21 +1200 Subject: [PATCH 3/3] Fix-up pyconfig.h.in --- pyconfig.h.in | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pyconfig.h.in b/pyconfig.h.in index fd8aa1c75b87ac..7b4cd544fdcae9 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -264,6 +264,10 @@ */ #undef HAVE_DECL_TZNAME +/* Define to 1 if you have the declaration of 'UT_NAMESIZE', and to 0 if you + don't. */ +#undef HAVE_DECL_UT_NAMESIZE + /* Define to 1 if you have the device macros. */ #undef HAVE_DEVICE_MACROS @@ -798,6 +802,9 @@ /* Define this if you have the makedev macro. */ #undef HAVE_MAKEDEV +/* Define if you have the 'MAXLOGNAME' constant. */ +#undef HAVE_MAXLOGNAME + /* Define to 1 if you have the 'mbrtowc' function. */ #undef HAVE_MBRTOWC @@ -1566,6 +1573,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UTMP_H +/* Define if you have the 'HAVE_UT_NAMESIZE' constant. */ +#undef HAVE_UT_NAMESIZE + /* Define to 1 if you have the 'uuid_create' function. */ #undef HAVE_UUID_CREATE @@ -1633,12 +1643,6 @@ /* Define to 1 if the system has the type '__uint128_t'. */ #undef HAVE___UINT128_T -/* Define to 1 if you have the MAXLOGNAME constant. */ -#undef HAVE_MAXLOGNAME - -/* Define to 1 if you have the UT_NAMESIZE constant. */ -#undef HAVE_UT_NAMESIZE - /* Define to 1 if 'major', 'minor', and 'makedev' are declared in . */ #undef MAJOR_IN_MKDEV