Issue #18571: Implementation of the PEP 446: file descriptors and file handles
are now created non-inheritable; add functions os.get/set_inheritable(),
os.get/set_handle_inheritable() and socket.socket.get/set_inheritable().
diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index 399f1ff..67b77f7 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -979,6 +979,8 @@
    :mod:`os.open` as *opener* results in functionality similar to passing
    ``None``).
 
+   The newly created file is :ref:`non-inheritable <fd_inheritance>`.
+
    The following example uses the :ref:`dir_fd <dir_fd>` parameter of the
    :func:`os.open` function to open a file relative to a given directory::
 
@@ -992,10 +994,6 @@
       ...
       >>> os.close(dir_fd)  # don't leak a file descriptor
 
-   .. versionchanged:: 3.3
-      The *opener* parameter was added.
-      The ``'x'`` mode was added.
-
    The type of :term:`file object` returned by the :func:`open` function
    depends on the mode.  When :func:`open` is used to open a file in a text
    mode (``'w'``, ``'r'``, ``'wt'``, ``'rt'``, etc.), it returns a subclass of
@@ -1022,10 +1020,15 @@
    and :mod:`shutil`.
 
    .. versionchanged:: 3.3
+      The *opener* parameter was added.
+      The ``'x'`` mode was added.
       :exc:`IOError` used to be raised, it is now an alias of :exc:`OSError`.
       :exc:`FileExistsError` is now raised if the file opened in exclusive
       creation mode (``'x'``) already exists.
 
+   .. versionchanged:: 3.4
+      The file is now non-inheritable.
+
 
 .. XXX works for bytes too, but should it?
 .. function:: ord(c)
diff --git a/Doc/library/io.rst b/Doc/library/io.rst
index 8f3964f..a218881 100644
--- a/Doc/library/io.rst
+++ b/Doc/library/io.rst
@@ -522,6 +522,8 @@
    :mod:`os.open` as *opener* results in functionality similar to passing
    ``None``).
 
+   The newly created file is :ref:`non-inheritable <fd_inheritance>`.
+
    See the :func:`open` built-in function for examples on using the *opener*
    parameter.
 
@@ -529,6 +531,9 @@
       The *opener* parameter was added.
       The ``'x'`` mode was added.
 
+   .. versionchanged:: 3.4
+      The file is now non-inheritable.
+
    In addition to the attributes and methods from :class:`IOBase` and
    :class:`RawIOBase`, :class:`FileIO` provides the following data
    attributes:
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index 82d05ed..848fd16 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -685,17 +685,30 @@
 
 .. function:: dup(fd)
 
-   Return a duplicate of file descriptor *fd*.
+   Return a duplicate of file descriptor *fd*. The new file descriptor is
+   :ref:`non-inheritable <fd_inheritance>`.
+
+   On Windows, when duplicating a standard stream (0: stdin, 1: stdout,
+   2: stderr), the new file descriptor is :ref:`inheritable
+   <fd_inheritance>`.
 
    Availability: Unix, Windows.
 
+   .. versionchanged:: 3.4
+      The new file descriptor is now non-inheritable.
 
-.. function:: dup2(fd, fd2)
+
+.. function:: dup2(fd, fd2, inheritable=True)
 
    Duplicate file descriptor *fd* to *fd2*, closing the latter first if necessary.
+   The file descriptor *fd2* is :ref:`inheritable <fd_inheritance>` by default,
+   or non-inheritable if *inheritable* is ``False``.
 
    Availability: Unix, Windows.
 
+   .. versionchanged:: 3.4
+      Add the optional *inheritable* parameter.
+
 
 .. function:: fchmod(fd, mode)
 
@@ -848,6 +861,7 @@
    Open the file *file* and set various flags according to *flags* and possibly
    its mode according to *mode*.  When computing *mode*, the current umask value
    is first masked out.  Return the file descriptor for the newly opened file.
+   The new file descriptor is :ref:`non-inheritable <fd_inheritance>`.
 
    For a description of the flag and mode values, see the C run-time documentation;
    flag constants (like :const:`O_RDONLY` and :const:`O_WRONLY`) are defined in
@@ -859,6 +873,9 @@
 
    Availability: Unix, Windows.
 
+   .. versionchanged:: 3.4
+      The new file descriptor is now non-inheritable.
+
    .. note::
 
       This function is intended for low-level I/O.  For normal usage, use the
@@ -933,20 +950,28 @@
 
    .. index:: module: pty
 
-   Open a new pseudo-terminal pair. Return a pair of file descriptors ``(master,
-   slave)`` for the pty and the tty, respectively. For a (slightly) more portable
-   approach, use the :mod:`pty` module.
+   Open a new pseudo-terminal pair. Return a pair of file descriptors
+   ``(master, slave)`` for the pty and the tty, respectively. The new file
+   descriptors are :ref:`non-inheritable <fd_inheritance>`. For a (slightly) more
+   portable approach, use the :mod:`pty` module.
 
    Availability: some flavors of Unix.
 
+   .. versionchanged:: 3.4
+      The new file descriptors are now non-inheritable.
+
 
 .. function:: pipe()
 
-   Create a pipe.  Return a pair of file descriptors ``(r, w)`` usable for reading
-   and writing, respectively.
+   Create a pipe.  Return a pair of file descriptors ``(r, w)`` usable for
+   reading and writing, respectively. The new file descriptor are
+   :ref:`non-inheritable <fd_inheritance>`.
 
    Availability: Unix, Windows.
 
+   .. versionchanged:: 3.4
+      The new file descriptors are now non-inheritable.
+
 
 .. function:: pipe2(flags)
 
@@ -1178,6 +1203,50 @@
       Height of the terminal window in characters.
 
 
+.. _fd_inheritance:
+
+Inheritance of File Descriptors
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A file descriptor has a inheritable flag which indicates if the file descriptor
+can be inherited or not in child processes. Since Python 3.4, file descriptors
+created by Python are non-inheritable by default.
+
+On UNIX, non-inheritable file descriptors are closed in child processes at the
+execution of a new program, other file descriptors are inherited.
+
+On Windows, non-inheritable handles and file descriptors are closed in child
+processes, except standard streams (file descriptors 0, 1 and 2: stdin, stdout
+and stderr) which are always inherited. Using :func:`os.spawn*` functions,
+all inheritable handles and all inheritable file descriptors are inherited.
+Using the :mod:`subprocess` module, all file descriptors except standard
+streams are closed, inheritable handles are only inherited if the *close_fds*
+parameter is ``False``.
+
+.. versionadded:: 3.4
+
+.. function:: get_inheritable(fd)
+
+   Get the `inheritable flag <fd_inheritance>`_ of the specified file
+   descriptor. Return a :class:`bool`.
+
+.. function:: set_inheritable(fd, inheritable)
+
+   Set the `inheritable flag <fd_inheritance>`_ of the specified file descriptor.
+
+.. function:: get_handle_inheritable(handle)
+
+   Get the `inheritable flag <fd_inheritance>`_ of the specified handle. Return a :class:`bool`.
+
+   Availability: Windows.
+
+.. function:: set_handle_inheritable(handle, inheritable)
+
+   Set the `inheritable flag <fd_inheritance>`_ of the specified handle.
+
+   Availability: Windows.
+
+
 .. _os-file-dir:
 
 Files and Directories
diff --git a/Doc/library/select.rst b/Doc/library/select.rst
index 2b5225d..02ce775 100644
--- a/Doc/library/select.rst
+++ b/Doc/library/select.rst
@@ -37,8 +37,13 @@
    increases this value, :c:func:`devpoll` may return an
    incomplete list of active file descriptors.
 
+   The new file descriptor is :ref:`non-inheritable <fd_inheritance>`.
+
    .. versionadded:: 3.3
 
+   .. versionchanged:: 3.4
+      The new file descriptor is now non-inheritable.
+
 .. function:: epoll(sizehint=-1, flags=0)
 
    (Only supported on Linux 2.5.44 and newer.) Return an edge polling object,
@@ -49,11 +54,14 @@
    :ref:`epoll-objects` below for the methods supported by epolling objects.
    They also support the :keyword:`with` statement.
 
+   The new file descriptor is :ref:`non-inheritable <fd_inheritance>`.
+
    .. versionchanged:: 3.3
       Added the *flags* parameter.
 
    .. versionchanged:: 3.4
       Support for the :keyword:`with` statement was added.
+      The new file descriptor is now non-inheritable.
 
 
 .. function:: poll()
@@ -69,6 +77,11 @@
    (Only supported on BSD.)  Returns a kernel queue object; see section
    :ref:`kqueue-objects` below for the methods supported by kqueue objects.
 
+   The new file descriptor is :ref:`non-inheritable <fd_inheritance>`.
+
+   .. versionchanged:: 3.4
+      The new file descriptor is now non-inheritable.
+
 
 .. function:: kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0)
 
diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst
index edd196a..d666ace 100644
--- a/Doc/library/socket.rst
+++ b/Doc/library/socket.rst
@@ -460,7 +460,7 @@
    ``'udp'``, otherwise any protocol will match.
 
 
-.. function:: socket([family[, type[, proto]]])
+.. function:: socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
 
    Create a new socket using the given address family, socket type and protocol
    number.  The address family should be :const:`AF_INET` (the default),
@@ -471,12 +471,18 @@
    case where the address family is :const:`AF_CAN` the protocol should be one
    of :const:`CAN_RAW` or :const:`CAN_BCM`.
 
+   The newly created socket is :ref:`non-inheritable <fd_inheritance>`.
+
    .. versionchanged:: 3.3
       The AF_CAN family was added.
       The AF_RDS family was added.
 
-  .. versionchanged:: 3.4
-      The CAN_BCM protocol was added.
+   .. versionchanged:: 3.4
+       The CAN_BCM protocol was added.
+
+   .. versionchanged:: 3.4
+      The socket is now non-inheritable.
+
 
 .. function:: socketpair([family[, type[, proto]]])
 
@@ -486,12 +492,17 @@
    if defined on the platform; otherwise, the default is :const:`AF_INET`.
    Availability: Unix.
 
+   The newly created sockets are :ref:`non-inheritable <fd_inheritance>`.
+
    .. versionchanged:: 3.2
       The returned socket objects now support the whole socket API, rather
       than a subset.
 
+   .. versionchanged:: 3.4
+      The sockets are now non-inheritable.
 
-.. function:: fromfd(fd, family, type[, proto])
+
+.. function:: fromfd(fd, family, type, proto=0)
 
    Duplicate the file descriptor *fd* (an integer as returned by a file object's
    :meth:`fileno` method) and build a socket object from the result.  Address
@@ -502,6 +513,11 @@
    a socket passed to a program as standard input or output (such as a server
    started by the Unix inet daemon).  The socket is assumed to be in blocking mode.
 
+   The newly created socket is :ref:`non-inheritable <fd_inheritance>`.
+
+   .. versionchanged:: 3.4
+      The socket is now non-inheritable.
+
 
 .. function:: ntohl(x)
 
@@ -730,6 +746,11 @@
    *new* socket object usable to send and receive data on the connection, and
    *address* is the address bound to the socket on the other end of the connection.
 
+   The newly created socket is :ref:`non-inheritable <fd_inheritance>`.
+
+   .. versionchanged:: 3.4
+      The socket is now non-inheritable.
+
 
 .. method:: socket.bind(address)
 
@@ -775,6 +796,16 @@
    .. versionadded:: 3.2
 
 
+.. method:: socket.dup()
+
+   Duplicate the socket.
+
+   The newly created socket is :ref:`non-inheritable <fd_inheritance>`.
+
+   .. versionchanged:: 3.4
+      The socket is now non-inheritable.
+
+
 .. method:: socket.fileno()
 
    Return the socket's file descriptor (a small integer).  This is useful with
@@ -785,6 +816,15 @@
    this limitation.
 
 
+.. method:: socket.get_inheritable()
+
+   Get the :ref:`inheritable flag <fd_inheritance>` of the socket's file
+   descriptor or socket's handle: ``True`` if the socket can be inherited in
+   child processes, ``False`` if it cannot.
+
+   .. versionadded:: 3.4
+
+
 .. method:: socket.getpeername()
 
    Return the remote address to which the socket is connected.  This is useful to
@@ -1068,6 +1108,14 @@
    .. versionadded:: 3.3
 
 
+.. method:: socket.set_inheritable(inheritable)
+
+   Set the :ref:`inheritable flag <fd_inheritance>` of the socket's file
+   descriptor or socket's handle.
+
+   .. versionadded:: 3.4
+
+
 .. method:: socket.setblocking(flag)
 
    Set blocking or non-blocking mode of the socket: if *flag* is false, the
diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst
index d56b422..41b161c 100644
--- a/Doc/whatsnew/3.4.rst
+++ b/Doc/whatsnew/3.4.rst
@@ -96,6 +96,7 @@
 
 * :ref:`PEP 442: Safe object finalization <pep-442>`.
 * :ref:`PEP 445: Configurable memory allocators <pep-445>`.
