[Python-checkins] cpython (merge 3.5 -> default): Merge 3.5 (create_stdio)

victor.stinner python-checkins at python.org
Fri Sep 4 17:31:51 CEST 2015


https://hg.python.org/cpython/rev/73911e6c97c8
changeset:   97673:73911e6c97c8
parent:      97670:75e10a5cbf43
parent:      97672:d562a421d6cd
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Fri Sep 04 17:30:48 2015 +0200
summary:
  Merge 3.5 (create_stdio)

files:
  Misc/ACKS            |   1 +
  Misc/NEWS            |   6 ++
  Python/pylifecycle.c |  79 ++++++++++++++-----------------
  3 files changed, 44 insertions(+), 42 deletions(-)


diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1066,6 +1066,7 @@
 Yongzhi Pan
 Martin Panter
 Mathias Panzenböck
+Marco Paolini
 M. Papillon
 Peter Parente
 Alexandre Parenteau
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -101,6 +101,12 @@
 Library
 -------
 
+- Issue #24891: Fix a race condition at Python startup if the file descriptor
+  of stdin (0), stdout (1) or stderr (2) is closed while Python is creating
+  sys.stdin, sys.stdout and sys.stderr objects. These attributes are now set
+  to None if the creation of the object failed, instead of raising an OSError
+  exception. Initial patch written by Marco Paolini.
+
 - Issue #24992: Fix error handling and a race condition (related to garbage
   collection) in collections.OrderedDict constructor.
 
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1,4 +1,3 @@
-
 /* Python interpreter top-level routines, including init/exit */
 
 #include "Python.h"
@@ -963,6 +962,23 @@
     }
 }
 
+/* Check if a file descriptor is valid or not.
+   Return 0 if the file descriptor is invalid, return non-zero otherwise. */
+static int
+is_valid_fd(int fd)
+{
+    int fd2;
+    if (fd < 0 || !_PyVerify_fd(fd))
+        return 0;
+    _Py_BEGIN_SUPPRESS_IPH
+    fd2 = dup(fd);
+    if (fd2 >= 0)
+        close(fd2);
+    _Py_END_SUPPRESS_IPH
+    return fd2 >= 0;
+}
+
+/* returns Py_None if the fd is not valid */
 static PyObject*
 create_stdio(PyObject* io,
     int fd, int write_mode, char* name,
@@ -978,6 +994,9 @@
     _Py_IDENTIFIER(TextIOWrapper);
     _Py_IDENTIFIER(mode);
 
+    if (!is_valid_fd(fd))
+        Py_RETURN_NONE;
+
     /* stdin is always opened in buffered mode, first because it shouldn't
        make a difference in common use cases, second because TextIOWrapper
        depends on the presence of a read1() method which only exists on
@@ -1059,23 +1078,17 @@
     Py_XDECREF(stream);
     Py_XDECREF(text);
     Py_XDECREF(raw);
+
+    if (PyErr_ExceptionMatches(PyExc_OSError) && !is_valid_fd(fd)) {
+        /* Issue #24891: the file descriptor was closed after the first
+           is_valid_fd() check was called. Ignore the OSError and set the
+           stream to None. */
+        PyErr_Clear();
+        Py_RETURN_NONE;
+    }
     return NULL;
 }
 
-static int
-is_valid_fd(int fd)
-{
-    int dummy_fd;
-    if (fd < 0 || !_PyVerify_fd(fd))
-        return 0;
-    _Py_BEGIN_SUPPRESS_IPH
-    dummy_fd = dup(fd);
-    if (dummy_fd >= 0)
-        close(dummy_fd);
-    _Py_END_SUPPRESS_IPH
-    return dummy_fd >= 0;
-}
-
 /* Initialize sys.stdin, stdout, stderr and builtins.open */
 static int
 initstdio(void)
@@ -1158,30 +1171,18 @@
      * and fileno() may point to an invalid file descriptor. For example
      * GUI apps don't have valid standard streams by default.
      */
-    if (!is_valid_fd(fd)) {
-        std = Py_None;
-        Py_INCREF(std);
-    }
-    else {
-        std = create_stdio(iomod, fd, 0, "<stdin>", encoding, errors);
-        if (std == NULL)
-            goto error;
-    } /* if (fd < 0) */
+    std = create_stdio(iomod, fd, 0, "<stdin>", encoding, errors);
+    if (std == NULL)
+        goto error;
     PySys_SetObject("__stdin__", std);
     _PySys_SetObjectId(&PyId_stdin, std);
     Py_DECREF(std);
 
     /* Set sys.stdout */
     fd = fileno(stdout);
-    if (!is_valid_fd(fd)) {
-        std = Py_None;
-        Py_INCREF(std);
-    }
-    else {
-        std = create_stdio(iomod, fd, 1, "<stdout>", encoding, errors);
-        if (std == NULL)
-            goto error;
-    } /* if (fd < 0) */
+    std = create_stdio(iomod, fd, 1, "<stdout>", encoding, errors);
+    if (std == NULL)
+        goto error;
     PySys_SetObject("__stdout__", std);
     _PySys_SetObjectId(&PyId_stdout, std);
     Py_DECREF(std);
@@ -1189,15 +1190,9 @@
 #if 1 /* Disable this if you have trouble debugging bootstrap stuff */
     /* Set sys.stderr, replaces the preliminary stderr */
     fd = fileno(stderr);
-    if (!is_valid_fd(fd)) {
-        std = Py_None;
-        Py_INCREF(std);
-    }
-    else {
-        std = create_stdio(iomod, fd, 1, "<stderr>", encoding, "backslashreplace");
-        if (std == NULL)
-            goto error;
-    } /* if (fd < 0) */
+    std = create_stdio(iomod, fd, 1, "<stderr>", encoding, "backslashreplace");
+    if (std == NULL)
+        goto error;
 
     /* Same as hack above, pre-import stderr's codec to avoid recursion
        when import.c tries to write to stderr in verbose mode. */

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list