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]

Reply via email to