+* :ref:`PEP 446: Make newly created file descriptors non-inheritable <pep-446>`.
 
 Implementation improvements:
 
@@ -118,6 +119,19 @@
 
 Please read on for a comprehensive list of user-facing changes.
 
+.. _pep-446:
+
+PEP 446: Make newly created file descriptors non-inheritable
+============================================================
+
+The :pep:`446` makes newly created file descriptors `non-inheritable
+<fd_inheritance>`_. New functions and methods:
+
+* :func:`os.get_inheritable`, :func:`os.set_inheritable`
+* :func:`os.get_handle_inheritable`, :func:`os.set_handle_inheritable`
+* :meth:`socket.socket.get_inheritable`, :meth:`socket.socket.set_inheritable`
+
+
 .. _pep-445:
 
 PEP 445: Add new APIs to customize Python memory allocators
@@ -267,6 +281,16 @@
 will no longer inherit unneeded handles/file descriptors from their parents.
 
 
+os
+--
+
+New functions to get and set the `inheritable flag <fd_inheritance>`_ of a file
+descriptors or a Windows handle:
+
+* :func:`os.get_inheritable`, :func:`os.set_inheritable`
+* :func:`os.get_handle_inheritable`, :func:`os.set_handle_inheritable`
+
+
 poplib
 ------
 
@@ -288,6 +312,15 @@
 (:issue:`2118`).
 
 
+socket
+------
+
+Socket objects have new methods to get or set their `inheritable flag
+<fd_inheritance>`_:
+
+* :meth:`socket.socket.get_inheritable`, :meth:`socket.socket.set_inheritable`
+
+
 ssl
 ---
 
diff --git a/Include/fileutils.h b/Include/fileutils.h
index 7c18cf2..5466e3c 100644
--- a/Include/fileutils.h
+++ b/Include/fileutils.h
@@ -27,11 +27,19 @@
     struct stat *statbuf);
 #endif
 
+PyAPI_FUNC(int) _Py_open(
+    const char *pathname,
+    int flags);
+
 PyAPI_FUNC(FILE *) _Py_wfopen(
     const wchar_t *path,
     const wchar_t *mode);
 
 PyAPI_FUNC(FILE*) _Py_fopen(
+    const char *pathname,
+    const char *mode);
+
+PyAPI_FUNC(FILE*) _Py_fopen_obj(
     PyObject *path,
     const char *mode);
 
@@ -53,6 +61,13 @@
     wchar_t *buf,
     size_t size);
 
+PyAPI_FUNC(int) _Py_get_inheritable(int fd);
+
+PyAPI_FUNC(int) _Py_set_inheritable(int fd, int inheritable,
+                                    int *atomic_flag_works);
+
+PyAPI_FUNC(int) _Py_dup(int fd);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index 137ebcb..7ce86a4 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -129,6 +129,8 @@
     be kept open when the file is closed. This does not work when a file name is
     given and must be True in that case.
 
+    The newly created file is non-inheritable.
+
     A custom opener can be used by passing a callable as *opener*. The
     underlying file descriptor for the file object is then obtained by calling
     *opener* with (*file*, *flags*). *opener* must return an open file
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
index 443fa7e..9fbe46d 100644
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -509,7 +509,7 @@
             c1 = Connection(s1.detach())
             c2 = Connection(s2.detach())
         else:
-            fd1, fd2 = util.pipe()
+            fd1, fd2 = os.pipe()
             c1 = Connection(fd1, writable=False)
             c2 = Connection(fd2, readable=False)
 
@@ -536,7 +536,9 @@
             _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE,
             _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE |
             _winapi.PIPE_WAIT,
