https://github.com/python/cpython/commit/7cafd76a7fc6e52d93dd11e454a49c4dbebdf080 commit: 7cafd76a7fc6e52d93dd11e454a49c4dbebdf080 branch: main author: Nadeshiko Manju <[email protected]> committer: vstinner <[email protected]> date: 2025-10-10T10:56:10+02:00 summary:
gh-139184: Set O_CLOEXEC for master_fd when calling os.forkpty() (#139408) Signed-off-by: Manjusaka <[email protected]> Co-authored-by: Shamil <[email protected]> Co-authored-by: Bénédikt Tran <[email protected]> files: A Misc/NEWS.d/next/Library/2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst M Doc/library/os.rst M Doc/library/pty.rst M Lib/test/test_pty.py M Modules/clinic/posixmodule.c.h M Modules/posixmodule.c diff --git a/Doc/library/os.rst b/Doc/library/os.rst index ba3d189454941f..540eaa09d0e32a 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -4592,6 +4592,8 @@ written in Python, such as a mail server's external command delivery program. master end of the pseudo-terminal. For a more portable approach, use the :mod:`pty` module. If an error occurs :exc:`OSError` is raised. + The returned file descriptor *fd* is :ref:`non-inheritable <fd_inheritance>`. + .. audit-event:: os.forkpty "" os.forkpty .. warning:: @@ -4608,6 +4610,9 @@ written in Python, such as a mail server's external command delivery program. threads, this now raises a :exc:`DeprecationWarning`. See the longer explanation on :func:`os.fork`. + .. versionchanged:: next + The returned file descriptor is now made non-inheritable. + .. availability:: Unix, not WASI, not Android, not iOS. diff --git a/Doc/library/pty.rst b/Doc/library/pty.rst index 1a44bb13a841de..9fef8760b627a5 100644 --- a/Doc/library/pty.rst +++ b/Doc/library/pty.rst @@ -33,9 +33,14 @@ The :mod:`pty` module defines the following functions: file descriptor connected to the child's controlling terminal (and also to the child's standard input and output). + The returned file descriptor *fd* is :ref:`non-inheritable <fd_inheritance>`. + .. warning:: On macOS the use of this function is unsafe when mixed with using higher-level system APIs, and that includes using :mod:`urllib.request`. + .. versionchanged:: next + The returned file descriptor is now made non-inheritable. + .. function:: openpty() diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index fbba7025ac4abf..a2018e864445e1 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -230,6 +230,7 @@ def test_fork(self): os._exit(2) os._exit(4) else: + self.assertFalse(os.get_inheritable(master_fd)) debug("Waiting for child (%d) to finish." % pid) # In verbose mode, we have to consume the debug output from the # child or the child will block, causing this test to hang in the diff --git a/Misc/NEWS.d/next/Library/2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst b/Misc/NEWS.d/next/Library/2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst new file mode 100644 index 00000000000000..d50cdeaadc45a6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst @@ -0,0 +1 @@ +:func:`os.forkpty` does now make the returned file descriptor non-inheritable. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index dddf98d127c15f..3d9863ad179d3c 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -5035,7 +5035,8 @@ PyDoc_STRVAR(os_forkpty__doc__, "Returns a tuple of (pid, master_fd).\n" "Like fork(), return pid of 0 to the child process,\n" "and pid of child to the parent process.\n" -"To both, return fd of newly opened pseudo-terminal."); +"To both, return fd of newly opened pseudo-terminal.\n" +"The master_fd is non-inheritable."); #define OS_FORKPTY_METHODDEF \ {"forkpty", (PyCFunction)os_forkpty, METH_NOARGS, os_forkpty__doc__}, @@ -13446,4 +13447,4 @@ os__emscripten_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py #ifndef OS__EMSCRIPTEN_LOG_METHODDEF #define OS__EMSCRIPTEN_LOG_METHODDEF #endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */ -/*[clinic end generated code: output=b5b370c499174f85 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=47ace1528820858b input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 4189d300856068..7a2e36bf294205 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9018,11 +9018,12 @@ Returns a tuple of (pid, master_fd). Like fork(), return pid of 0 to the child process, and pid of child to the parent process. To both, return fd of newly opened pseudo-terminal. +The master_fd is non-inheritable. [clinic start generated code]*/ static PyObject * os_forkpty_impl(PyObject *module) -/*[clinic end generated code: output=60d0a5c7512e4087 input=f1f7f4bae3966010]*/ +/*[clinic end generated code: output=60d0a5c7512e4087 input=24765e0f33275b3b]*/ { int master_fd = -1; pid_t pid; @@ -9048,6 +9049,12 @@ os_forkpty_impl(PyObject *module) } else { /* parent: release the import lock. */ PyOS_AfterFork_Parent(); + /* set O_CLOEXEC on master_fd */ + if (_Py_set_inheritable(master_fd, 0, NULL) < 0) { + PyErr_FormatUnraisable("Exception ignored when setting master_fd " + "non-inheritable in forkpty()"); + } + // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. if (warn_about_fork_with_threads("forkpty") < 0) return NULL; @@ -9055,6 +9062,7 @@ os_forkpty_impl(PyObject *module) if (pid == -1) { return posix_error(); } + return Py_BuildValue("(Ni)", PyLong_FromPid(pid), master_fd); } #endif /* HAVE_FORKPTY */ _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
