From 3a159dffe693520a3ac15fb2c153913f55cade81 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 3 Mar 2026 03:07:49 +0900 Subject: [PATCH] gh-145335: Fix crash when passing -1 as fd in os.pathconf (GH-145390) (cherry picked from commit 5c3a47b94a39f87c36b1f36704d80775802ad034) Co-authored-by: AN Long Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Lib/test/test_os.py | 10 ++++++++++ .../2026-03-01-13-37-31.gh-issue-145335.e36kPJ.rst | 2 ++ Modules/posixmodule.c | 9 +++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-03-01-13-37-31.gh-issue-145335.e36kPJ.rst diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 7a28e7b4598c16..555c85f4857c2d 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2607,6 +2607,16 @@ def test_fpathconf_bad_fd(self): self.check(os.pathconf, "PC_NAME_MAX") self.check(os.fpathconf, "PC_NAME_MAX") + @unittest.skipUnless(hasattr(os, 'pathconf'), 'test needs os.pathconf()') + @unittest.skipIf( + support.linked_to_musl(), + 'musl fpathconf ignores the file descriptor and returns a constant', + ) + def test_pathconf_negative_fd_uses_fd_semantics(self): + with self.assertRaises(OSError) as ctx: + os.pathconf(-1, 1) + self.assertEqual(ctx.exception.errno, errno.EBADF) + @unittest.skipUnless(hasattr(os, 'ftruncate'), 'test needs os.ftruncate()') def test_ftruncate(self): self.check(os.truncate, 0) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-03-01-13-37-31.gh-issue-145335.e36kPJ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-01-13-37-31.gh-issue-145335.e36kPJ.rst new file mode 100644 index 00000000000000..42ed85c7da31ac --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-01-13-37-31.gh-issue-145335.e36kPJ.rst @@ -0,0 +1,2 @@ +Fix a crash in :func:`os.pathconf` when called with ``-1`` as the path +argument. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index cb4fe1e6c8ca03..bb9ef0e6da6c77 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1251,6 +1251,8 @@ get_posix_state(PyObject *module) * Contains a file descriptor if path.accept_fd was true * and the caller provided a signed integer instead of any * sort of string. + * path.is_fd + * True if path was provided as a file descriptor. * * WARNING: if your "path" parameter is optional, and is * unspecified, path_converter will never get called. @@ -1303,6 +1305,7 @@ typedef struct { const wchar_t *wide; const char *narrow; int fd; + bool is_fd; int value_error; Py_ssize_t length; PyObject *object; @@ -1312,7 +1315,7 @@ typedef struct { #define PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, \ make_wide, suppress_value_error, allow_fd) \ {function_name, argument_name, nullable, nonstrict, make_wide, \ - suppress_value_error, allow_fd, NULL, NULL, -1, 0, 0, NULL, NULL} + suppress_value_error, allow_fd, NULL, NULL, -1, false, 0, 0, NULL, NULL} #ifdef MS_WINDOWS #define PATH_T_INITIALIZE_P(function_name, argument_name, nullable, \ nonstrict, suppress_value_error, allow_fd) \ @@ -1446,6 +1449,7 @@ path_converter(PyObject *o, void *p) } path->wide = NULL; path->narrow = NULL; + path->is_fd = true; goto success_exit; } else { @@ -13841,8 +13845,9 @@ os_pathconf_impl(PyObject *module, path_t *path, int name) errno = 0; #ifdef HAVE_FPATHCONF - if (path->fd != -1) + if (path->is_fd) { limit = fpathconf(path->fd, name); + } else #endif limit = pathconf(path->narrow, name);