-            1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL
+            1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER,
+            # default security descriptor: the handle cannot be inherited
+            _winapi.NULL
             )
         h2 = _winapi.CreateFile(
             address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py
index 208bd4e..11df382 100644
--- a/Lib/multiprocessing/forkserver.py
+++ b/Lib/multiprocessing/forkserver.py
@@ -60,8 +60,8 @@
         raise ValueError('too many fds')
     with socket.socket(socket.AF_UNIX) as client:
         client.connect(_forkserver_address)
-        parent_r, child_w = util.pipe()
-        child_r, parent_w = util.pipe()
+        parent_r, child_w = os.pipe()
+        child_r, parent_w = os.pipe()
         allfds = [child_r, child_w, _forkserver_alive_fd,
                   semaphore_tracker._semaphore_tracker_fd]
         allfds += fds
diff --git a/Lib/multiprocessing/popen_fork.py b/Lib/multiprocessing/popen_fork.py
index fd25ddc..c9f3aae 100644
--- a/Lib/multiprocessing/popen_fork.py
+++ b/Lib/multiprocessing/popen_fork.py
@@ -66,7 +66,7 @@
 
     def _launch(self, process_obj):
         code = 1
-        parent_r, child_w = util.pipe()
+        parent_r, child_w = os.pipe()
         self.pid = os.fork()
         if self.pid == 0:
             try:
diff --git a/Lib/multiprocessing/popen_spawn_posix.py b/Lib/multiprocessing/popen_spawn_posix.py
index e67915d..751bf22 100644
--- a/Lib/multiprocessing/popen_spawn_posix.py
+++ b/Lib/multiprocessing/popen_spawn_posix.py
@@ -54,8 +54,8 @@
 
         parent_r = child_w = child_r = parent_w = None
         try:
-            parent_r, child_w = util.pipe()
-            child_r, parent_w = util.pipe()
+            parent_r, child_w = os.pipe()
+            child_r, parent_w = os.pipe()
             cmd = spawn.get_command_line(tracker_fd=tracker_fd,
                                          pipe_handle=child_r)
             self._fds.extend([child_r, child_w])
diff --git a/Lib/multiprocessing/semaphore_tracker.py b/Lib/multiprocessing/semaphore_tracker.py
index 99a0dd4..6c9e4a5 100644
--- a/Lib/multiprocessing/semaphore_tracker.py
+++ b/Lib/multiprocessing/semaphore_tracker.py
@@ -45,7 +45,7 @@
         except Exception:
             pass
         cmd = 'from multiprocessing.semaphore_tracker import main; main(%d)'
-        r, w = util.pipe()
+        r, w = os.pipe()
         try:
             fds_to_pass.append(r)
             # process will out live us, so no need to wait on pid
diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py
index d9e4799..ac8e913 100644
--- a/Lib/multiprocessing/util.py
+++ b/Lib/multiprocessing/util.py
@@ -365,7 +365,7 @@
         if flag & fcntl.FD_CLOEXEC:
             fcntl.fcntl(fd, fcntl.F_SETFD, flag & ~fcntl.FD_CLOEXEC)
             tmp.append((fd, flag))
-    errpipe_read, errpipe_write = _posixsubprocess.cloexec_pipe()
+    errpipe_read, errpipe_write = os.pipe()
     try:
         return _posixsubprocess.fork_exec(
             args, [os.fsencode(path)], True, passfds, None, None,
@@ -381,7 +381,9 @@
 #
 # Return pipe with CLOEXEC set on fds
 #
+# Deprecated: os.pipe() creates non-inheritable file descriptors
+# since Python 3.4
+#
 
 def pipe():
-    import _posixsubprocess
-    return _posixsubprocess.cloexec_pipe()
+    return os.pipe()
diff --git a/Lib/socket.py b/Lib/socket.py
index 96f8ed0..81864f5 100644
--- a/Lib/socket.py
+++ b/Lib/socket.py
@@ -137,7 +137,8 @@
     def dup(self):
         """dup() -> socket object
 
-        Return a new socket object connected to the same system resource.
+        Duplicate the socket. Return a new socket object connected to the same
+        system resource. The new socket is non-inheritable.
         """
         fd = dup(self.fileno())
         sock = self.__class__(self.family, self.type, self.proto, fileno=fd)
@@ -229,6 +230,20 @@
         self._closed = True
         return super().detach()
 
+    if os.name == 'nt':
+        def get_inheritable(self):
+            return os.get_handle_inheritable(self.fileno())
+        def set_inheritable(self, inheritable):
+            os.set_handle_inheritable(self.fileno(), inheritable)
+    else:
+        def get_inheritable(self):
+            return os.get_inheritable(self.fileno())
+        def set_inheritable(self, inheritable):
+            os.set_inheritable(self.fileno(), inheritable)
+    get_inheritable.__doc__ = "Get the inheritable flag of the socket"
+    set_inheritable.__doc__ = "Set the inheritable flag of the socket"
+
+
 def fromfd(fd, family, type, proto=0):
     """ fromfd(fd, family, type[, proto]) -> socket object
 
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 6ebcc25..4ce3c92 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -405,7 +405,6 @@
     import select
     _has_poll = hasattr(select, 'poll')
     import _posixsubprocess
-    _create_pipe = _posixsubprocess.cloexec_pipe
 
     # When select or poll has indicated that the file is writable,
     # we can write up to _PIPE_BUF bytes without risk of blocking.
@@ -1258,7 +1257,7 @@
             if stdin is None:
                 pass
             elif stdin == PIPE:
-                p2cread, p2cwrite = _create_pipe()
+                p2cread, p2cwrite = os.pipe()
             elif stdin == DEVNULL:
                 p2cread = self._get_devnull()
             elif isinstance(stdin, int):
@@ -1270,7 +1269,7 @@
             if stdout is None:
                 pass
             elif stdout == PIPE:
-                c2pread, c2pwrite = _create_pipe()
+                c2pread, c2pwrite = os.pipe()
             elif stdout == DEVNULL:
                 c2pwrite = self._get_devnull()
             elif isinstance(stdout, int):
@@ -1282,7 +1281,7 @@
             if stderr is None:
                 pass
             elif stderr == PIPE:
-                errread, errwrite = _create_pipe()
+                errread, errwrite = os.pipe()
             elif stderr == STDOUT:
                 errwrite = c2pwrite
             elif stderr == DEVNULL:
@@ -1334,7 +1333,7 @@
             # For transferring possible exec failure from child to parent.
             # Data format: "exception name:hex errno:description"
             # Pickle is not used; it is complex and involves memory allocation.
-            errpipe_read, errpipe_write = _create_pipe()
+            errpipe_read, errpipe_write = os.pipe()
             try:
                 try:
                     # We must avoid complex work that could involve
diff --git a/Lib/tempfile.py b/Lib/tempfile.py
index df73289..1eff59c 100644
--- a/Lib/tempfile.py
+++ b/Lib/tempfile.py
@@ -35,33 +35,12 @@
 from random import Random as _Random
 
 try:
-    import fcntl as _fcntl
-except ImportError:
-    def _set_cloexec(fd):
-        pass
-else:
-    def _set_cloexec(fd):
-        try:
-            flags = _fcntl.fcntl(fd, _fcntl.F_GETFD, 0)
-        except OSError:
-            pass
-        else:
-            # flags read successfully, modify
-            flags |= _fcntl.FD_CLOEXEC
-            _fcntl.fcntl(fd, _fcntl.F_SETFD, flags)
-
-
-try:
     import _thread
 except ImportError:
     import _dummy_thread as _thread
 _allocate_lock = _thread.allocate_lock
 
 _text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL
-if hasattr(_os, 'O_CLOEXEC'):
-    _text_openflags |= _os.O_CLOEXEC
-if hasattr(_os, 'O_NOINHERIT'):
-    _text_openflags |= _os.O_NOINHERIT
 if hasattr(_os, 'O_NOFOLLOW'):
     _text_openflags |= _os.O_NOFOLLOW
 
@@ -90,8 +69,8 @@
     # Fallback.  All we need is something that raises OSError if the
     # file doesn't exist.
     def _stat(fn):
-        f = open(fn)
-        f.close()
+        fd = _os.open(fn, _os.O_RDONLY)
+        os.close(fd)
 
 def _exists(fn):
     try:
@@ -217,7 +196,6 @@
         file = _os.path.join(dir, pre + name + suf)
         try:
             fd = _os.open(file, flags, 0o600)
-            _set_cloexec(fd)
             return (fd, _os.path.abspath(file))
         except FileExistsError:
             continue    # try again
diff --git a/Lib/test/subprocessdata/inherited.py b/Lib/test/subprocessdata/inherited.py
new file mode 100644
index 0000000..1aac041
--- /dev/null
+++ b/Lib/test/subprocessdata/inherited.py
@@ -0,0 +1,22 @@
+"""Similar to fd_status.py, but only checks file descriptors passed on the
+command line."""
+
+import errno
+import os
+import sys
+import stat
+
+if __name__ == "__main__":
+    fds = map(int, sys.argv[1:])
+    inherited = []
+    for fd in fds:
+        try:
+            st = os.fstat(fd)
+        except OSError as e:
+            if e.errno == errno.EBADF:
+                continue
+            raise
+        # Ignore Solaris door files
+        if not stat.S_ISDOOR(st.st_mode):
+            inherited.append(fd)
+    print(','.join(map(str, inherited)))
diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py
index 87655f4..084d247 100644
--- a/Lib/test/test_asyncore.py
+++ b/Lib/test/test_asyncore.py
@@ -744,7 +744,12 @@
         s.create_socket(self.family)
         self.assertEqual(s.socket.family, self.family)
         SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', 0)
-        self.assertEqual(s.socket.type, socket.SOCK_STREAM | SOCK_NONBLOCK)
+        sock_type = socket.SOCK_STREAM | SOCK_NONBLOCK
+        if hasattr(socket, 'SOCK_CLOEXEC'):
+            self.assertIn(s.socket.type,
+                          (sock_type | socket.SOCK_CLOEXEC, sock_type))
+        else:
+            self.assertEqual(s.socket.type, sock_type)
 
     def test_bind(self):
         if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX:
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index 8d3271f..2411c9b 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -1015,6 +1015,11 @@
             os.environ.clear()
             os.environ.update(old_environ)
 
+    def test_open_non_inheritable(self):
+        fileobj = open(__file__)
+        with fileobj:
+            self.assertFalse(os.get_inheritable(fileobj.fileno()))
+
     def test_ord(self):
         self.assertEqual(ord(' '), 32)
         self.assertEqual(ord('A'), 65)
diff --git a/Lib/test/test_devpoll.py b/Lib/test/test_devpoll.py
index 167e0ee..40ebeee 100644
--- a/Lib/test/test_devpoll.py
+++ b/Lib/test/test_devpoll.py
@@ -2,7 +2,11 @@
 
 # Initial tests are copied as is from "test_poll.py"
 
-import os, select, random, unittest, sys
+import os
+import random
+import select
+import sys
+import unittest
 from test.support import TESTFN, run_unittest
 
 try:
@@ -111,6 +115,11 @@
         self.assertRaises(ValueError, devpoll.register, fd, fd, select.POLLIN)
         self.assertRaises(ValueError, devpoll.unregister, fd)
 
+    def test_fd_non_inheritable(self):
+        devpoll = select.devpoll()
+        self.addCleanup(devpoll.close)
+        self.assertEqual(os.get_inheritable(devpoll.fileno()), False)
+
 
 def test_main():
     run_unittest(DevPollTests)
diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py
index 93a9e1d..6459fba 100644
--- a/Lib/test/test_epoll.py
+++ b/Lib/test/test_epoll.py
@@ -21,10 +21,11 @@
 """
 Tests for epoll wrapper.
 """
-import socket
 import errno
-import time
+import os
 import select
+import socket
+import time
 import unittest
 
 from test import support
@@ -249,6 +250,11 @@
         self.assertRaises(ValueError, epoll.register, fd, select.EPOLLIN)
         self.assertRaises(ValueError, epoll.unregister, fd)
 
+    def test_fd_non_inheritable(self):
+        epoll = select.epoll()
+        self.addCleanup(epoll.close)
+        self.assertEqual(os.get_inheritable(epoll.fileno()), False)
+
 
 def test_main():
     support.run_unittest(TestEPoll)
diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py
index 930f088..bafdeba 100644
--- a/Lib/test/test_kqueue.py
+++ b/Lib/test/test_kqueue.py
@@ -206,6 +206,11 @@
         # operations must fail with ValueError("I/O operation on closed ...")
         self.assertRaises(ValueError, kqueue.control, None, 4)
 
+    def test_fd_non_inheritable(self):
+        kqueue = select.kqueue()
+        self.addCleanup(kqueue.close)
+        self.assertEqual(os.get_inheritable(kqueue.fileno()), False)
+
 
 def test_main():
     support.run_unittest(TestKQueue)
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 1d41b77..d0dd364 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -2298,6 +2298,72 @@
         else:
             self.skipTest("Could not determine the number of CPUs")
 
+
+class FDInheritanceTests(unittest.TestCase):
+    def test_get_inheritable(self):
+        fd = os.open(__file__, os.O_RDONLY)
+        self.addCleanup(os.close, fd)
+        for inheritable in (False, True):
+            os.set_inheritable(fd, inheritable)
+            self.assertEqual(os.get_inheritable(fd), inheritable)
+
+    def test_set_inheritable(self):
+        fd = os.open(__file__, os.O_RDONLY)
+        self.addCleanup(os.close, fd)
+        os.set_inheritable(fd, True)
+        self.assertEqual(os.get_inheritable(fd), True)
+
+    def test_open(self):
+        fd = os.open(__file__, os.O_RDONLY)
+        self.addCleanup(os.close, fd)
+        self.assertEqual(os.get_inheritable(fd), False)
+
+    @unittest.skipUnless(hasattr(os, 'pipe'), "need os.pipe()")
+    def test_pipe(self):
+        rfd, wfd = os.pipe()
+        self.addCleanup(os.close, rfd)
+        self.addCleanup(os.close, wfd)
+        self.assertEqual(os.get_inheritable(rfd), False)
+        self.assertEqual(os.get_inheritable(wfd), False)
+
+    def test_dup(self):
+        fd1 = os.open(__file__, os.O_RDONLY)
+        self.addCleanup(os.close, fd1)
+
+        fd2 = os.dup(fd1)
+        self.addCleanup(os.close, fd2)
+        self.assertEqual(os.get_inheritable(fd2), False)
+
+    @unittest.skipUnless(hasattr(os, 'dup2'), "need os.dup2()")
+    def test_dup2(self):
+        fd = os.open(__file__, os.O_RDONLY)
+        self.addCleanup(os.close, fd)
+
+        # inheritable by default
+        fd2 = os.open(__file__, os.O_RDONLY)
+        try:
+            os.dup2(fd, fd2)
+            self.assertEqual(os.get_inheritable(fd2), True)
+        finally:
+            os.close(fd2)
+
+        # force non-inheritable
+        fd3 = os.open(__file__, os.O_RDONLY)
+        try:
+            os.dup2(fd, fd3, inheritable=False)
+            self.assertEqual(os.get_inheritable(fd3), False)
+        finally:
+            os.close(fd3)
+
+    @unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()")
+    def test_openpty(self):
+        master_fd, slave_fd = os.openpty()
+        self.addCleanup(os.close, master_fd)
+        self.addCleanup(os.close, slave_fd)
+        self.assertEqual(os.get_inheritable(master_fd), False)
+        self.assertEqual(os.get_inheritable(slave_fd), False)
+
+
 @support.reap_threads
 def test_main():
     support.run_unittest(
@@ -2330,6 +2396,7 @@
         OSErrorTests,
         RemoveDirsTests,
         CPUCountTests,
+        FDInheritanceTests,
     )
 
 if __name__ == "__main__":
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index c7a52d8..c7b9ba8 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -23,10 +23,6 @@
 import pickle
 import struct
 try:
-    import fcntl
-except ImportError:
-    fcntl = False
-try:
     import multiprocessing
 except ImportError:
     multiprocessing = False
@@ -1108,9 +1104,15 @@
 
     def testNewAttributes(self):
         # testing .family, .type and .protocol
+
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         self.assertEqual(sock.family, socket.AF_INET)
-        self.assertEqual(sock.type, socket.SOCK_STREAM)
+        if hasattr(socket, 'SOCK_CLOEXEC'):
+            self.assertIn(sock.type,
+                          (socket.SOCK_STREAM | socket.SOCK_CLOEXEC,
+                           socket.SOCK_STREAM))
+        else:
+            self.assertEqual(sock.type, socket.SOCK_STREAM)
         self.assertEqual(sock.proto, 0)
         sock.close()
 
@@ -4749,16 +4751,46 @@
         self.assertRaises(OSError, sock.sendall, b'foo')
 
 
-@unittest.skipUnless(hasattr(socket, "SOCK_CLOEXEC"),
-                     "SOCK_CLOEXEC not defined")
-@unittest.skipUnless(fcntl, "module fcntl not available")
-class CloexecConstantTest(unittest.TestCase):
+class InheritanceTest(unittest.TestCase):
+    @unittest.skipUnless(hasattr(socket, "SOCK_CLOEXEC"),
+                         "SOCK_CLOEXEC not defined")
     @support.requires_linux_version(2, 6, 28)
     def test_SOCK_CLOEXEC(self):
         with socket.socket(socket.AF_INET,
                            socket.SOCK_STREAM | socket.SOCK_CLOEXEC) as s:
             self.assertTrue(s.type & socket.SOCK_CLOEXEC)
-            self.assertTrue(fcntl.fcntl(s, fcntl.F_GETFD) & fcntl.FD_CLOEXEC)
+            self.assertTrue(sock.get_inheritable())
+
+    def test_default_inheritable(self):
+        sock = socket.socket()
+        with sock:
+            self.assertEqual(sock.get_inheritable(), False)
+
+    def test_dup(self):
+        sock = socket.socket()
+        with sock:
+            newsock = sock.dup()
+            sock.close()
+            with newsock:
+                self.assertEqual(newsock.get_inheritable(), False)
+
+    def test_set_inheritable(self):
+        sock = socket.socket()
+        with sock:
+            sock.set_inheritable(True)
+            self.assertEqual(sock.get_inheritable(), True)
+
+            sock.set_inheritable(False)
+            self.assertEqual(sock.get_inheritable(), False)
+
+    @unittest.skipUnless(hasattr(socket, "socketpair"),
+                         "need socket.socketpair()")
+    def test_socketpair(self):
+        s1, s2 = socket.socketpair()
+        self.addCleanup(s1.close)
+        self.addCleanup(s2.close)
+        self.assertEqual(s1.get_inheritable(), False)
+        self.assertEqual(s2.get_inheritable(), False)
 
 
 @unittest.skipUnless(hasattr(socket, "SOCK_NONBLOCK"),
@@ -4927,7 +4959,7 @@
         NetworkConnectionAttributesTest,
         NetworkConnectionBehaviourTest,
         ContextManagersTest,
-        CloexecConstantTest,
+        InheritanceTest,
         NonblockConstantTest
     ])
     if hasattr(socket, "socketpair"):
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 8a3ac4b..8cd2d1e 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -1501,16 +1501,28 @@
         # Terminating a dead process
         self._kill_dead_process('terminate')
 
+    def _save_fds(self, save_fds):
+        fds = []
+        for fd in save_fds:
+            inheritable = os.get_inheritable(fd)
+            saved = os.dup(fd)
+            fds.append((fd, saved, inheritable))
+        return fds
+
+    def _restore_fds(self, fds):
+        for fd, saved, inheritable in fds:
+            os.dup2(saved, fd, inheritable=inheritable)
+            os.close(saved)
+
     def check_close_std_fds(self, fds):
         # Issue #9905: test that subprocess pipes still work properly with
         # some standard fds closed
         stdin = 0
-        newfds = []
-        for a in fds:
-            b = os.dup(a)
-            newfds.append(b)
-            if a == 0:
-                stdin = b
+        saved_fds = self._save_fds(fds)
+        for fd, saved, inheritable in saved_fds:
+            if fd == 0:
+                stdin = saved
+                break
         try:
             for fd in fds:
                 os.close(fd)
@@ -1525,10 +1537,7 @@
             err = support.strip_python_stderr(err)
             self.assertEqual((out, err), (b'apple', b'orange'))
         finally:
-            for b, a in zip(newfds, fds):
-                os.dup2(b, a)
-            for b in newfds:
-                os.close(b)
+            self._restore_fds(saved_fds)
 
     def test_close_fd_0(self):
         self.check_close_std_fds([0])
@@ -1568,7 +1577,7 @@
             os.lseek(temp_fds[1], 0, 0)
 
             # move the standard file descriptors out of the way
-            saved_fds = [os.dup(fd) for fd in range(3)]
+            saved_fds = self._save_fds(range(3))
             try:
                 # duplicate the file objects over the standard fd's
                 for fd, temp_fd in enumerate(temp_fds):
@@ -1584,10 +1593,7 @@
                     stderr=temp_fds[0])
                 p.wait()
             finally:
-                # restore the original fd's underneath sys.stdin, etc.
-                for std, saved in enumerate(saved_fds):
-                    os.dup2(saved, std)
-                    os.close(saved)
+                self._restore_fds(saved_fds)
 
             for fd in temp_fds:
                 os.lseek(fd, 0, 0)
@@ -1611,7 +1617,7 @@
                 os.unlink(fname)
 
             # save a copy of the standard file descriptors
-            saved_fds = [os.dup(fd) for fd in range(3)]
+            saved_fds = self._save_fds(range(3))
             try:
                 # duplicate the temp files over the standard fd's 0, 1, 2
                 for fd, temp_fd in enumerate(temp_fds):
@@ -1637,9 +1643,7 @@
                 out = os.read(stdout_no, 1024)
                 err = support.strip_python_stderr(os.read(stderr_no, 1024))
             finally:
-                for std, saved in enumerate(saved_fds):
-                    os.dup2(saved, std)
-                    os.close(saved)
+                self._restore_fds(saved_fds)
 
             self.assertEqual(out, b"got STDIN")
             self.assertEqual(err, b"err")
@@ -1810,6 +1814,9 @@
             self.addCleanup(os.close, fd)
             open_fds.add(fd)
 
+        for fd in open_fds:
+            os.set_inheritable(fd, True)
+
         p = subprocess.Popen([sys.executable, fd_status],
                              stdout=subprocess.PIPE, close_fds=False)
         output, ignored = p.communicate()
@@ -1854,6 +1861,8 @@
             fds = os.pipe()
             self.addCleanup(os.close, fds[0])
             self.addCleanup(os.close, fds[1])
+            os.set_inheritable(fds[0], True)
+            os.set_inheritable(fds[1], True)
             open_fds.update(fds)
 
         for fd in open_fds:
@@ -1876,6 +1885,32 @@
                         close_fds=False, pass_fds=(fd, )))
             self.assertIn('overriding close_fds', str(context.warning))
 
+    def test_pass_fds_inheritable(self):
+        script = support.findfile("inherited.py", subdir="subprocessdata")
+
+        inheritable, non_inheritable = os.pipe()
+        self.addCleanup(os.close, inheritable)
+        self.addCleanup(os.close, non_inheritable)
+        os.set_inheritable(inheritable, True)
+        os.set_inheritable(non_inheritable, False)
+        pass_fds = (inheritable, non_inheritable)
+        args = [sys.executable, script]
+        args += list(map(str, pass_fds))
+
+        p = subprocess.Popen(args,
+                             stdout=subprocess.PIPE, close_fds=True,
+                             pass_fds=pass_fds)
+        output, ignored = p.communicate()
+        fds = set(map(int, output.split(b',')))
+
+        # the inheritable file descriptor must be inherited, so its inheritable
+        # flag must be set in the child process after fork() and before exec()
+        self.assertEqual(fds, set(pass_fds))
+
+        # inheritable flag must not be changed in the parent process
+        self.assertEqual(os.get_inheritable(inheritable), True)
+        self.assertEqual(os.get_inheritable(non_inheritable), False)
+
     def test_stdout_stdin_are_single_inout_fd(self):
         with io.open(os.devnull, "r+") as inout:
             p = subprocess.Popen([sys.executable, "-c", "import sys; sys.exit(0)"],
diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py
index 6b146d2..493c640 100644
--- a/Lib/test/test_tempfile.py
+++ b/Lib/test/test_tempfile.py
@@ -333,6 +333,7 @@
             v="q"
 
         file = self.do_create()
+        self.assertEqual(os.get_inheritable(file.fd), False)
         fd = "%d" % file.fd
 
         try:
diff --git a/Lib/xmlrpc/server.py b/Lib/xmlrpc/server.py
index 78ca4e0..d914282 100644
--- a/Lib/xmlrpc/server.py
+++ b/Lib/xmlrpc/server.py
@@ -584,13 +584,6 @@
         SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types)
         socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
 
-        # [Bug #1222790] If possible, set close-on-exec flag; if a
-        # method spawns a subprocess, the subprocess shouldn't have
-        # the listening socket open.
-        if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
-            flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
-            flags |= fcntl.FD_CLOEXEC
-            fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
 
 class MultiPathXMLRPCServer(SimpleXMLRPCServer):
     """Multipath XML-RPC Server
diff --git a/Misc/NEWS b/Misc/NEWS
index 9b2d379..8c59c09 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,11 @@
 Core and Builtins
 -----------------
 
+- Issue #18571: Implementation of the PEP 446: file descriptors and file
+  handles are now created non-inheritable; add functions
+  os.get/set_inheritable(), os.get/set_handle_inheritable() and
+  socket.socket.get/set_inheritable().
+
 - Issue #11619: The parser and the import machinery do not encode Unicode
   filenames anymore on Windows.
 
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 27e5579..b5af867 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -1694,26 +1694,24 @@
     /* We have to simulate this by writing to a temporary FILE*,
        then reading back, then writing to the argument stream. */
     char fn[100];
-    int fd;
-    FILE *fp;
-    PyObject *res;
+    int fd = -1;
+    FILE *fp = NULL;
+    PyObject *res = NULL;
 
     strcpy(fn, "/tmp/py.curses.putwin.XXXXXX");
     fd = mkstemp(fn);
     if (fd < 0)
         return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
+    if (_Py_set_inheritable(fd, 0, NULL) < 0)
+        goto exit;
     fp = fdopen(fd, "wb+");
     if (fp == NULL) {
-        close(fd);
-        remove(fn);
-        return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
+        PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
+        goto exit;
     }
     res = PyCursesCheckERR(putwin(self->win, fp), "putwin");
-    if (res == NULL) {
-        fclose(fp);
-        remove(fn);
-        return res;
-    }
+    if (res == NULL)
+        goto exit;
     fseek(fp, 0, 0);
     while (1) {
         char buf[BUFSIZ];
@@ -1727,7 +1725,12 @@
         if (res == NULL)
             break;
     }
-    fclose(fp);
+
+exit:
+    if (fp != NULL)
+        fclose(fp);
+    else if (fd != -1)
+        close(fd);
     remove(fn);
     return res;
 }
@@ -2252,12 +2255,13 @@
 PyCurses_GetWin(PyCursesWindowObject *self, PyObject *stream)
 {
     char fn[100];
-    int fd;
-    FILE *fp;
+    int fd = -1;
+    FILE *fp = NULL;
     PyObject *data;
     size_t datalen;
     WINDOW *win;
     _Py_IDENTIFIER(read);
+    PyObject *res = NULL;
 
     PyCursesInitialised;
 
@@ -2265,44 +2269,47 @@
     fd = mkstemp(fn);
     if (fd < 0)
         return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
+    if (_Py_set_inheritable(fd, 0, NULL) < 0)
+        goto error;
     fp = fdopen(fd, "wb+");
     if (fp == NULL) {
-        close(fd);
-        remove(fn);
-        return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
+        PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
+        goto error;
     }
+
     data = _PyObject_CallMethodId(stream, &PyId_read, "");
-    if (data == NULL) {
-        fclose(fp);
-        remove(fn);
-        return NULL;
-    }
+    if (data == NULL)
+        goto error;
     if (!PyBytes_Check(data)) {
         PyErr_Format(PyExc_TypeError,
                      "f.read() returned %.100s instead of bytes",
                      data->ob_type->tp_name);
         Py_DECREF(data);
-        fclose(fp);
-        remove(fn);
-        return NULL;
+        goto error;
     }
     datalen = PyBytes_GET_SIZE(data);
     if (fwrite(PyBytes_AS_STRING(data), 1, datalen, fp) != datalen) {
         Py_DECREF(data);
-        fclose(fp);
-        remove(fn);
-        return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
+        PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn);
+        goto error;
     }
     Py_DECREF(data);
+
     fseek(fp, 0, 0);
     win = getwin(fp);
-    fclose(fp);
-    remove(fn);
     if (win == NULL) {
         PyErr_SetString(PyCursesError, catchall_NULL);
-        return NULL;
+        goto error;
     }
-    return PyCursesWindow_New(win, NULL);
+    res = PyCursesWindow_New(win, NULL);
+
+error:
+    if (fp != NULL)
+        fclose(fp);
+    else if (fd != -1)
+        close(fd);
+    remove(fn);
+    return res;
 }
 
 static PyObject *
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
index e88ae87..e757c82 100644
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -202,6 +202,9 @@
     return 0;
 }
 
