python / python/cpython

os.forkpty() and pty.fork() return FD with O_CLOEXEC

Open
#139,184 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

3.15 stdlib type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:
(pid, fd) = pty.fork() # os.forkpty()
print('os.get_inheritable(fd))

https://docs.python.org/3/library/os.html#os.forkpty
https://docs.python.org/3/library/pty.html#pty.fork

Say nothing about that.

Lib/pty.py:

def fork():
    """fork() -> (pid, master_fd)
    Fork and make the child a session leader with a controlling terminal."""

    try:
        pid, fd = os.forkpty()
    except (AttributeError, OSError):
        pass
    else:
        if pid == CHILD:
            try:
                os.setsid()
            except OSError:
                # os.forkpty() already set us session leader
                pass
        return pid, fd

    master_fd, slave_fd = openpty()
    pid = os.fork()
    if pid == CHILD:
        os.close(master_fd)
        os.login_tty(slave_fd)
    else:
        os.close(slave_fd)

    # Parent and child process.
    return pid, master_fd

Also uses openpty() which also does not set O_CLOEXEC

Meanwhile, there is a C function posix_openpt(int flags); which allows setting O_CLOEXEC.

Seems, fd-leak safe solution is to boilerplate all the actions by hand. i.e. same way as pty.fork() does, but also implement openpty() using posix_openpt() + ptsname() / ptsname_r().

Just setting O_CLOEXEC after openpty() or after calling C forkpty() is not enough because of race-condition and possible file descriptor leak.

So, CLOEXEC behavior should be documented and race-condition prevented

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-139408

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with Lib/pty.py and the documented os.forkpty() and pty.fork() entry points, then compare their openpty() paths with the posix_openpt(int flags) interface mentioned in the report. The work is done when CLOEXEC behavior is documented and the fork/openpty paths prevent the described race-condition file-descriptor leak.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.