741 lines
55 KiB
HTML
741 lines
55 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>PEP 446 – Make newly created file descriptors non-inheritable | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0446/">
|
||
<link rel="stylesheet" href="../_static/style.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
|
||
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
|
||
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
|
||
<meta property="og:title" content='PEP 446 – Make newly created file descriptors non-inheritable | peps.python.org'>
|
||
<meta property="og:description" content="Leaking file descriptors in child processes causes various annoying issues and is a known major security vulnerability. Using the subprocess module with the close_fds parameter set to True is not possible in all cases.">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0446/">
|
||
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
||
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
||
<meta property="og:image:alt" content="Python PEPs">
|
||
<meta property="og:image:width" content="200">
|
||
<meta property="og:image:height" content="200">
|
||
<meta name="description" content="Leaking file descriptors in child processes causes various annoying issues and is a known major security vulnerability. Using the subprocess module with the close_fds parameter set to True is not possible in all cases.">
|
||
<meta name="theme-color" content="#3776ab">
|
||
</head>
|
||
<body>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Following system colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="9"></circle>
|
||
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected dark colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected light colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="5"></circle>
|
||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||
</svg>
|
||
</symbol>
|
||
</svg>
|
||
<script>
|
||
|
||
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
|
||
</script>
|
||
<section id="pep-page-section">
|
||
<header>
|
||
<h1>Python Enhancement Proposals</h1>
|
||
<ul class="breadcrumbs">
|
||
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 446</li>
|
||
</ul>
|
||
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
|
||
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
|
||
</button>
|
||
</header>
|
||
<article>
|
||
<section id="pep-content">
|
||
<h1 class="page-title">PEP 446 – Make newly created file descriptors non-inheritable</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Victor Stinner <vstinner at python.org></dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
|
||
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-even">Created<span class="colon">:</span></dt>
|
||
<dd class="field-even">05-Aug-2013</dd>
|
||
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-odd">3.4</dd>
|
||
<dt class="field-even">Replaces<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="../pep-0433/">433</a></dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
|
||
<li><a class="reference internal" href="#inheritance-of-file-descriptors">Inheritance of File Descriptors</a></li>
|
||
<li><a class="reference internal" href="#inheritance-of-file-descriptors-on-windows">Inheritance of File Descriptors on Windows</a></li>
|
||
<li><a class="reference internal" href="#only-inherit-some-handles-on-windows">Only Inherit Some Handles on Windows</a></li>
|
||
<li><a class="reference internal" href="#inheritance-of-file-descriptors-on-unix">Inheritance of File Descriptors on UNIX</a></li>
|
||
<li><a class="reference internal" href="#issues-with-inheritable-file-descriptors">Issues with Inheritable File Descriptors</a></li>
|
||
<li><a class="reference internal" href="#security-vulnerability">Security Vulnerability</a></li>
|
||
<li><a class="reference internal" href="#issues-fixed-in-the-subprocess-module">Issues fixed in the subprocess module</a></li>
|
||
<li><a class="reference internal" href="#atomic-creation-of-non-inheritable-file-descriptors">Atomic Creation of non-inheritable File Descriptors</a></li>
|
||
<li><a class="reference internal" href="#status-of-python-3-3">Status of Python 3.3</a></li>
|
||
<li><a class="reference internal" href="#closing-all-open-file-descriptors">Closing All Open File Descriptors</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#proposal">Proposal</a><ul>
|
||
<li><a class="reference internal" href="#non-inheritable-file-descriptors">Non-inheritable File Descriptors</a></li>
|
||
<li><a class="reference internal" href="#new-functions-and-methods">New Functions And Methods</a></li>
|
||
<li><a class="reference internal" href="#other-changes">Other Changes</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backward-compatibility">Backward Compatibility</a></li>
|
||
<li><a class="reference internal" href="#related-work">Related Work</a></li>
|
||
<li><a class="reference internal" href="#rejected-alternatives">Rejected Alternatives</a><ul>
|
||
<li><a class="reference internal" href="#add-a-new-open-noinherit-function">Add a new open_noinherit() function</a></li>
|
||
<li><a class="reference internal" href="#pep-433">PEP 433</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#python-issues">Python Issues</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>Leaking file descriptors in child processes causes various annoying
|
||
issues and is a known major security vulnerability. Using the
|
||
<code class="docutils literal notranslate"><span class="pre">subprocess</span></code> module with the <em>close_fds</em> parameter set to <code class="docutils literal notranslate"><span class="pre">True</span></code> is
|
||
not possible in all cases.</p>
|
||
<p>This PEP proposes to make all file descriptors created by Python
|
||
non-inheritable by default to reduce the risk of these issues. This PEP
|
||
fixes also a race condition in multi-threaded applications on operating
|
||
systems supporting atomic flags to create non-inheritable file
|
||
descriptors.</p>
|
||
<p>We are aware of the code breakage this is likely to cause, and doing it
|
||
anyway for the good of mankind. (Details in the section “Backward
|
||
Compatibility” below.)</p>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<section id="inheritance-of-file-descriptors">
|
||
<h3><a class="toc-backref" href="#inheritance-of-file-descriptors" role="doc-backlink">Inheritance of File Descriptors</a></h3>
|
||
<p>Each operating system handles the inheritance of file descriptors
|
||
differently. Windows creates non-inheritable handles by default, whereas
|
||
UNIX and the POSIX API on Windows create inheritable file descriptors by
|
||
default. Python prefers the POSIX API over the native Windows API, to
|
||
have a single code base and to use the same type for file descriptors,
|
||
and so it creates inheritable file descriptors.</p>
|
||
<p>There is one exception: <code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code> creates non-inheritable pipes on
|
||
Windows, whereas it creates inheritable pipes on UNIX. The reason is an
|
||
implementation artifact: <code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code> calls <code class="docutils literal notranslate"><span class="pre">CreatePipe()</span></code> on Windows
|
||
(native API), whereas it calls <code class="docutils literal notranslate"><span class="pre">pipe()</span></code> on UNIX (POSIX API). The call
|
||
to <code class="docutils literal notranslate"><span class="pre">CreatePipe()</span></code> was added in Python in 1994, before the introduction
|
||
of <code class="docutils literal notranslate"><span class="pre">pipe()</span></code> in the POSIX API in Windows 98. The <a class="reference external" href="http://bugs.python.org/issue4708">issue #4708</a> proposes to change <code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code> on
|
||
Windows to create inheritable pipes.</p>
|
||
</section>
|
||
<section id="inheritance-of-file-descriptors-on-windows">
|
||
<h3><a class="toc-backref" href="#inheritance-of-file-descriptors-on-windows" role="doc-backlink">Inheritance of File Descriptors on Windows</a></h3>
|
||
<p>On Windows, the native type of file objects is handles (C type
|
||
<code class="docutils literal notranslate"><span class="pre">HANDLE</span></code>). These handles have a <code class="docutils literal notranslate"><span class="pre">HANDLE_FLAG_INHERIT</span></code> flag which
|
||
defines if a handle can be inherited in a child process or not. For the
|
||
POSIX API, the C runtime (CRT) also provides file descriptors (C type
|
||
<code class="docutils literal notranslate"><span class="pre">int</span></code>). The handle of a file descriptor can be retrieve using the
|
||
function <code class="docutils literal notranslate"><span class="pre">_get_osfhandle(fd)</span></code>. A file descriptor can be created from a
|
||
handle using the function <code class="docutils literal notranslate"><span class="pre">_open_osfhandle(handle)</span></code>.</p>
|
||
<p>Using <a class="reference external" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx">CreateProcess()</a>,
|
||
handles are only inherited if their inheritable flag
|
||
(<code class="docutils literal notranslate"><span class="pre">HANDLE_FLAG_INHERIT</span></code>) is set <strong>and</strong> the <code class="docutils literal notranslate"><span class="pre">bInheritHandles</span></code>
|
||
parameter of <code class="docutils literal notranslate"><span class="pre">CreateProcess()</span></code> is <code class="docutils literal notranslate"><span class="pre">TRUE</span></code>; all file descriptors
|
||
except standard streams (0, 1, 2) are closed in the child process, even
|
||
if <code class="docutils literal notranslate"><span class="pre">bInheritHandles</span></code> is <code class="docutils literal notranslate"><span class="pre">TRUE</span></code>. Using the <code class="docutils literal notranslate"><span class="pre">spawnv()</span></code> function, all
|
||
inheritable handles and all inheritable file descriptors are inherited
|
||
in the child process. This function uses the undocumented fields
|
||
<em>cbReserved2</em> and <em>lpReserved2</em> of the <a class="reference external" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms686331%28v=vs.85%29.aspx">STARTUPINFO</a>
|
||
structure to pass an array of file descriptors.</p>
|
||
<p>To replace standard streams (stdin, stdout, stderr) using
|
||
<code class="docutils literal notranslate"><span class="pre">CreateProcess()</span></code>, the <code class="docutils literal notranslate"><span class="pre">STARTF_USESTDHANDLES</span></code> flag must be set in
|
||
the <em>dwFlags</em> field of the <code class="docutils literal notranslate"><span class="pre">STARTUPINFO</span></code> structure and the
|
||
<em>bInheritHandles</em> parameter of <code class="docutils literal notranslate"><span class="pre">CreateProcess()</span></code> must be set to
|
||
<code class="docutils literal notranslate"><span class="pre">TRUE</span></code>. So when at least one standard stream is replaced, all
|
||
inheritable handles are inherited by the child process.</p>
|
||
<p>The default value of the <em>close_fds</em> parameter of <code class="docutils literal notranslate"><span class="pre">subprocess</span></code> process
|
||
is <code class="docutils literal notranslate"><span class="pre">True</span></code> (<code class="docutils literal notranslate"><span class="pre">bInheritHandles=FALSE</span></code>) if <em>stdin</em>, <em>stdout</em> and
|
||
<em>stderr</em> parameters are <code class="docutils literal notranslate"><span class="pre">None</span></code>, <code class="docutils literal notranslate"><span class="pre">False</span></code> (<code class="docutils literal notranslate"><span class="pre">bInheritHandles=TRUE</span></code>)
|
||
otherwise.</p>
|
||
<p>See also:</p>
|
||
<ul class="simple">
|
||
<li><a class="reference external" href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms724466%28v=vs.85%29.aspx">Handle Inheritance</a></li>
|
||
<li><a class="reference external" href="http://stackoverflow.com/questions/12058911/can-tcp-socket-handles-be-set-not-inheritable">Stackoverflow: Can TCP SOCKET handles be set not inheritable?</a></li>
|
||
</ul>
|
||
</section>
|
||
<section id="only-inherit-some-handles-on-windows">
|
||
<h3><a class="toc-backref" href="#only-inherit-some-handles-on-windows" role="doc-backlink">Only Inherit Some Handles on Windows</a></h3>
|
||
<p>Since Windows Vista, <code class="docutils literal notranslate"><span class="pre">CreateProcess()</span></code> supports an extension of the
|
||
STARTUPINFO structure: the <a class="reference external" href="http://msdn.microsoft.com/en-us/library/ms686329%28v=vs.85%29.aspx">STARTUPINFOEX structure</a>.
|
||
Using this new structure, it is possible to specify a list of handles to
|
||
inherit: <code class="docutils literal notranslate"><span class="pre">PROC_THREAD_ATTRIBUTE_HANDLE_LIST</span></code>. Read <a class="reference external" href="http://blogs.msdn.com/b/oldnewthing/archive/2011/12/16/10248328.aspx">Programmatically
|
||
controlling which handles are inherited by new processes in Win32</a>
|
||
(Raymond Chen, Dec 2011) for more information.</p>
|
||
<p>Before Windows Vista, it is possible to make handles inheritable and
|
||
call <code class="docutils literal notranslate"><span class="pre">CreateProcess()</span></code> with <code class="docutils literal notranslate"><span class="pre">bInheritHandles=TRUE</span></code>. This option
|
||
works if all other handles are non-inheritable. There is a race
|
||
condition: if another thread calls <code class="docutils literal notranslate"><span class="pre">CreateProcess()</span></code> with
|
||
<code class="docutils literal notranslate"><span class="pre">bInheritHandles=TRUE</span></code>, handles will also be inherited in the second
|
||
process.</p>
|
||
<p>Microsoft suggests to use a lock to avoid the race condition: read
|
||
<a class="reference external" href="http://support.microsoft.com/kb/315939/en-us">Q315939: PRB: Child Inherits Unintended Handles During CreateProcess
|
||
Call</a> (last review:
|
||
November 2006). The <a class="reference external" href="http://bugs.python.org/issue16500">Python issue #16500 “Add an atfork module”</a> proposes to add such lock, it can
|
||
be used to make handles non-inheritable without the race condition. Such
|
||
lock only protects against a race condition between Python threads; C
|
||
threads are not protected.</p>
|
||
<p>Another option is to duplicate handles that must be inherited, passing the
|
||
values of the duplicated handles to the child process, so the child
|
||
process can steal duplicated handles using <a class="reference external" href="http://msdn.microsoft.com/en-us/library/windows/apps/ms724251%28v=vs.85%29.aspx">DuplicateHandle()</a>
|
||
with <code class="docutils literal notranslate"><span class="pre">DUPLICATE_CLOSE_SOURCE</span></code>. Handle values change between the
|
||
parent and the child process because the handles are duplicated (twice);
|
||
the parent and/or the child process must be adapted to handle this
|
||
change. If the child program cannot be modified, an intermediate program
|
||
can be used to steal handles from the parent process before spawning the
|
||
final child program. The intermediate program has to pass the handle from the
|
||
child process to the parent process. The parent may have to close
|
||
duplicated handles if all handles were not stolen, for example if the
|
||
intermediate process fails. If the command line is used to pass the
|
||
handle values, the command line must be modified when handles are
|
||
duplicated, because their values are modified.</p>
|
||
<p>This PEP does not include a solution to this problem because there is no
|
||
perfect solution working on all Windows versions. This point is deferred
|
||
until use cases relying on handle or file descriptor inheritance on
|
||
Windows are well known, so we can choose the best solution and carefully
|
||
test its implementation.</p>
|
||
</section>
|
||
<section id="inheritance-of-file-descriptors-on-unix">
|
||
<h3><a class="toc-backref" href="#inheritance-of-file-descriptors-on-unix" role="doc-backlink">Inheritance of File Descriptors on UNIX</a></h3>
|
||
<p>POSIX provides a <em>close-on-exec</em> flag on file descriptors to automatically
|
||
close a file descriptor when the C function <code class="docutils literal notranslate"><span class="pre">execv()</span></code> is
|
||
called. File descriptors with the <em>close-on-exec</em> flag cleared are
|
||
inherited in the child process, file descriptors with the flag set are
|
||
closed in the child process.</p>
|
||
<p>The flag can be set in two syscalls (one to get current flags, a second
|
||
to set new flags) using <code class="docutils literal notranslate"><span class="pre">fcntl()</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">int</span> <span class="n">flags</span><span class="p">,</span> <span class="n">res</span><span class="p">;</span>
|
||
<span class="n">flags</span> <span class="o">=</span> <span class="n">fcntl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">F_GETFD</span><span class="p">);</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">flags</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="o">/*</span> <span class="n">handle</span> <span class="n">the</span> <span class="n">error</span> <span class="o">*/</span> <span class="p">}</span>
|
||
<span class="n">flags</span> <span class="o">|=</span> <span class="n">FD_CLOEXEC</span><span class="p">;</span>
|
||
<span class="o">/*</span> <span class="ow">or</span> <span class="s2">"flags &= ~FD_CLOEXEC;"</span> <span class="n">to</span> <span class="n">clear</span> <span class="n">the</span> <span class="n">flag</span> <span class="o">*/</span>
|
||
<span class="n">res</span> <span class="o">=</span> <span class="n">fcntl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">F_SETFD</span><span class="p">,</span> <span class="n">flags</span><span class="p">);</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">res</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="o">/*</span> <span class="n">handle</span> <span class="n">the</span> <span class="n">error</span> <span class="o">*/</span> <span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>FreeBSD, Linux, Mac OS X, NetBSD, OpenBSD and QNX also support setting
|
||
the flag in a single syscall using ioctl():</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>int res;
|
||
res = ioctl(fd, FIOCLEX, 0);
|
||
if (!res) { /* handle the error */ }
|
||
</pre></div>
|
||
</div>
|
||
<p>NOTE: The <em>close-on-exec</em> flag has no effect on <code class="docutils literal notranslate"><span class="pre">fork()</span></code>: all file
|
||
descriptors are inherited by the child process. The <a class="reference external" href="http://bugs.python.org/issue16500">Python issue #16500
|
||
“Add an atfork module”</a> proposes to
|
||
add a new <code class="docutils literal notranslate"><span class="pre">atfork</span></code> module to execute code at fork, which may be used to
|
||
automatically close file descriptors.</p>
|
||
</section>
|
||
<section id="issues-with-inheritable-file-descriptors">
|
||
<h3><a class="toc-backref" href="#issues-with-inheritable-file-descriptors" role="doc-backlink">Issues with Inheritable File Descriptors</a></h3>
|
||
<p>Most of the time, inheritable file descriptors “leaked” to child
|
||
processes are not noticed, because they don’t cause major bugs. It does
|
||
not mean that these bugs must not be fixed.</p>
|
||
<p>Two common issues with inherited file descriptors:</p>
|
||
<ul class="simple">
|
||
<li>On Windows, a directory cannot be removed before all file handles open
|
||
in the directory are closed. The same issue can be seen with files,
|
||
except if the file was created with the <code class="docutils literal notranslate"><span class="pre">FILE_SHARE_DELETE</span></code> flag
|
||
(<code class="docutils literal notranslate"><span class="pre">O_TEMPORARY</span></code> mode for <code class="docutils literal notranslate"><span class="pre">open()</span></code>).</li>
|
||
<li>If a listening socket is leaked to a child process, the socket address
|
||
cannot be reused before the parent and child processes terminated. For
|
||
example, if a web server spawns a new program to handle a process, and
|
||
the server restarts while the program is not done, the server cannot
|
||
start because the TCP port is still in use.</li>
|
||
</ul>
|
||
<p>Example of issues in open source projects:</p>
|
||
<ul class="simple">
|
||
<li><a class="reference external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=147659">Mozilla (Firefox)</a>:
|
||
open since 2002-05</li>
|
||
<li><a class="reference external" href="https://bugs.freedesktop.org/show_bug.cgi?id=15947">dbus library</a>:
|
||
fixed in 2008-05 (<a class="reference external" href="http://cgit.freedesktop.org/dbus/dbus/commit/?id=e2bc7232069b14b7299cb8b2eab436f60a232007">dbus commit</a>),
|
||
close file descriptors in the child process</li>
|
||
<li><a class="reference external" href="https://bugzilla.redhat.com/show_bug.cgi?id=390591">autofs</a>:
|
||
fixed in 2009-02, set the CLOEXEC flag</li>
|
||
<li><a class="reference external" href="https://bugzilla.redhat.com/show_bug.cgi?id=528134">qemu</a>:
|
||
fixed in 2009-12 (<a class="reference external" href="http://git.qemu.org/?p=qemu.git;a=commit;h=40ff6d7e8dceca227e7f8a3e8e0d58b2c66d19b4">qemu commit</a>),
|
||
set CLOEXEC flag</li>
|
||
<li><a class="reference external" href="https://trac.torproject.org/projects/tor/ticket/2029">Tor</a>:
|
||
fixed in 2010-12, set CLOEXEC flag</li>
|
||
<li><a class="reference external" href="http://caml.inria.fr/mantis/view.php?id=5256">OCaml</a>: open since
|
||
2011-04, “PR#5256: Processes opened using Unix.open_process* inherit
|
||
all opened file descriptors (including sockets)”</li>
|
||
<li><a class="reference external" href="https://zeromq.jira.com/browse/LIBZMQ-408">ØMQ</a>:
|
||
open since 2012-08</li>
|
||
<li><a class="reference external" href="https://bugzilla.redhat.com/show_bug.cgi?id=837033">Squid</a>:
|
||
open since 2012-07</li>
|
||
</ul>
|
||
<p>See also: <a class="reference external" href="http://danwalsh.livejournal.com/53603.html">Excuse me son, but your code is leaking !!!</a> (Dan Walsh, March 2012)
|
||
for SELinux issues with leaked file descriptors.</p>
|
||
</section>
|
||
<section id="security-vulnerability">
|
||
<h3><a class="toc-backref" href="#security-vulnerability" role="doc-backlink">Security Vulnerability</a></h3>
|
||
<p>Leaking sensitive file handles and file descriptors can lead to security
|
||
vulnerabilities. An untrusted child process might read sensitive data like
|
||
passwords or take control of the parent process though a leaked file
|
||
descriptor. With a leaked listening socket, a child process can accept
|
||
new connections to read sensitive data.</p>
|
||
<p>Example of vulnerabilities:</p>
|
||
<ul class="simple">
|
||
<li><a class="reference external" href="http://www.securityfocus.com/archive/1/348368">Hijacking Apache https by mod_php</a> (2003)<ul>
|
||
<li>Apache: <a class="reference external" href="https://issues.apache.org/bugzilla/show_bug.cgi?id=46425">Apr should set FD_CLOEXEC if APR_FOPEN_NOCLEANUP is not set</a>:
|
||
fixed in 2009</li>
|
||
<li>PHP: <a class="reference external" href="https://bugs.php.net/bug.php?id=38915">system() (and similar) don’t cleanup opened handles of Apache</a>: open since 2006</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference external" href="http://cwe.mitre.org/data/definitions/403.html">CWE-403: Exposure of File Descriptor to Unintended Control Sphere</a> (2008)</li>
|
||
<li><a class="reference external" href="http://www.openssh.com/txt/portable-keysign-rand-helper.adv">OpenSSH Security Advisory: portable-keysign-rand-helper.adv</a>
|
||
(2011)</li>
|
||
</ul>
|
||
<p>Read also the CERT Secure Coding Standards:
|
||
<a class="reference external" href="https://www.securecoding.cert.org/confluence/display/seccode/FIO42-C.+Ensure+files+are+properly+closed+when+they+are+no+longer+needed">FIO42-C. Ensure files are properly closed when they are no longer
|
||
needed</a>.</p>
|
||
</section>
|
||
<section id="issues-fixed-in-the-subprocess-module">
|
||
<h3><a class="toc-backref" href="#issues-fixed-in-the-subprocess-module" role="doc-backlink">Issues fixed in the subprocess module</a></h3>
|
||
<p>Inherited file descriptors caused 4 issues in the <code class="docutils literal notranslate"><span class="pre">subprocess</span></code>
|
||
module:</p>
|
||
<ul class="simple">
|
||
<li><a class="reference external" href="http://bugs.python.org/issue2320">Issue #2320: Race condition in subprocess using stdin</a> (opened in 2008)</li>
|
||
<li><a class="reference external" href="http://bugs.python.org/issue3006">Issue #3006: subprocess.Popen causes socket to remain open after
|
||
close</a> (opened in 2008)</li>
|
||
<li><a class="reference external" href="http://bugs.python.org/issue7213">Issue #7213: subprocess leaks open file descriptors between Popen
|
||
instances causing hangs</a>
|
||
(opened in 2009)</li>
|
||
<li><a class="reference external" href="http://bugs.python.org/issue12786">Issue #12786: subprocess wait() hangs when stdin is closed</a> (opened in 2011)</li>
|
||
</ul>
|
||
<p>These issues were fixed in Python 3.2 by 4 different changes in the
|
||
<code class="docutils literal notranslate"><span class="pre">subprocess</span></code> module:</p>
|
||
<ul class="simple">
|
||
<li>Pipes are now non-inheritable;</li>
|
||
<li>The default value of the <em>close_fds</em> parameter is now <code class="docutils literal notranslate"><span class="pre">True</span></code>,
|
||
with one exception on Windows: the default value is <code class="docutils literal notranslate"><span class="pre">False</span></code> if
|
||
at least one standard stream is replaced;</li>
|
||
<li>A new <em>pass_fds</em> parameter has been added;</li>
|
||
<li>Creation of a <code class="docutils literal notranslate"><span class="pre">_posixsubprocess</span></code> module implemented in C.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="atomic-creation-of-non-inheritable-file-descriptors">
|
||
<h3><a class="toc-backref" href="#atomic-creation-of-non-inheritable-file-descriptors" role="doc-backlink">Atomic Creation of non-inheritable File Descriptors</a></h3>
|
||
<p>In a multi-threaded application, an inheritable file descriptor may be
|
||
created just before a new program is spawned, before the file descriptor
|
||
is made non-inheritable. In this case, the file descriptor is leaked to
|
||
the child process. This race condition could be avoided if the file
|
||
descriptor is created directly non-inheritable.</p>
|
||
<p>FreeBSD, Linux, Mac OS X, Windows and many other operating systems
|
||
support creating non-inheritable file descriptors with the inheritable
|
||
flag cleared atomically at the creation of the file descriptor.</p>
|
||
<p>A new <code class="docutils literal notranslate"><span class="pre">WSA_FLAG_NO_HANDLE_INHERIT</span></code> flag for <code class="docutils literal notranslate"><span class="pre">WSASocket()</span></code> was added
|
||
in Windows 7 SP1 and Windows Server 2008 R2 SP1 to create
|
||
non-inheritable sockets. If this flag is used on an older Windows
|
||
version (ex: Windows XP SP3), <code class="docutils literal notranslate"><span class="pre">WSASocket()</span></code> fails with
|
||
<code class="docutils literal notranslate"><span class="pre">WSAEPROTOTYPE</span></code>.</p>
|
||
<p>On UNIX, new flags were added for files and sockets:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">O_CLOEXEC</span></code>: available on Linux (2.6.23), FreeBSD (8.3),
|
||
Mac OS 10.8, OpenBSD 5.0, Solaris 11, QNX, BeOS, next NetBSD release
|
||
(6.1?). This flag is part of POSIX.1-2008.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">SOCK_CLOEXEC</span></code> flag for <code class="docutils literal notranslate"><span class="pre">socket()</span></code> and <code class="docutils literal notranslate"><span class="pre">socketpair()</span></code>,
|
||
available on Linux 2.6.27, OpenBSD 5.2, NetBSD 6.0.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">fcntl()</span></code>: <code class="docutils literal notranslate"><span class="pre">F_DUPFD_CLOEXEC</span></code> flag, available on Linux 2.6.24,
|
||
OpenBSD 5.0, FreeBSD 9.1, NetBSD 6.0, Solaris 11. This flag is part
|
||
of POSIX.1-2008.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">fcntl()</span></code>: <code class="docutils literal notranslate"><span class="pre">F_DUP2FD_CLOEXEC</span></code> flag, available on FreeBSD 9.1
|
||
and Solaris 11.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">recvmsg()</span></code>: <code class="docutils literal notranslate"><span class="pre">MSG_CMSG_CLOEXEC</span></code>, available on Linux 2.6.23,
|
||
NetBSD 6.0.</li>
|
||
</ul>
|
||
<p>On Linux older than 2.6.23, <code class="docutils literal notranslate"><span class="pre">O_CLOEXEC</span></code> flag is simply ignored. So
|
||
<code class="docutils literal notranslate"><span class="pre">fcntl()</span></code> must be called to check if the file descriptor is
|
||
non-inheritable: <code class="docutils literal notranslate"><span class="pre">O_CLOEXEC</span></code> is not supported if the <code class="docutils literal notranslate"><span class="pre">FD_CLOEXEC</span></code>
|
||
flag is missing. On Linux older than 2.6.27, <code class="docutils literal notranslate"><span class="pre">socket()</span></code> or
|
||
<code class="docutils literal notranslate"><span class="pre">socketpair()</span></code> fail with <code class="docutils literal notranslate"><span class="pre">errno</span></code> set to <code class="docutils literal notranslate"><span class="pre">EINVAL</span></code> if the
|
||
<code class="docutils literal notranslate"><span class="pre">SOCK_CLOEXEC</span></code> flag is set in the socket type.</p>
|
||
<p>New functions:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">dup3()</span></code>: available on Linux 2.6.27 (and glibc 2.9)</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">pipe2()</span></code>: available on Linux 2.6.27 (and glibc 2.9)</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">accept4()</span></code>: available on Linux 2.6.28 (and glibc 2.10)</li>
|
||
</ul>
|
||
<p>On Linux older than 2.6.28, <code class="docutils literal notranslate"><span class="pre">accept4()</span></code> fails with <code class="docutils literal notranslate"><span class="pre">errno</span></code> set to
|
||
<code class="docutils literal notranslate"><span class="pre">ENOSYS</span></code>.</p>
|
||
<p>Summary:</p>
|
||
<table class="docutils align-default">
|
||
<thead>
|
||
<tr class="row-odd"><th class="head">Operating System</th>
|
||
<th class="head">Atomic File</th>
|
||
<th class="head">Atomic Socket</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="row-even"><td>FreeBSD</td>
|
||
<td>8.3 (2012)</td>
|
||
<td>X</td>
|
||
</tr>
|
||
<tr class="row-odd"><td>Linux</td>
|
||
<td>2.6.23 (2007)</td>
|
||
<td>2.6.27 (2008)</td>
|
||
</tr>
|
||
<tr class="row-even"><td>Mac OS X</td>
|
||
<td>10.8 (2012)</td>
|
||
<td>X</td>
|
||
</tr>
|
||
<tr class="row-odd"><td>NetBSD</td>
|
||
<td>6.1 (?)</td>
|
||
<td>6.0 (2012)</td>
|
||
</tr>
|
||
<tr class="row-even"><td>OpenBSD</td>
|
||
<td>5.0 (2011)</td>
|
||
<td>5.2 (2012)</td>
|
||
</tr>
|
||
<tr class="row-odd"><td>Solaris</td>
|
||
<td>11 (2011)</td>
|
||
<td>X</td>
|
||
</tr>
|
||
<tr class="row-even"><td>Windows</td>
|
||
<td>XP (2001)</td>
|
||
<td>Seven SP1 (2011), 2008 R2 SP1 (2011)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Legend:</p>
|
||
<ul class="simple">
|
||
<li>“Atomic File”: first version of the operating system supporting
|
||
creating atomically a non-inheritable file descriptor using
|
||
<code class="docutils literal notranslate"><span class="pre">open()</span></code></li>
|
||
<li>“Atomic Socket”: first version of the operating system supporting
|
||
creating atomically a non-inheritable socket</li>
|
||
<li>“X”: not supported yet</li>
|
||
</ul>
|
||
<p>See also:</p>
|
||
<ul class="simple">
|
||
<li><a class="reference external" href="http://udrepper.livejournal.com/20407.html">Secure File Descriptor Handling</a> (Ulrich Drepper,
|
||
2008)</li>
|
||
<li><a class="reference external" href="http://lwn.net/Articles/412131/">Ghosts of Unix past, part 2: Conflated designs</a> (Neil Brown, 2010) explains the
|
||
history of <code class="docutils literal notranslate"><span class="pre">O_CLOEXEC</span></code> and <code class="docutils literal notranslate"><span class="pre">O_NONBLOCK</span></code> flags</li>
|
||
<li><a class="reference external" href="http://lwn.net/Articles/292843/">File descriptor handling changes in 2.6.27</a></li>
|
||
<li><a class="reference external" href="https://wiki.freebsd.org/AtomicCloseOnExec">FreeBSD: atomic close on exec</a></li>
|
||
</ul>
|
||
</section>
|
||
<section id="status-of-python-3-3">
|
||
<h3><a class="toc-backref" href="#status-of-python-3-3" role="doc-backlink">Status of Python 3.3</a></h3>
|
||
<p>Python 3.3 creates inheritable file descriptors on all platforms, except
|
||
<code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code> which creates non-inheritable file descriptors on Windows.</p>
|
||
<p>New constants and functions related to the atomic creation of
|
||
non-inheritable file descriptors were added to Python 3.3:
|
||
<code class="docutils literal notranslate"><span class="pre">os.O_CLOEXEC</span></code>, <code class="docutils literal notranslate"><span class="pre">os.pipe2()</span></code> and <code class="docutils literal notranslate"><span class="pre">socket.SOCK_CLOEXEC</span></code>.</p>
|
||
<p>On UNIX, the <code class="docutils literal notranslate"><span class="pre">subprocess</span></code> module closes all file descriptors in the
|
||
child process by default, except standard streams (0, 1, 2) and file
|
||
descriptors of the <em>pass_fds</em> parameter. If the <em>close_fds</em> parameter is
|
||
set to <code class="docutils literal notranslate"><span class="pre">False</span></code>, all inheritable file descriptors are inherited in the
|
||
child process.</p>
|
||
<p>On Windows, the <code class="docutils literal notranslate"><span class="pre">subprocess</span></code> closes all handles and file descriptors
|
||
in the child process by default. If at least one standard stream (stdin,
|
||
stdout or stderr) is replaced (ex: redirected into a pipe), all
|
||
inheritable handles and file descriptors 0, 1 and 2 are inherited in the
|
||
child process.</p>
|
||
<p>Using the functions of the <code class="docutils literal notranslate"><span class="pre">os.execv*()</span></code> and <code class="docutils literal notranslate"><span class="pre">os.spawn*()</span></code> families,
|
||
all inheritable handles and all inheritable file descriptors are
|
||
inherited by the child process.</p>
|
||
<p>On UNIX, the <code class="docutils literal notranslate"><span class="pre">multiprocessing</span></code> module uses <code class="docutils literal notranslate"><span class="pre">os.fork()</span></code> and so all
|
||
file descriptors are inherited by child processes.</p>
|
||
<p>On Windows, all inheritable handles and file descriptors 0, 1 and 2 are
|
||
inherited by the child process using the <code class="docutils literal notranslate"><span class="pre">multiprocessing</span></code> module, all
|
||
file descriptors except standard streams are closed.</p>
|
||
<p>Summary:</p>
|
||
<table class="docutils align-default">
|
||
<thead>
|
||
<tr class="row-odd"><th class="head">Module</th>
|
||
<th class="head">FD on UNIX</th>
|
||
<th class="head">Handles on Windows</th>
|
||
<th class="head">FD on Windows</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="row-even"><td>subprocess, default</td>
|
||
<td>STD, pass_fds</td>
|
||
<td>none</td>
|
||
<td>STD</td>
|
||
</tr>
|
||
<tr class="row-odd"><td>subprocess, replace stdout</td>
|
||
<td>STD, pass_fds</td>
|
||
<td>all</td>
|
||
<td>STD</td>
|
||
</tr>
|
||
<tr class="row-even"><td>subprocess, close_fds=False</td>
|
||
<td>all</td>
|
||
<td>all</td>
|
||
<td>STD</td>
|
||
</tr>
|
||
<tr class="row-odd"><td>multiprocessing</td>
|
||
<td>not applicable</td>
|
||
<td>all</td>
|
||
<td>STD</td>
|
||
</tr>
|
||
<tr class="row-even"><td>os.execv(), os.spawn()</td>
|
||
<td>all</td>
|
||
<td>all</td>
|
||
<td>all</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Legend:</p>
|
||
<ul class="simple">
|
||
<li>“all”: all <em>inheritable</em> file descriptors or handles are inherited in
|
||
the child process</li>
|
||
<li>“none”: all handles are closed in the child process</li>
|
||
<li>“STD”: only file descriptors 0 (stdin), 1 (stdout) and 2 (stderr) are
|
||
inherited in the child process</li>
|
||
<li>“pass_fds”: file descriptors of the <em>pass_fds</em> parameter of the
|
||
subprocess are inherited</li>
|
||
<li>“not applicable”: on UNIX, the multiprocessing uses <code class="docutils literal notranslate"><span class="pre">fork()</span></code>,
|
||
so this case is not affected by this PEP.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="closing-all-open-file-descriptors">
|
||
<h3><a class="toc-backref" href="#closing-all-open-file-descriptors" role="doc-backlink">Closing All Open File Descriptors</a></h3>
|
||
<p>On UNIX, the <code class="docutils literal notranslate"><span class="pre">subprocess</span></code> module closes almost all file descriptors in
|
||
the child process. This operation requires MAXFD system calls, where
|
||
MAXFD is the maximum number of file descriptors, even if there are only
|
||
few open file descriptors. This maximum can be read using:
|
||
<code class="docutils literal notranslate"><span class="pre">os.sysconf("SC_OPEN_MAX")</span></code>.</p>
|
||
<p>The operation can be slow if MAXFD is large. For example, on a FreeBSD
|
||
buildbot with <code class="docutils literal notranslate"><span class="pre">MAXFD=655,000</span></code>, the operation took 300 ms: see
|
||
<a class="reference external" href="http://bugs.python.org/issue11284#msg132668">issue #11284: slow close file descriptors</a>.</p>
|
||
<p>On Linux, Python 3.3 gets the list of all open file descriptors from
|
||
<code class="docutils literal notranslate"><span class="pre">/proc/<PID>/fd/</span></code>, and so performances depends on the number of open
|
||
file descriptors, not on MAXFD.</p>
|
||
<p>See also:</p>
|
||
<ul class="simple">
|
||
<li><a class="reference external" href="http://bugs.python.org/issue1663329">Python issue #1663329</a>:
|
||
subprocess close_fds perform poor if <code class="docutils literal notranslate"><span class="pre">SC_OPEN_MAX</span></code> is high</li>
|
||
<li><a class="reference external" href="https://bugzilla.redhat.com/show_bug.cgi?id=837033">Squid Bug #837033</a>:
|
||
Squid should set CLOEXEC on opened FDs. “32k+ close() calls in each
|
||
child process take a long time ([12-56] seconds) in Xen PV guests.”</li>
|
||
</ul>
|
||
</section>
|
||
</section>
|
||
<section id="proposal">
|
||
<h2><a class="toc-backref" href="#proposal" role="doc-backlink">Proposal</a></h2>
|
||
<section id="non-inheritable-file-descriptors">
|
||
<h3><a class="toc-backref" href="#non-inheritable-file-descriptors" role="doc-backlink">Non-inheritable File Descriptors</a></h3>
|
||
<p>The following functions are modified to make newly created file
|
||
descriptors non-inheritable by default:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">asyncore.dispatcher.create_socket()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">io.FileIO</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">io.open()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">open()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">os.dup()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">os.fdopen()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">os.open()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">os.openpty()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">select.devpoll()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">select.epoll()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">select.kqueue()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">socket.socket()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">socket.socket.accept()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">socket.socket.dup()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">socket.socket.fromfd()</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">socket.socketpair()</span></code></li>
|
||
</ul>
|
||
<p><code class="docutils literal notranslate"><span class="pre">os.dup2()</span></code> still creates inheritable by default, see below.</p>
|
||
<p>When available, atomic flags are used to make file descriptors
|
||
non-inheritable. The atomicity is not guaranteed because a fallback is
|
||
required when atomic flags are not available.</p>
|
||
</section>
|
||
<section id="new-functions-and-methods">
|
||
<h3><a class="toc-backref" href="#new-functions-and-methods" role="doc-backlink">New Functions And Methods</a></h3>
|
||
<p>New functions available on all platforms:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">os.get_inheritable(fd:</span> <span class="pre">int)</span></code>: return <code class="docutils literal notranslate"><span class="pre">True</span></code> if the file
|
||
descriptor can be inherited by child processes, <code class="docutils literal notranslate"><span class="pre">False</span></code> otherwise.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">os.set_inheritable(fd:</span> <span class="pre">int,</span> <span class="pre">inheritable:</span> <span class="pre">bool)</span></code>: set the
|
||
inheritable flag of the specified file descriptor.</li>
|
||
</ul>
|
||
<p>New functions only available on Windows:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">os.get_handle_inheritable(handle:</span> <span class="pre">int)</span></code>: return <code class="docutils literal notranslate"><span class="pre">True</span></code> if the
|
||
handle can be inherited by child processes, <code class="docutils literal notranslate"><span class="pre">False</span></code> otherwise.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">os.set_handle_inheritable(handle:</span> <span class="pre">int,</span> <span class="pre">inheritable:</span> <span class="pre">bool)</span></code>:
|
||
set the inheritable flag of the specified handle.</li>
|
||
</ul>
|
||
<p>New methods:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">socket.socket.get_inheritable()</span></code>: return <code class="docutils literal notranslate"><span class="pre">True</span></code> if the
|
||
socket can be inherited by child processes, <code class="docutils literal notranslate"><span class="pre">False</span></code> otherwise.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">socket.socket.set_inheritable(inheritable:</span> <span class="pre">bool)</span></code>:
|
||
set the inheritable flag of the specified socket.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="other-changes">
|
||
<h3><a class="toc-backref" href="#other-changes" role="doc-backlink">Other Changes</a></h3>
|
||
<p>On UNIX, subprocess makes file descriptors of the <em>pass_fds</em> parameter
|
||
inheritable. The file descriptor is made inheritable in the child
|
||
process after the <code class="docutils literal notranslate"><span class="pre">fork()</span></code> and before <code class="docutils literal notranslate"><span class="pre">execv()</span></code>, so the inheritable
|
||
flag of file descriptors is unchanged in the parent process.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">os.dup2()</span></code> has a new optional <em>inheritable</em> parameter: <code class="docutils literal notranslate"><span class="pre">os.dup2(fd,</span>
|
||
<span class="pre">fd2,</span> <span class="pre">inheritable=True)</span></code>. <em>fd2</em> is created inheritable by default, but
|
||
non-inheritable if <em>inheritable</em> is <code class="docutils literal notranslate"><span class="pre">False</span></code>.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">os.dup2()</span></code> behaves differently than <code class="docutils literal notranslate"><span class="pre">os.dup()</span></code> because the most
|
||
common use case of <code class="docutils literal notranslate"><span class="pre">os.dup2()</span></code> is to replace the file descriptors of
|
||
the standard streams: <code class="docutils literal notranslate"><span class="pre">stdin</span></code> (<code class="docutils literal notranslate"><span class="pre">0</span></code>), <code class="docutils literal notranslate"><span class="pre">stdout</span></code> (<code class="docutils literal notranslate"><span class="pre">1</span></code>) and
|
||
<code class="docutils literal notranslate"><span class="pre">stderr</span></code> (<code class="docutils literal notranslate"><span class="pre">2</span></code>). Standard streams are expected to be inherited by
|
||
child processes.</p>
|
||
</section>
|
||
</section>
|
||
<section id="backward-compatibility">
|
||
<h2><a class="toc-backref" href="#backward-compatibility" role="doc-backlink">Backward Compatibility</a></h2>
|
||
<p>This PEP break applications relying on inheritance of file descriptors.
|
||
Developers are encouraged to reuse the high-level Python module
|
||
<code class="docutils literal notranslate"><span class="pre">subprocess</span></code> which handles the inheritance of file descriptors in a
|
||
portable way.</p>
|
||
<p>Applications using the <code class="docutils literal notranslate"><span class="pre">subprocess</span></code> module with the <em>pass_fds</em>
|
||
parameter or using only <code class="docutils literal notranslate"><span class="pre">os.dup2()</span></code> to redirect standard streams should
|
||
not be affected.</p>
|
||
<p>Python no longer conform to POSIX, since file descriptors are now
|
||
made non-inheritable by default. Python was not designed to conform to
|
||
POSIX, but was designed to develop portable applications.</p>
|
||
</section>
|
||
<section id="related-work">
|
||
<h2><a class="toc-backref" href="#related-work" role="doc-backlink">Related Work</a></h2>
|
||
<p>The programming languages Go, Perl and Ruby make newly created file
|
||
descriptors non-inheritable by default: since Go 1.0 (2009), Perl 1.0
|
||
(1987) and Ruby 2.0 (2013).</p>
|
||
<p>The SCons project, written in Python, overrides builtin functions
|
||
<code class="docutils literal notranslate"><span class="pre">file()</span></code> and <code class="docutils literal notranslate"><span class="pre">open()</span></code> to make files non-inheritable on Windows:
|
||
see <a class="reference external" href="https://bitbucket.org/scons/scons/src/c8dbbaa4598e7119ae80f72068386be105b5ad98/src/engine/SCons/Platform/win32.py?at=default#cl-68">win32.py</a>.</p>
|
||
</section>
|
||
<section id="rejected-alternatives">
|
||
<h2><a class="toc-backref" href="#rejected-alternatives" role="doc-backlink">Rejected Alternatives</a></h2>
|
||
<section id="add-a-new-open-noinherit-function">
|
||
<h3><a class="toc-backref" href="#add-a-new-open-noinherit-function" role="doc-backlink">Add a new open_noinherit() function</a></h3>
|
||
<p>In June 2007, Henning von Bargen proposed on the python-dev mailing list
|
||
to add a new open_noinherit() function to fix issues of inherited file
|
||
descriptors in child processes. At this time, the default value of the
|
||
<em>close_fds</em> parameter of the subprocess module was <code class="docutils literal notranslate"><span class="pre">False</span></code>.</p>
|
||
<p>Read the mail thread: <a class="reference external" href="https://mail.python.org/pipermail/python-dev/2007-June/073688.html">[Python-Dev] Proposal for a new function
|
||
“open_noinherit” to avoid problems with subprocesses and security risks</a>.</p>
|
||
</section>
|
||
<section id="pep-433">
|
||
<h3><a class="toc-backref" href="#pep-433" role="doc-backlink">PEP 433</a></h3>
|
||
<p><a class="pep reference internal" href="../pep-0433/" title="PEP 433 – Easier suppression of file descriptor inheritance">PEP 433</a>, “Easier suppression of file descriptor inheritance”,
|
||
was a previous attempt proposing various other alternatives, but no
|
||
consensus could be reached.</p>
|
||
</section>
|
||
</section>
|
||
<section id="python-issues">
|
||
<h2><a class="toc-backref" href="#python-issues" role="doc-backlink">Python Issues</a></h2>
|
||
<ul class="simple">
|
||
<li><a class="reference external" href="http://bugs.python.org/issue10115">#10115: Support accept4() for atomic setting of flags at socket
|
||
creation</a></li>
|
||
<li><a class="reference external" href="http://bugs.python.org/issue12105">#12105: open() does not able to set flags, such as O_CLOEXEC</a></li>
|
||
<li><a class="reference external" href="http://bugs.python.org/issue12107">#12107: TCP listening sockets created without FD_CLOEXEC flag</a></li>
|
||
<li><a class="reference external" href="http://bugs.python.org/issue16850">#16850: Add “e” mode to open(): close-and-exec
|
||
(O_CLOEXEC) / O_NOINHERIT</a></li>
|
||
<li><a class="reference external" href="http://bugs.python.org/issue16860">#16860: Use O_CLOEXEC in the tempfile module</a></li>
|
||
<li><a class="reference external" href="http://bugs.python.org/issue16946">#16946: subprocess: _close_open_fd_range_safe() does not set
|
||
close-on-exec flag on Linux < 2.6.23 if O_CLOEXEC is defined</a></li>
|
||
<li><a class="reference external" href="http://bugs.python.org/issue17070">#17070: Use the new cloexec to improve security and avoid bugs</a></li>
|
||
<li><a class="reference external" href="http://bugs.python.org/issue18571">#18571: Implementation of the PEP 446: non-inheritable file
|
||
descriptors</a></li>
|
||
</ul>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>This document has been placed into the public domain.</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0446.rst">https://github.com/python/peps/blob/main/peps/pep-0446.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0446.rst">2023-09-09 17:39:29 GMT</a></p>
|
||
|
||
</article>
|
||
<nav id="pep-sidebar">
|
||
<h2>Contents</h2>
|
||
<ul>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
|
||
<li><a class="reference internal" href="#inheritance-of-file-descriptors">Inheritance of File Descriptors</a></li>
|
||
<li><a class="reference internal" href="#inheritance-of-file-descriptors-on-windows">Inheritance of File Descriptors on Windows</a></li>
|
||
<li><a class="reference internal" href="#only-inherit-some-handles-on-windows">Only Inherit Some Handles on Windows</a></li>
|
||
<li><a class="reference internal" href="#inheritance-of-file-descriptors-on-unix">Inheritance of File Descriptors on UNIX</a></li>
|
||
<li><a class="reference internal" href="#issues-with-inheritable-file-descriptors">Issues with Inheritable File Descriptors</a></li>
|
||
<li><a class="reference internal" href="#security-vulnerability">Security Vulnerability</a></li>
|
||
<li><a class="reference internal" href="#issues-fixed-in-the-subprocess-module">Issues fixed in the subprocess module</a></li>
|
||
<li><a class="reference internal" href="#atomic-creation-of-non-inheritable-file-descriptors">Atomic Creation of non-inheritable File Descriptors</a></li>
|
||
<li><a class="reference internal" href="#status-of-python-3-3">Status of Python 3.3</a></li>
|
||
<li><a class="reference internal" href="#closing-all-open-file-descriptors">Closing All Open File Descriptors</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#proposal">Proposal</a><ul>
|
||
<li><a class="reference internal" href="#non-inheritable-file-descriptors">Non-inheritable File Descriptors</a></li>
|
||
<li><a class="reference internal" href="#new-functions-and-methods">New Functions And Methods</a></li>
|
||
<li><a class="reference internal" href="#other-changes">Other Changes</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backward-compatibility">Backward Compatibility</a></li>
|
||
<li><a class="reference internal" href="#related-work">Related Work</a></li>
|
||
<li><a class="reference internal" href="#rejected-alternatives">Rejected Alternatives</a><ul>
|
||
<li><a class="reference internal" href="#add-a-new-open-noinherit-function">Add a new open_noinherit() function</a></li>
|
||
<li><a class="reference internal" href="#pep-433">PEP 433</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#python-issues">Python Issues</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0446.rst">Page Source (GitHub)</a>
|
||
</nav>
|
||
</section>
|
||
<script src="../_static/colour_scheme.js"></script>
|
||
<script src="../_static/wrap_tables.js"></script>
|
||
<script src="../_static/sticky_banner.js"></script>
|
||
</body>
|
||
</html> |