+#ifdef O_CLOEXEC
+extern int _Py_open_cloexec_works;
+#endif
 
 static int
 fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
@@ -221,6 +224,11 @@
     int fd = -1;
     int closefd = 1;
     int fd_is_own = 0;
+#ifdef O_CLOEXEC
+    int *atomic_flag_works = &_Py_open_cloexec_works;
+#elif !defined(MS_WINDOWS)
+    int *atomic_flag_works = NULL;
+#endif
 
     assert(PyFileIO_Check(oself));
     if (self->fd >= 0) {
@@ -345,6 +353,11 @@
     if (append)
         flags |= O_APPEND;
 #endif
+#ifdef MS_WINDOWS
+    flags |= O_NOINHERIT;
+#elif defined(O_CLOEXEC)
+    flags |= O_CLOEXEC;
+#endif
 
     if (fd >= 0) {
         if (check_fd(fd))
@@ -369,10 +382,18 @@
             else
 #endif
                 self->fd = open(name, flags, 0666);
+
             Py_END_ALLOW_THREADS
-        } else {
-            PyObject *fdobj = PyObject_CallFunction(
-                                  opener, "Oi", nameobj, flags);
+        }
+        else {
+            PyObject *fdobj;
+
+#ifndef MS_WINDOWS
+            /* the opener may clear the atomic flag */
+            atomic_flag_works = NULL;
+#endif
+
+            fdobj = PyObject_CallFunction(opener, "Oi", nameobj, flags);
             if (fdobj == NULL)
                 goto error;
             if (!PyLong_Check(fdobj)) {
@@ -394,6 +415,11 @@
             PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
             goto error;
         }
+
+#ifndef MS_WINDOWS
+        if (_Py_set_inheritable(self->fd, 0, atomic_flag_works) < 0)
+            goto error;
+#endif
     }
     if (dircheck(self, nameobj) < 0)
         goto error;
diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c
index 8d70107..a2d7022 100644
--- a/Modules/_posixsubprocess.c
+++ b/Modules/_posixsubprocess.c
@@ -35,7 +35,7 @@
 # define FD_DIR "/proc/self/fd"
 #endif
 
-#define POSIX_CALL(call)   if ((call) == -1) goto error
+#define POSIX_CALL(call)   do { if ((call) == -1) goto error; } while (0)
 
 
 /* Maximum file descriptor, initialized on module load. */
@@ -87,7 +87,7 @@
     if (stat("/dev", &dev_stat) != 0)
         return 0;
     if (stat(FD_DIR, &dev_fd_stat) != 0)
-        return 0; 
+        return 0;
     if (dev_stat.st_dev == dev_fd_stat.st_dev)
         return 0;  /* / == /dev == /dev/fd means it is static. #fail */
     return 1;
@@ -136,6 +136,29 @@
     return 0;
 }
 
+static int
+make_inheritable(PyObject *py_fds_to_keep, int errpipe_write)
+{
+    Py_ssize_t i, len;
+
+    len = PySequence_Length(py_fds_to_keep);
+    for (i = 0; i < len; ++i) {
+        PyObject* fdobj = PySequence_Fast_GET_ITEM(py_fds_to_keep, i);
+        long fd = PyLong_AsLong(fdobj);
+        assert(!PyErr_Occurred());
+        assert(0 <= fd && fd <= INT_MAX);
+        if (fd == errpipe_write) {
+            /* errpipe_write is part of py_fds_to_keep. It must be closed at
+               exec(), but kept open in the child process until exec() is
+               called. */
+            continue;
+        }
+        if (_Py_set_inheritable((int)fd, 1, NULL) < 0)
+            return -1;
+    }
+    return 0;
+}
+
 
 /* Close all file descriptors in the range start_fd inclusive to
  * end_fd exclusive except for those in py_fds_to_keep.  If the
@@ -205,18 +228,8 @@
     int fd_dir_fd;
     if (start_fd >= end_fd)
         return;
-#ifdef O_CLOEXEC
-    fd_dir_fd = open(FD_DIR, O_RDONLY | O_CLOEXEC, 0);
-#else
-    fd_dir_fd = open(FD_DIR, O_RDONLY, 0);
-#ifdef FD_CLOEXEC
-    {
-        int old = fcntl(fd_dir_fd, F_GETFD);
-        if (old != -1)
-            fcntl(fd_dir_fd, F_SETFD, old | FD_CLOEXEC);
-    }
-#endif
-#endif
+
+    fd_dir_fd = _Py_open(FD_DIR, O_RDONLY);
     if (fd_dir_fd == -1) {
         /* No way to get a list of open fds. */
         _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep);
@@ -356,16 +369,16 @@
     /* Buffer large enough to hold a hex integer.  We can't malloc. */
     char hex_errno[sizeof(saved_errno)*2+1];
 
+    if (make_inheritable(py_fds_to_keep, errpipe_write) < 0)
+        goto error;
+
     /* Close parent's pipe ends. */
-    if (p2cwrite != -1) {
+    if (p2cwrite != -1)
         POSIX_CALL(close(p2cwrite));
-    }
-    if (c2pread != -1) {
+    if (c2pread != -1)
         POSIX_CALL(close(c2pread));
-    }
-    if (errread != -1) {
+    if (errread != -1)
         POSIX_CALL(close(errread));
-    }
     POSIX_CALL(close(errpipe_read));
 
     /* When duping fds, if there arises a situation where one of the fds is
@@ -379,38 +392,34 @@
        dup2() removes the CLOEXEC flag but we must do it ourselves if dup2()
        would be a no-op (issue #10806). */
     if (p2cread == 0) {
-        int old = fcntl(p2cread, F_GETFD);
-        if (old != -1)
-            fcntl(p2cread, F_SETFD, old & ~FD_CLOEXEC);
-    } else if (p2cread != -1) {
+        if (_Py_set_inheritable(p2cread, 1, NULL) < 0)
+            goto error;
+    }
+    else if (p2cread != -1)
         POSIX_CALL(dup2(p2cread, 0));  /* stdin */
