diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index fbba7025ac4abf..385169efcd5b1d 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -12,6 +12,7 @@ raise unittest.SkipTest("pty is not available on this platform") import errno +import fcntl import os import pty import tty @@ -230,6 +231,8 @@ def test_fork(self): os._exit(2) os._exit(4) else: + flags = fcntl.fcntl(master_fd, fcntl.F_GETFD) + self.assertTrue(flags & fcntl.FD_CLOEXEC) 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..7388afaa94ad48 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-29-14-15-20.gh-issue-139184.dNl9O4.rst @@ -0,0 +1 @@ +Set O_CLOEXEC for master_fd when call os.forkpty diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index b7a0110226590e..be2b501e4fa889 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9059,6 +9059,9 @@ 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) + goto error; // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. if (warn_about_fork_with_threads("forkpty") < 0) return NULL; @@ -9066,7 +9069,12 @@ os_forkpty_impl(PyObject *module) if (pid == -1) { return posix_error(); } + return Py_BuildValue("(Ni)", PyLong_FromPid(pid), master_fd); +error: + if (master_fd != -1) + close(master_fd); + return NULL; } #endif /* HAVE_FORKPTY */