-    }
+
     if (c2pwrite == 1) {
-        int old = fcntl(c2pwrite, F_GETFD);
-        if (old != -1)
-            fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC);
-    } else if (c2pwrite != -1) {
+        if (_Py_set_inheritable(c2pwrite, 1, NULL) < 0)
+            goto error;
+    }
+    else if (c2pwrite != -1)
         POSIX_CALL(dup2(c2pwrite, 1));  /* stdout */
-    }
+
     if (errwrite == 2) {
-        int old = fcntl(errwrite, F_GETFD);
-        if (old != -1)
-            fcntl(errwrite, F_SETFD, old & ~FD_CLOEXEC);
-    } else if (errwrite != -1) {
-        POSIX_CALL(dup2(errwrite, 2));  /* stderr */
+        if (_Py_set_inheritable(errwrite, 1, NULL) < 0)
+            goto error;
     }
+    else if (errwrite != -1)
+        POSIX_CALL(dup2(errwrite, 2));  /* stderr */
 
     /* Close pipe fds.  Make sure we don't close the same fd more than */
     /* once, or standard fds. */
-    if (p2cread > 2) {
+    if (p2cread > 2)
         POSIX_CALL(close(p2cread));
-    }
-    if (c2pwrite > 2 && c2pwrite != p2cread) {
+    if (c2pwrite > 2 && c2pwrite != p2cread)
         POSIX_CALL(close(c2pwrite));
-    }
-    if (errwrite != c2pwrite && errwrite != p2cread && errwrite > 2) {
+    if (errwrite != c2pwrite && errwrite != p2cread && errwrite > 2)
         POSIX_CALL(close(errwrite));
-    }
 
     if (cwd)
         POSIX_CALL(chdir(cwd));
@@ -544,7 +553,7 @@
         PyObject *result;
         _Py_IDENTIFIER(isenabled);
         _Py_IDENTIFIER(disable);
-        
+
         gc_module = PyImport_ImportModule("gc");
         if (gc_module == NULL)
             return NULL;
@@ -721,52 +730,6 @@
 Raises: Only on an error in the parent process.\n\
 ");
 
-PyDoc_STRVAR(subprocess_cloexec_pipe_doc,
-"cloexec_pipe() -> (read_end, write_end)\n\n\
-Create a pipe whose ends have the cloexec flag set.");
-
-static PyObject *
-subprocess_cloexec_pipe(PyObject *self, PyObject *noargs)
-{
-    int fds[2];
-    int res;
-#ifdef HAVE_PIPE2
-    Py_BEGIN_ALLOW_THREADS
-    res = pipe2(fds, O_CLOEXEC);
-    Py_END_ALLOW_THREADS
-    if (res != 0 && errno == ENOSYS)
-    {
-        {
-#endif
-        /* We hold the GIL which offers some protection from other code calling
-         * fork() before the CLOEXEC flags have been set but we can't guarantee
-         * anything without pipe2(). */
-        long oldflags;
-
-        res = pipe(fds);
-
-        if (res == 0) {
-            oldflags = fcntl(fds[0], F_GETFD, 0);
-            if (oldflags < 0) res = oldflags;
-        }
-        if (res == 0)
-            res = fcntl(fds[0], F_SETFD, oldflags | FD_CLOEXEC);
-
-        if (res == 0) {
-            oldflags = fcntl(fds[1], F_GETFD, 0);
-            if (oldflags < 0) res = oldflags;
-        }
-        if (res == 0)
-            res = fcntl(fds[1], F_SETFD, oldflags | FD_CLOEXEC);
-#ifdef HAVE_PIPE2
-        }
-    }
-#endif
-    if (res != 0)
-        return PyErr_SetFromErrno(PyExc_OSError);
-    return Py_BuildValue("(ii)", fds[0], fds[1]);
-}
-
 /* module level code ********************************************************/
 
 PyDoc_STRVAR(module_doc,
@@ -775,7 +738,6 @@
 
 static PyMethodDef module_methods[] = {
     {"fork_exec", subprocess_fork_exec, METH_VARARGS, subprocess_fork_exec_doc},
-    {"cloexec_pipe", subprocess_cloexec_pipe, METH_NOARGS, subprocess_cloexec_pipe_doc},
     {NULL, NULL}  /* sentinel */
 };
 
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index a5086ca..48804ef 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -2350,7 +2350,7 @@
     FILE *f;
     DH *dh;
 
-    f = _Py_fopen(filepath, "rb");
+    f = _Py_fopen_obj(filepath, "rb");
     if (f == NULL) {
         if (!PyErr_Occurred())
             PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath);
diff --git a/Modules/main.c b/Modules/main.c
index 25190b8..9171070 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -143,7 +143,7 @@
 {
     char *startup = Py_GETENV("PYTHONSTARTUP");
     if (startup != NULL && startup[0] != '\0') {
-        FILE *fp = fopen(startup, "r");
+        FILE *fp = _Py_fopen(startup, "r");
         if (fp != NULL) {
             (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf);
             PyErr_Clear();
diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c
index 4b4be61..366dac1 100644
--- a/Modules/mmapmodule.c
+++ b/Modules/mmapmodule.c
@@ -1208,18 +1208,18 @@
         flags |= MAP_ANONYMOUS;
 #else
         /* SVR4 method to map anonymous memory is to open /dev/zero */
-        fd = devzero = open("/dev/zero", O_RDWR);
+        fd = devzero = _Py_open("/dev/zero", O_RDWR);
         if (devzero == -1) {
             Py_DECREF(m_obj);
             PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
         }
 #endif
-    } else {
-        m_obj->fd = dup(fd);
+    }
+    else {
+        m_obj->fd = _Py_dup(fd);
         if (m_obj->fd == -1) {
             Py_DECREF(m_obj);
-            PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
         }
     }
diff --git a/Modules/ossaudiodev.c b/Modules/ossaudiodev.c
index 5e45abd..b0a16db 100644
--- a/Modules/ossaudiodev.c
+++ b/Modules/ossaudiodev.c
@@ -115,7 +115,9 @@
        one open at a time.  This does *not* affect later I/O; OSS
        provides a special ioctl() for non-blocking read/write, which is
        exposed via oss_nonblock() below. */
-    if ((fd = open(devicename, imode|O_NONBLOCK)) == -1) {
+    fd = _Py_open(devicename, imode|O_NONBLOCK);
+
+    if (fd == -1) {
         PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
         return NULL;
     }
@@ -177,7 +179,8 @@
             devicename = "/dev/mixer";
     }
 
-    if ((fd = open(devicename, O_RDWR)) == -1) {
+    fd = _Py_open(devicename, O_RDWR);
+    if (fd == -1) {
         PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
         return NULL;
     }
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index fde6752..6570142 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -3535,10 +3535,7 @@
 #ifdef HAVE_FDOPENDIR
     if (path->fd != -1) {
         /* closedir() closes the FD, so we duplicate it */
-        Py_BEGIN_ALLOW_THREADS
-        fd = dup(path->fd);
-        Py_END_ALLOW_THREADS
-
+        fd = _Py_dup(path->fd);
         if (fd == -1) {
             list = posix_error();
             goto exit;
@@ -5768,7 +5765,7 @@
 static PyObject *
 posix_openpty(PyObject *self, PyObject *noargs)
 {
-    int master_fd, slave_fd;
+    int master_fd = -1, slave_fd = -1;
 #ifndef HAVE_OPENPTY
     char * slave_name;
 #endif
@@ -5781,37 +5778,52 @@
 
 #ifdef HAVE_OPENPTY
     if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0)
-        return posix_error();
+        goto posix_error;
+
+    if (_Py_set_inheritable(master_fd, 0, NULL) < 0)
+        goto error;
+    if (_Py_set_inheritable(slave_fd, 0, NULL) < 0)
+        goto error;
+
 #elif defined(HAVE__GETPTY)
     slave_name = _getpty(&master_fd, O_RDWR, 0666, 0);
     if (slave_name == NULL)
-        return posix_error();
+        goto posix_error;
+    if (_Py_set_inheritable(master_fd, 0, NULL) < 0)
+        goto error;
 
-    slave_fd = open(slave_name, O_RDWR);
+    slave_fd = _Py_open(slave_name, O_RDWR);
     if (slave_fd < 0)
-        return posix_error();
+        goto posix_error;
+
 #else
-    master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */
+    master_fd = _Py_open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */
     if (master_fd < 0)
-        return posix_error();
+        goto posix_error;
+
     sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL);
+
     /* change permission of slave */
     if (grantpt(master_fd) < 0) {
         PyOS_setsig(SIGCHLD, sig_saved);
-        return posix_error();
+        goto posix_error;
     }
+
     /* unlock slave */
     if (unlockpt(master_fd) < 0) {
         PyOS_setsig(SIGCHLD, sig_saved);
-        return posix_error();
+        goto posix_error;
     }
+
     PyOS_setsig(SIGCHLD, sig_saved);
+
     slave_name = ptsname(master_fd); /* get name of slave */
     if (slave_name == NULL)
-        return posix_error();
-    slave_fd = open(slave_name, O_RDWR | O_NOCTTY); /* open slave */
+        goto posix_error;
+
+    slave_fd = _Py_open(slave_name, O_RDWR | O_NOCTTY); /* open slave */
     if (slave_fd < 0)
-        return posix_error();
+        goto posix_error;
 #if !defined(__CYGWIN__) && !defined(HAVE_DEV_PTC)
     ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */
     ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */
@@ -5823,6 +5835,14 @@
 
     return Py_BuildValue("(ii)", master_fd, slave_fd);
 
+posix_error:
+    posix_error();
+error:
+    if (master_fd != -1)
+        close(master_fd);
+    if (slave_fd != -1)
+        close(slave_fd);
+    return NULL;
 }
 #endif /* defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) */
 
@@ -7432,6 +7452,10 @@
 
 /* Functions acting on file descriptors */
 
+#ifdef O_CLOEXEC
+extern int _Py_open_cloexec_works;
+#endif
+
 PyDoc_STRVAR(posix_open__doc__,
 "open(path, flags, mode=0o777, *, dir_fd=None)\n\n\
 Open a file for low level IO.  Returns a file handle (integer).\n\
@@ -7451,6 +7475,11 @@
     int fd;
     PyObject *return_value = NULL;
     static char *keywords[] = {"path", "flags", "mode", "dir_fd", NULL};
+#ifdef O_CLOEXEC
+    int *atomic_flag_works = &_Py_open_cloexec_works;
+#elif !defined(MS_WINDOWS)
+    int *atomic_flag_works = NULL;
+#endif
 
     memset(&path, 0, sizeof(path));
     path.function_name = "open";
@@ -7465,6 +7494,12 @@
         ))
         return NULL;
 
+#ifdef MS_WINDOWS
+    flags |= O_NOINHERIT;
+#elif defined(O_CLOEXEC)
+    flags |= O_CLOEXEC;
+#endif
+
     Py_BEGIN_ALLOW_THREADS
 #ifdef MS_WINDOWS
     if (path.wide)
@@ -7484,6 +7519,13 @@
         goto exit;
     }
 
+#ifndef MS_WINDOWS
+    if (_Py_set_inheritable(fd, 0, atomic_flag_works) < 0) {
+        close(fd);
+        goto exit;
+    }
+#endif
+
     return_value = PyLong_FromLong((long)fd);
 
 exit:
@@ -7540,13 +7582,14 @@
 posix_dup(PyObject *self, PyObject *args)
 {
     int fd;
+
     if (!PyArg_ParseTuple(args, "i:dup", &fd))
         return NULL;
-    if (!_PyVerify_fd(fd))
-        return posix_error();
-    fd = dup(fd);
-    if (fd < 0)
-        return posix_error();
+    
+    fd = _Py_dup(fd);
+    if (fd == -1)
+        return NULL;
+
     return PyLong_FromLong((long)fd);
 }
 
@@ -7556,16 +7599,82 @@
 Duplicate file descriptor.");
 
 static PyObject *
-posix_dup2(PyObject *self, PyObject *args)
+posix_dup2(PyObject *self, PyObject *args, PyObject *kwargs)
 {
-    int fd, fd2, res;
-    if (!PyArg_ParseTuple(args, "ii:dup2", &fd, &fd2))
+    static char *keywords[] = {"fd", "fd2", "inheritable", NULL};
+    int fd, fd2;
+    int inheritable = 1;
+    int res;
+#if defined(HAVE_DUP3) && \
+    !(defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC))
+    /* dup3() is available on Linux 2.6.27+ and glibc 2.9 */
+    int dup3_works = -1;
+#endif
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|i:dup2", keywords,
+                                     &fd, &fd2, &inheritable))
         return NULL;
+
     if (!_PyVerify_fd_dup2(fd, fd2))
         return posix_error();
+
+#ifdef MS_WINDOWS
+    Py_BEGIN_ALLOW_THREADS
     res = dup2(fd, fd2);
+    Py_END_ALLOW_THREADS
     if (res < 0)
         return posix_error();
+
+    /* Character files like console cannot be make non-inheritable */
+    if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) {
+        close(fd2);
+        return NULL;
+    }
+
+#elif defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC)
+    Py_BEGIN_ALLOW_THREADS
+    if (!inheritable)
+        res = fcntl(fd, F_DUP2FD_CLOEXEC, fd2);
+    else
+        res = dup2(fd, fd2);
+    Py_END_ALLOW_THREADS
+    if (res < 0)
+        return posix_error();
+
+#else
+
+#ifdef HAVE_DUP3
+    if (!inheritable && dup3_works != 0) {
+        Py_BEGIN_ALLOW_THREADS
+        res = dup3(fd, fd2, O_CLOEXEC);
+        Py_END_ALLOW_THREADS
+        if (res < 0) {
+            if (dup3_works == -1)
+                dup3_works = (errno != ENOSYS);
+            if (dup3_works)
+                return posix_error();
+        }
+    }
+
+    if (inheritable || dup3_works == 0)
+    {
+#endif
+        Py_BEGIN_ALLOW_THREADS
+        res = dup2(fd, fd2);
+        Py_END_ALLOW_THREADS
+        if (res < 0)
+            return posix_error();
+
+        if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) {
+            close(fd2);
+            return NULL;
+        }
+#ifdef HAVE_DUP3
+    }
+#endif
+
+#endif
+
     Py_INCREF(Py_None);
     return Py_None;
 }
@@ -8052,24 +8161,69 @@
 static PyObject *
 posix_pipe(PyObject *self, PyObject *noargs)
 {
-#if !defined(MS_WINDOWS)
     int fds[2];
-    int res;
-    res = pipe(fds);
-    if (res != 0)
-        return posix_error();
-    return Py_BuildValue("(ii)", fds[0], fds[1]);
-#else /* MS_WINDOWS */
+#ifdef MS_WINDOWS
     HANDLE read, write;
-    int read_fd, write_fd;
+    SECURITY_ATTRIBUTES attr;
     BOOL ok;
-    ok = CreatePipe(&read, &write, NULL, 0);
+#else
+    int res;
+#endif
+
+#ifdef MS_WINDOWS
+    attr.nLength = sizeof(attr);
+    attr.lpSecurityDescriptor = NULL;
+    attr.bInheritHandle = FALSE;
+
+    Py_BEGIN_ALLOW_THREADS
+    ok = CreatePipe(&read, &write, &attr, 0);
+    if (ok) {
+        fds[0] = _open_osfhandle((Py_intptr_t)read, _O_RDONLY);
+        fds[1] = _open_osfhandle((Py_intptr_t)write, _O_WRONLY);
+        if (fds[0] == -1 || fds[1] == -1) {
+            CloseHandle(read);
+            CloseHandle(write);
+            ok = 0;
+        }
+    }
+    Py_END_ALLOW_THREADS
+
     if (!ok)
         return PyErr_SetFromWindowsErr(0);
-    read_fd = _open_osfhandle((Py_intptr_t)read, 0);
-    write_fd = _open_osfhandle((Py_intptr_t)write, 1);
-    return Py_BuildValue("(ii)", read_fd, write_fd);
-#endif /* MS_WINDOWS */
+#else
+
+#ifdef HAVE_PIPE2
+    Py_BEGIN_ALLOW_THREADS
+    res = pipe2(fds, O_CLOEXEC);
+    Py_END_ALLOW_THREADS
+
+    if (res != 0 && errno == ENOSYS)
+    {
+#endif
+        Py_BEGIN_ALLOW_THREADS
+        res = pipe(fds);
+        Py_END_ALLOW_THREADS
+
+        if (res == 0) {
+            if (_Py_set_inheritable(fds[0], 0, NULL) < 0) {
+                close(fds[0]);
+                close(fds[1]);
+                return NULL;
+            }
+            if (_Py_set_inheritable(fds[1], 0, NULL) < 0) {
+                close(fds[0]);
+                close(fds[1]);
+                return NULL;
+            }
+        }
+#ifdef HAVE_PIPE2
+    }
+#endif
+
+    if (res != 0)
+        return PyErr_SetFromErrno(PyExc_OSError);
+#endif /* !MS_WINDOWS */
+    return Py_BuildValue("(ii)", fds[0], fds[1]);
 }
 #endif  /* HAVE_PIPE */
 
@@ -10659,6 +10813,102 @@
         Py_RETURN_NONE;
 }
 
+PyDoc_STRVAR(get_inheritable__doc__,
+    "get_inheritable(fd) -> bool\n" \
+    "\n" \
+    "Get the close-on-exe flag of the specified file descriptor.");
+
+static PyObject*
+posix_get_inheritable(PyObject *self, PyObject *args)
+{
+    int fd;
+    int inheritable;
+
+    if (!PyArg_ParseTuple(args, "i:get_inheritable", &fd))
+        return NULL;
+
+    if (!_PyVerify_fd(fd))
+        return posix_error();
+
+    inheritable = _Py_get_inheritable(fd);
+    if (inheritable < 0)
+        return NULL;
+    return PyBool_FromLong(inheritable);
+}
+
+PyDoc_STRVAR(set_inheritable__doc__,
+    "set_inheritable(fd, inheritable)\n" \
+    "\n" \
+    "Set the inheritable flag of the specified file descriptor.");
+
+static PyObject*
+posix_set_inheritable(PyObject *self, PyObject *args)
+{
+    int fd, inheritable;
+
+    if (!PyArg_ParseTuple(args, "ii:set_inheritable", &fd, &inheritable))
+        return NULL;
+
+    if (!_PyVerify_fd(fd))
+        return posix_error();
+
+    if (_Py_set_inheritable(fd, inheritable, NULL) < 0)
+        return NULL;
+    Py_RETURN_NONE;
+}
+
+
+#ifdef MS_WINDOWS
+PyDoc_STRVAR(get_handle_inheritable__doc__,
+    "get_handle_inheritable(fd) -> bool\n" \
+    "\n" \
+    "Get the close-on-exe flag of the specified file descriptor.");
+
+static PyObject*
+posix_get_handle_inheritable(PyObject *self, PyObject *args)
+{
+    Py_intptr_t handle;
+    DWORD flags;
+
+    if (!PyArg_ParseTuple(args, _Py_PARSE_INTPTR ":get_handle_inheritable", &handle))
+        return NULL;
+
+    if (!GetHandleInformation((HANDLE)handle, &flags)) {
+        PyErr_SetFromWindowsErr(0);
+        return NULL;
+    }
+
+    return PyBool_FromLong(flags & HANDLE_FLAG_INHERIT);
+}
+
+PyDoc_STRVAR(set_handle_inheritable__doc__,
+    "set_handle_inheritable(fd, inheritable)\n" \
+    "\n" \
+    "Set the inheritable flag of the specified handle.");
+
+static PyObject*
+posix_set_handle_inheritable(PyObject *self, PyObject *args)
+{
+    int inheritable = 1;
+    Py_intptr_t handle;
+    DWORD flags;
+
+    if (!PyArg_ParseTuple(args, _Py_PARSE_INTPTR "i:set_handle_inheritable",
+                          &handle, &inheritable))
+        return NULL;
+
+    if (inheritable)
+        flags = HANDLE_FLAG_INHERIT;
+    else
+        flags = 0;
+    if (!SetHandleInformation((HANDLE)handle, HANDLE_FLAG_INHERIT, flags)) {
+        PyErr_SetFromWindowsErr(0);
+        return NULL;
+    }
+    Py_RETURN_NONE;
+}
+#endif   /* MS_WINDOWS */
+
 
 static PyMethodDef posix_methods[] = {
     {"access",          (PyCFunction)posix_access,
@@ -10934,7 +11184,8 @@
     {"closerange",      posix_closerange, METH_VARARGS, posix_closerange__doc__},
     {"device_encoding", device_encoding, METH_VARARGS, device_encoding__doc__},
     {"dup",             posix_dup, METH_VARARGS, posix_dup__doc__},
-    {"dup2",            posix_dup2, METH_VARARGS, posix_dup2__doc__},
+    {"dup2",            (PyCFunction)posix_dup2,
+                        METH_VARARGS | METH_KEYWORDS, posix_dup2__doc__},
 #ifdef HAVE_LOCKF
     {"lockf",           posix_lockf, METH_VARARGS, posix_lockf__doc__},
 #endif
@@ -11105,6 +11356,14 @@
 #endif
     {"cpu_count", (PyCFunction)posix_cpu_count,
                   METH_NOARGS, posix_cpu_count__doc__},
+    {"get_inheritable", posix_get_inheritable, METH_VARARGS, get_inheritable__doc__},
+    {"set_inheritable", posix_set_inheritable, METH_VARARGS, set_inheritable__doc__},
+#ifdef MS_WINDOWS
+    {"get_handle_inheritable", posix_get_handle_inheritable,
+     METH_VARARGS, get_handle_inheritable__doc__},
+    {"set_handle_inheritable", posix_set_handle_inheritable,
+     METH_VARARGS, set_handle_inheritable__doc__},
+#endif
     {NULL,              NULL}            /* Sentinel */
 };
 
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index 5da0d92..0ff09b4 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -1004,7 +1004,7 @@
     */
     limit_result = getrlimit(RLIMIT_NOFILE, &limit);
     if (limit_result != -1)
-        fd_devpoll = open("/dev/poll", O_RDWR);
+        fd_devpoll = _Py_open("/dev/poll", O_RDWR);
     Py_END_ALLOW_THREADS
 
     if (limit_result == -1) {
@@ -1194,6 +1194,7 @@
     if (fd == -1) {
         Py_BEGIN_ALLOW_THREADS
 #ifdef HAVE_EPOLL_CREATE1
+        flags |= EPOLL_CLOEXEC;
         if (flags)
             self->epfd = epoll_create1(flags);
         else
@@ -1209,6 +1210,14 @@
         PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
+
+#ifndef HAVE_EPOLL_CREATE1
+    if (_Py_set_inheritable(self->epfd, 0, NULL) < 0) {
+        Py_DECREF(self);
+        return NULL;
+    }
+#endif
+
     return (PyObject *)self;
 }
 
@@ -1896,13 +1905,19 @@
         PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
+
+    if (fd == -1) {
+        if (_Py_set_inheritable(self->kqfd, 0, NULL) < 0) {
+            Py_DECREF(self);
+            return NULL;
+        }
+    }
     return (PyObject *)self;
 }
 
 static PyObject *
 kqueue_queue_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
-
     if ((args != NULL && PyObject_Size(args)) ||
                     (kwds != NULL && PyObject_Size(kwds))) {
         PyErr_SetString(PyExc_ValueError,
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index c5efbc1..ca8dade 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -97,13 +97,14 @@
 
 /* Socket object documentation */
 PyDoc_STRVAR(sock_doc,
-"socket([family[, type[, proto]]]) -> socket object\n\
+"socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) -> socket object\n\
 \n\
 Open a socket of the given type.  The family argument specifies the\n\
 address family; it defaults to AF_INET.  The type argument specifies\n\
 whether this is a stream (SOCK_STREAM, this is the default)\n\
 or datagram (SOCK_DGRAM) socket.  The protocol argument defaults to 0,\n\
 specifying the default protocol.  Keyword arguments are accepted.\n\
+The socket is created as non-inheritable.\n\
 \n\
 A socket object represents one endpoint of a network connection.\n\
 \n\
@@ -114,7 +115,7 @@
 close() -- close the socket\n\
 connect(addr) -- connect the socket to a remote address\n\
 connect_ex(addr) -- connect, return an error code instead of an exception\n\
-_dup() -- return a new socket fd duplicated from fileno()\n\
+dup() -- return a new socket fd duplicated from fileno()\n\
 fileno() -- return underlying file descriptor\n\
 getpeername() -- return remote address [*]\n\
 getsockname() -- return local address\n\
@@ -356,22 +357,7 @@
 #endif
 
 #ifdef MS_WINDOWS
-/* On Windows a socket is really a handle not an fd */
-static SOCKET
-dup_socket(SOCKET handle)
-{
-    WSAPROTOCOL_INFO info;
-
-    if (WSADuplicateSocket(handle, GetCurrentProcessId(), &info))
-        return INVALID_SOCKET;
-
-    return WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
-                     FROM_PROTOCOL_INFO, &info, 0, WSA_FLAG_OVERLAPPED);
-}
 #define SOCKETCLOSE closesocket
-#else
-/* On Unix we can use dup to duplicate the file descriptor of a socket*/
-#define dup_socket(fd) dup(fd)
 #endif
 
 #ifdef MS_WIN32
@@ -499,6 +485,11 @@
     (errno == expected)
 #endif
 
+#ifdef MS_WINDOWS
+/* Does WSASocket() support the WSA_FLAG_NO_HANDLE_INHERIT flag? */
+static int support_wsa_no_inherit = -1;
+#endif
+
 /* Convenience function to raise an error according to errno
    and return a NULL pointer from a function. */
 
@@ -1955,6 +1946,11 @@
     PyObject *addr = NULL;
     PyObject *res = NULL;
     int timeout;
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+    /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
+    static int accept4_works = -1;
+#endif
+
     if (!getsockaddrlen(s, &addrlen))
         return NULL;
     memset(&addrbuf, 0, addrlen);
@@ -1963,10 +1959,24 @@
         return select_error();
 
     BEGIN_SELECT_LOOP(s)
+
     Py_BEGIN_ALLOW_THREADS
     timeout = internal_select_ex(s, 0, interval);
     if (!timeout) {
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+        if (accept4_works != 0) {
+            newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen,
+                            SOCK_CLOEXEC);
+            if (newfd == INVALID_SOCKET && accept4_works == -1) {
+                /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
+                accept4_works = (errno != ENOSYS);
+            }
+        }
+        if (accept4_works == 0)
+            newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
+#else
         newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
+#endif
     }
     Py_END_ALLOW_THREADS
 
@@ -1979,6 +1989,25 @@
     if (newfd == INVALID_SOCKET)
         return s->errorhandler();
 
+#ifdef MS_WINDOWS
+    if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) {
+        PyErr_SetFromWindowsErr(0);
+        SOCKETCLOSE(newfd);
+        goto finally;
+    }
+#else
+
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+    if (!accept4_works)
+#endif
+    {
+        if (_Py_set_inheritable(newfd, 0, NULL) < 0) {
+            SOCKETCLOSE(newfd);
+            goto finally;
+        }
+    }
+#endif
+
     sock = PyLong_FromSocket_t(newfd);
     if (sock == NULL) {
         SOCKETCLOSE(newfd);
@@ -3909,6 +3938,12 @@
 
 /* Initialize a new socket object. */
 
+#ifdef SOCK_CLOEXEC
+/* socket() and socketpair() fail with EINVAL on Linux kernel older
+ * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */
+static int sock_cloexec_works = -1;
+#endif
+
 /*ARGSUSED*/
 static int
 sock_initobj(PyObject *self, PyObject *args, PyObject *kwds)
@@ -3918,6 +3953,13 @@
     SOCKET_T fd = INVALID_SOCKET;
     int family = AF_INET, type = SOCK_STREAM, proto = 0;
     static char *keywords[] = {"family", "type", "proto", "fileno", 0};
+#ifndef MS_WINDOWS
+#ifdef SOCK_CLOEXEC
+    int *atomic_flag_works = &sock_cloexec_works;
+#else
+    int *atomic_flag_works = NULL;
+#endif
+#endif
 
     if (!PyArg_ParseTupleAndKeywords(args, kwds,
                                      "|iiiO:socket", keywords,
@@ -3962,14 +4004,74 @@
         }
     }
     else {
+#ifdef MS_WINDOWS
+        /* Windows implementation */
+#ifndef WSA_FLAG_NO_HANDLE_INHERIT
+#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
+#endif
+
         Py_BEGIN_ALLOW_THREADS
-        fd = socket(family, type, proto);
+        if (support_wsa_no_inherit) {
+            fd = WSASocket(family, type, proto,
+                           NULL, 0,
+                           WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT);
+            if (fd == INVALID_SOCKET) {
+                /* Windows 7 or Windows 2008 R2 without SP1 or the hotfix */
+                support_wsa_no_inherit = 0;
+                fd = socket(family, type, proto);
+            }
+        }
+        else {
+            fd = socket(family, type, proto);
+        }
         Py_END_ALLOW_THREADS
 
         if (fd == INVALID_SOCKET) {
             set_error();
             return -1;
         }
+
+        if (!support_wsa_no_inherit) {
+            if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0)) {
+                closesocket(fd);
+                PyErr_SetFromWindowsErr(0);
+                return -1;
+            }
+        }
+#else
+        /* UNIX */
+        Py_BEGIN_ALLOW_THREADS
+#ifdef SOCK_CLOEXEC
+        if (sock_cloexec_works != 0) {
+            fd = socket(family, type | SOCK_CLOEXEC, proto);
+            if (sock_cloexec_works == -1) {
+                if (fd >= 0) {
+                    sock_cloexec_works = 1;
+                }
+                else if (errno == EINVAL) {
+                    /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */
+                    sock_cloexec_works = 0;
+                    fd = socket(family, type, proto);
+                }
+            }
+        }
+        else
+#endif
+        {
+            fd = socket(family, type, proto);
+        }
+        Py_END_ALLOW_THREADS
+
+        if (fd == INVALID_SOCKET) {
+            set_error();
+            return -1;
+        }
+
+        if (_Py_set_inheritable(fd, 0, atomic_flag_works) < 0) {
+            SOCKETCLOSE(fd);
+            return -1;
+        }
+#endif
     }
     init_sockobject(s, fd, family, type, proto);
 
@@ -4535,16 +4637,36 @@
 {
     SOCKET_T fd, newfd;
     PyObject *newfdobj;
-
+#ifdef MS_WINDOWS
+    WSAPROTOCOL_INFO info;
+#endif
 
     fd = PyLong_AsSocket_t(fdobj);
     if (fd == (SOCKET_T)(-1) && PyErr_Occurred())
         return NULL;
 
-    newfd = dup_socket(fd);
+#ifdef MS_WINDOWS
+    if (WSADuplicateSocket(fd, GetCurrentProcessId(), &info))
+        return set_error();
+
+    newfd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
+                      FROM_PROTOCOL_INFO,
+                      &info, 0, WSA_FLAG_OVERLAPPED);
     if (newfd == INVALID_SOCKET)
         return set_error();
 
+    if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) {
+        closesocket(newfd);
+        PyErr_SetFromWindowsErr(0);
+        return NULL;
+    }
+#else
+    /* On UNIX, dup can be used to duplicate the file descriptor of a socket */
+    newfd = _Py_dup(fd);
+    if (newfd == INVALID_SOCKET)
+        return NULL; 
+#endif
+
     newfdobj = PyLong_FromSocket_t(newfd);
     if (newfdobj == NULL)
         SOCKETCLOSE(newfd);
@@ -4572,6 +4694,12 @@
     SOCKET_T sv[2];
     int family, type = SOCK_STREAM, proto = 0;
     PyObject *res = NULL;
+#ifdef SOCK_CLOEXEC
+    int *atomic_flag_works = &sock_cloexec_works;
+#else
+    int *atomic_flag_works = NULL;
+#endif
+    int ret;
 
 #if defined(AF_UNIX)
     family = AF_UNIX;
@@ -4581,9 +4709,38 @@
     if (!PyArg_ParseTuple(args, "|iii:socketpair",
                           &family, &type, &proto))
         return NULL;
+
     /* Create a pair of socket fds */
-    if (socketpair(family, type, proto, sv) < 0)
+    Py_BEGIN_ALLOW_THREADS
+#ifdef SOCK_CLOEXEC
+    if (sock_cloexec_works != 0) {
+        ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv);
+        if (sock_cloexec_works == -1) {
+            if (ret >= 0) {
+                sock_cloexec_works = 1;
+            }
+            else if (errno == EINVAL) {
+                /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */
+                sock_cloexec_works = 0;
+                ret = socketpair(family, type, proto, sv);
+            }
+        }
+    }
+    else
+#endif
+    {
+        ret = socketpair(family, type, proto, sv);
+    }
+    Py_END_ALLOW_THREADS
+
+    if (ret < 0)
         return set_error();
+
+    if (_Py_set_inheritable(sv[0], 0, atomic_flag_works) < 0)
+        goto finally;
+    if (_Py_set_inheritable(sv[1], 0, atomic_flag_works) < 0)
+        goto finally;
+
     s0 = new_sockobject(sv[0], family, type, proto);
     if (s0 == NULL)
         goto finally;
@@ -4605,7 +4762,7 @@
 }
 
 PyDoc_STRVAR(socketpair_doc,
-"socketpair([family[, type[, proto]]]) -> (socket object, socket object)\n\
+"socketpair([family[, type [, proto]]]) -> (socket object, socket object)\n\
 \n\
 Create a pair of socket objects from the sockets returned by the platform\n\
 socketpair() function.\n\
@@ -5539,6 +5696,16 @@
     if (!os_init())
         return NULL;
 
+#ifdef MS_WINDOWS
+    if (support_wsa_no_inherit == -1) {
+        DWORD version = GetVersion();
+        DWORD major = (DWORD)LOBYTE(LOWORD(version));
+        DWORD minor = (DWORD)HIBYTE(LOWORD(version));
+        /* need Windows 7 SP1, 2008 R2 SP1 or later */
+        support_wsa_no_inherit = (major >= 6 && minor >= 1);
+    }
+#endif
+
     Py_TYPE(&sock_type) = &PyType_Type;
     m = PyModule_Create(&socketmodule);
     if (m == NULL)
diff --git a/Modules/zipimport.c b/Modules/zipimport.c
index 603efd6..633732f 100644
--- a/Modules/zipimport.c
+++ b/Modules/zipimport.c
@@ -870,7 +870,7 @@
     const char *charset;
     int bootstrap;
 
-    fp = _Py_fopen(archive, "rb");
+    fp = _Py_fopen_obj(archive, "rb");
     if (fp == NULL) {
         if (!PyErr_Occurred())
             PyErr_Format(ZipImportError, "can't open Zip file: %R", archive);
@@ -1064,7 +1064,7 @@
         return NULL;
     }
 
-    fp = _Py_fopen(archive, "rb");
+    fp = _Py_fopen_obj(archive, "rb");
     if (!fp) {
         if (!PyErr_Occurred())
             PyErr_Format(PyExc_IOError,
diff --git a/PC/_msi.c b/PC/_msi.c
index 6aba8b0..eb69014 100644
--- a/PC/_msi.c
+++ b/PC/_msi.c
@@ -55,7 +55,7 @@
 
 static FNFCIOPEN(cb_open)
 {
-    int result = _open(pszFile, oflag, pmode);
+    int result = _open(pszFile, oflag | O_NOINHERIT, pmode);
     if (result == -1)
         *err = errno;
     return result;
@@ -179,7 +179,7 @@
 
     CloseHandle(handle);
 
-    return _open(pszName, _O_RDONLY | _O_BINARY);
+    return _open(pszName, _O_RDONLY | _O_BINARY | O_NOINHERIT);
 }
 
 static PyObject* fcicreate(PyObject* obj, PyObject* args)
diff --git a/PC/bdist_wininst/install.c b/PC/bdist_wininst/install.c
index c11d45d..e9401d9 100644
--- a/PC/bdist_wininst/install.c
+++ b/PC/bdist_wininst/install.c
@@ -743,7 +743,7 @@
     if (pathname == NULL || pathname[0] == '\0')
         return 2;
 
-    fh = open(pathname, _O_RDONLY);
+    fh = open(pathname, _O_RDONLY | O_NOINHERIT);
     if (-1 == fh) {
         fprintf(stderr, "Could not open postinstall-script %s\n",
             pathname);
diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c
index 97990e9..dafb4bd 100644
--- a/Parser/tokenizer.c
+++ b/Parser/tokenizer.c
@@ -1730,10 +1730,15 @@
     FILE *fp;
     char *p_start =NULL , *p_end =NULL , *encoding = NULL;
 
+#ifndef PGEN
+    fd = _Py_dup(fd);
+#else
     fd = dup(fd);
+#endif
     if (fd < 0) {
         return NULL;
     }
+
     fp = fdopen(fd, "r");
     if (fp == NULL) {
         return NULL;
diff --git a/Python/errors.c b/Python/errors.c
index 7985eab..93b1120 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -1042,7 +1042,7 @@
     FILE *fp;
     if (filename == NULL || *filename == '\0' || lineno <= 0)
         return NULL;
-    fp = fopen(filename, "r" PY_STDIOTEXTMODE);
+    fp = _Py_fopen(filename, "r" PY_STDIOTEXTMODE);
     return err_programtext(fp, lineno);
 }
 
@@ -1052,7 +1052,7 @@
     FILE *fp;
     if (filename == NULL || lineno <= 0)
         return NULL;
-    fp = _Py_fopen(filename, "r" PY_STDIOTEXTMODE);
+    fp = _Py_fopen_obj(filename, "r" PY_STDIOTEXTMODE);
     return err_programtext(fp, lineno);
 }
 
diff --git a/Python/fileutils.c b/Python/fileutils.c
index 6983855..1cc7480 100644
--- a/Python/fileutils.c
+++ b/Python/fileutils.c
@@ -9,10 +9,29 @@
 #include <langinfo.h>
 #endif
 
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif /* HAVE_FCNTL_H */
+
 #ifdef __APPLE__
 extern wchar_t* _Py_DecodeUTF8_surrogateescape(const char *s, Py_ssize_t size);
 #endif
 
+#ifdef O_CLOEXEC
+/* Does open() supports the O_CLOEXEC flag? Possible values:
+
+   -1: unknown
+    0: open() ignores O_CLOEXEC flag, ex: Linux kernel older than 2.6.23
+    1: open() supports O_CLOEXEC flag, close-on-exec is set
+
+   The flag is used by _Py_open(), io.FileIO and os.open() */
+int _Py_open_cloexec_works = -1;
+#endif
+
 PyObject *
 _Py_device_encoding(int fd)
 {
@@ -547,14 +566,215 @@
 
 #endif
 
-/* Open a file. Use _wfopen() on Windows, encode the path to the locale
-   encoding and use fopen() otherwise. */
+int
+get_inheritable(int fd, int raise)
+{
+#ifdef MS_WINDOWS
+    HANDLE handle;
+    DWORD flags;
 
+    if (!_PyVerify_fd(fd)) {
+        if (raise)
+            PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    handle = (HANDLE)_get_osfhandle(fd);
+    if (handle == INVALID_HANDLE_VALUE) {
+        if (raise)
+            PyErr_SetFromWindowsErr(0);
+        return -1;
+    }
+
+    if (!GetHandleInformation(handle, &flags)) {
+        if (raise)
+            PyErr_SetFromWindowsErr(0);
+        return -1;
+    }
+
+    return (flags & HANDLE_FLAG_INHERIT);
+#else
+    int flags;
+
+    flags = fcntl(fd, F_GETFD, 0);
+    if (flags == -1) {
+        if (raise)
+            PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+    return !(flags & FD_CLOEXEC);
+#endif
+}
+
+/* Get the inheritable flag of the specified file descriptor.
+   Return 1 if it the file descriptor can be inherited, 0 if it cannot,
+   raise an exception and return -1 on error. */
+int
+_Py_get_inheritable(int fd)
+{
+    return get_inheritable(fd, 1);
+}
+
+static int
+set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works)
+{
+#ifdef MS_WINDOWS
+    HANDLE handle;
+    DWORD flags;
+#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
+    int request;
+    int err;
+#elif defined(HAVE_FCNTL_H)
+    int flags;
+    int res;
+#endif
+
+    /* atomic_flag_works can only be used to make the file descriptor
+       non-inheritable */
+    assert(!(atomic_flag_works != NULL && inheritable));
+
+    if (atomic_flag_works != NULL && !inheritable) {
+        if (*atomic_flag_works == -1) {
+            int inheritable = get_inheritable(fd, raise);
+            if (inheritable == -1)
+                return -1;
+            *atomic_flag_works = !inheritable;
+        }
+
+        if (*atomic_flag_works)
+            return 0;
+    }
+
+#ifdef MS_WINDOWS
+    if (!_PyVerify_fd(fd)) {
+        if (raise)
+            PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    handle = (HANDLE)_get_osfhandle(fd);
+    if (handle == INVALID_HANDLE_VALUE) {
+        if (raise)
+            PyErr_SetFromWindowsErr(0);
+        return -1;
+    }
+
+    if (inheritable)
+        flags = HANDLE_FLAG_INHERIT;
+    else
+        flags = 0;
+    if (!SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags)) {
+        if (raise)
+            PyErr_SetFromWindowsErr(0);
+        return -1;
+    }
+    return 0;
+
+#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
+    if (inheritable)
+        request = FIONCLEX;
+    else
+        request = FIOCLEX;
+    err = ioctl(fd, request);
+    if (err) {
+        if (raise)
+            PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+    return 0;
+
+#else
+    flags = fcntl(fd, F_GETFD);
+    if (flags < 0) {
+        if (raise)
+            PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    if (inheritable)
+        flags &= ~FD_CLOEXEC;
+    else
+        flags |= FD_CLOEXEC;
+    res = fcntl(fd, F_SETFD, flags);
+    if (res < 0) {
+        if (raise)
+            PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+    return 0;
+#endif
+}
+
+/* Make the file descriptor non-inheritable.
+   Return 0 success, set errno and return -1 on error. */
+static int
+make_non_inheritable(int fd)
+{
+    return set_inheritable(fd, 0, 0, NULL);
+}
+
+/* Set the inheritable flag of the specified file descriptor.
+   On success: return 0, on error: raise an exception if raise is nonzero
+   and return -1.
+
+   If atomic_flag_works is not NULL:
+
+    * if *atomic_flag_works==-1, check if the inheritable is set on the file
+      descriptor: if yes, set *atomic_flag_works to 1, otherwise set to 0 and
+      set the inheritable flag
+    * if *atomic_flag_works==1: do nothing
+    * if *atomic_flag_works==0: set inheritable flag to False
+
+   Set atomic_flag_works to NULL if no atomic flag was used to create the
+   file descriptor.
+
+   atomic_flag_works can only be used to make a file descriptor
+   non-inheritable: atomic_flag_works must be NULL if inheritable=1. */
+int
+_Py_set_inheritable(int fd, int inheritable, int *atomic_flag_works)
+{
+    return set_inheritable(fd, inheritable, 1, atomic_flag_works);
+}
+
+/* Open a file with the specified flags (wrapper to open() function).
+   The file descriptor is created non-inheritable. */
+int
+_Py_open(const char *pathname, int flags)
+{
+    int fd;
+#ifdef MS_WINDOWS
+    fd = open(pathname, flags | O_NOINHERIT);
+    if (fd < 0)
+        return fd;
+#else
+
+    int *atomic_flag_works;
+#ifdef O_CLOEXEC
+    atomic_flag_works = &_Py_open_cloexec_works;
+    flags |= O_CLOEXEC;
+#else
+    atomic_flag_works = NULL;
+#endif
+    fd = open(pathname, flags);
+    if (fd < 0)
+        return fd;
+
+    if (set_inheritable(fd, 0, 0, atomic_flag_works) < 0) {
+        close(fd);
+        return -1;
+    }
+#endif   /* !MS_WINDOWS */
+    return fd;
+}
+
+/* Open a file. Use _wfopen() on Windows, encode the path to the locale
+   encoding and use fopen() otherwise. The file descriptor is created
+   non-inheritable. */
 FILE *
 _Py_wfopen(const wchar_t *path, const wchar_t *mode)
 {
-#ifndef MS_WINDOWS
     FILE *f;
+#ifndef MS_WINDOWS
     char *cpath;
     char cmode[10];
     size_t r;
@@ -568,21 +788,42 @@
         return NULL;
     f = fopen(cpath, cmode);
     PyMem_Free(cpath);
-    return f;
 #else
-    return _wfopen(path, mode);
+    f = _wfopen(path, mode);
 #endif
+    if (f == NULL)
+        return NULL;
+    if (make_non_inheritable(fileno(f)) < 0) {
+        fclose(f);
+        return NULL;
+    }
+    return f;
 }
 
-/* Call _wfopen() on Windows, or encode the path to the filesystem encoding and
-   call fopen() otherwise.
+/* Wrapper to fopen(). The file descriptor is created non-inheritable. */
+FILE*
+_Py_fopen(const char *pathname, const char *mode)
+{
+    FILE *f = fopen(pathname, mode);
+    if (f == NULL)
+        return NULL;
+    if (make_non_inheritable(fileno(f)) < 0) {
+        fclose(f);
+        return NULL;
+    }
+    return f;
+}
+
+/* Open a file. Call _wfopen() on Windows, or encode the path to the filesystem
+   encoding and call fopen() otherwise. The file descriptor is created
+   non-inheritable.
 
    Return the new file object on success, or NULL if the file cannot be open or
-   (if PyErr_Occurred()) on unicode error */
-
+   (if PyErr_Occurred()) on unicode error. */
 FILE*
-_Py_fopen(PyObject *path, const char *mode)
+_Py_fopen_obj(PyObject *path, const char *mode)
 {
+    FILE *f;
 #ifdef MS_WINDOWS
     wchar_t *wpath;
     wchar_t wmode[10];
@@ -602,16 +843,21 @@
     if (usize == 0)
         return NULL;
 
-    return _wfopen(wpath, wmode);
+    f = _wfopen(wpath, wmode);
 #else
-    FILE *f;
     PyObject *bytes;
     if (!PyUnicode_FSConverter(path, &bytes))
         return NULL;
     f = fopen(PyBytes_AS_STRING(bytes), mode);
     Py_DECREF(bytes);
-    return f;
 #endif
+    if (f == NULL)
+        return NULL;
+    if (make_non_inheritable(fileno(f)) < 0) {
+        fclose(f);
+        return NULL;
+    }
+    return f;
 }
 
 #ifdef HAVE_READLINK
@@ -729,3 +975,72 @@
 #endif
 }
 
+/* Duplicate a file descriptor. The new file descriptor is created as
+   non-inheritable. Return a new file descriptor on success, raise an OSError
+   exception and return -1 on error.
+
+   The GIL is released to call dup(). The caller must hold the GIL. */
+int
+_Py_dup(int fd)
+{
+#ifdef MS_WINDOWS
+    HANDLE handle;
+    DWORD ftype;
+#endif
+
+    if (!_PyVerify_fd(fd)) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+#ifdef MS_WINDOWS
+    handle = (HANDLE)_get_osfhandle(fd);
+    if (handle == INVALID_HANDLE_VALUE) {
+        PyErr_SetFromWindowsErr(0);
+        return -1;
+    }
+
+    /* get the file type, ignore the error if it failed */
+    ftype = GetFileType(handle);
+
+    Py_BEGIN_ALLOW_THREADS
+    fd = dup(fd);
+    Py_END_ALLOW_THREADS
+    if (fd < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    /* Character files like console cannot be make non-inheritable */
+    if (ftype != FILE_TYPE_CHAR) {
+        if (_Py_set_inheritable(fd, 0, NULL) < 0) {
+            close(fd);
+            return -1;
+        }
+    }
+#elif defined(HAVE_FCNTL_H) && defined(F_DUPFD_CLOEXEC)
+    Py_BEGIN_ALLOW_THREADS
+    fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+    Py_END_ALLOW_THREADS
+    if (fd < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+#else
+    Py_BEGIN_ALLOW_THREADS
+    fd = dup(fd);
+    Py_END_ALLOW_THREADS
+    if (fd < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    if (_Py_set_inheritable(fd, 0, NULL) < 0) {
+        close(fd);
+        return -1;
+    }
+#endif
+    return fd;
+}
+
diff --git a/Python/import.c b/Python/import.c
index b18ba2c..1a162ee 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -1797,7 +1797,7 @@
                           &name, PyUnicode_FSDecoder, &pathname, &fob))
         return NULL;
     if (fob != NULL) {
-        fp = _Py_fopen(pathname, "r");
+        fp = _Py_fopen_obj(pathname, "r");
         if (fp == NULL) {
             Py_DECREF(pathname);
             if (!PyErr_Occurred())
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index dc8f412..522a05d 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -1454,7 +1454,7 @@
         /* Try to run a pyc file. First, re-open in binary */
         if (closeit)
             fclose(fp);
-        if ((pyc_fp = fopen(filename, "rb")) == NULL) {
+        if ((pyc_fp = _Py_fopen(filename, "rb")) == NULL) {
             fprintf(stderr, "python: Can't reopen .pyc file\n");
             goto done;
         }
diff --git a/Python/random.c b/Python/random.c
index 709f980..8cf2fef 100644
--- a/Python/random.c
+++ b/Python/random.c
@@ -101,7 +101,7 @@
 
     assert (0 < size);
 
-    fd = open("/dev/urandom", O_RDONLY);
+    fd = _Py_open("/dev/urandom", O_RDONLY);
     if (fd < 0)
         Py_FatalError("Failed to open /dev/urandom");
 
@@ -134,7 +134,7 @@
         return 0;
 
     Py_BEGIN_ALLOW_THREADS
-    fd = open("/dev/urandom", O_RDONLY);
+    fd = _Py_open("/dev/urandom", O_RDONLY);
     Py_END_ALLOW_THREADS
     if (fd < 0)
     {
diff --git a/configure b/configure
index 0f9ba0f..5910f33 100755
--- a/configure
+++ b/configure
@@ -10275,7 +10275,7 @@
 
 # checks for library functions
 for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
- clock confstr ctermid execv faccessat fchmod fchmodat fchown fchownat \
+ clock confstr ctermid dup3 execv faccessat fchmod fchmodat fchown fchownat \
  fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \
  futimens futimes gai_strerror \
  getgrouplist getgroups getlogin getloadavg getpeername getpgid getpid \
diff --git a/configure.ac b/configure.ac
index e329c21..036ddbd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2807,7 +2807,7 @@
 
 # checks for library functions
 AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
- clock confstr ctermid execv faccessat fchmod fchmodat fchown fchownat \
+ clock confstr ctermid dup3 execv faccessat fchmod fchmodat fchown fchownat \
  fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \
  futimens futimes gai_strerror \
  getgrouplist getgroups getlogin getloadavg getpeername getpgid getpid \
diff --git a/pyconfig.h.in b/pyconfig.h.in
index 6dd9250..65205e4 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -193,6 +193,9 @@
 /* Define to 1 if you have the `dup2' function. */
 #undef HAVE_DUP2
 
+/* Define to 1 if you have the `dup3' function. */
+#undef HAVE_DUP3
+
 /* Defined when any dynamic module loading is enabled. */
 #undef HAVE_DYNAMIC_LOADING