@@ -1003,6 +1003,21 @@ initsite(void)
10031003 }
10041004}
10051005
1006+ /* Check if a file descriptor is valid or not.
1007+ Return 0 if the file descriptor is invalid, return non-zero otherwise. */
1008+ static int
1009+ is_valid_fd (int fd )
1010+ {
1011+ int fd2 ;
1012+ if (fd < 0 || !_PyVerify_fd (fd ))
1013+ return 0 ;
1014+ fd2 = dup (fd );
1015+ if (fd2 >= 0 )
1016+ close (fd2 );
1017+ return fd2 >= 0 ;
1018+ }
1019+
1020+ /* returns Py_None if the fd is not valid */
10061021static PyObject *
10071022create_stdio (PyObject * io ,
10081023 int fd , int write_mode , char * name ,
@@ -1018,6 +1033,9 @@ create_stdio(PyObject* io,
10181033 _Py_IDENTIFIER (TextIOWrapper );
10191034 _Py_IDENTIFIER (mode );
10201035
1036+ if (!is_valid_fd (fd ))
1037+ Py_RETURN_NONE ;
1038+
10211039 /* stdin is always opened in buffered mode, first because it shouldn't
10221040 make a difference in common use cases, second because TextIOWrapper
10231041 depends on the presence of a read1() method which only exists on
@@ -1099,20 +1117,15 @@ create_stdio(PyObject* io,
10991117 Py_XDECREF (stream );
11001118 Py_XDECREF (text );
11011119 Py_XDECREF (raw );
1102- return NULL ;
1103- }
11041120
1105- static int
1106- is_valid_fd (int fd )
1107- {
1108- int dummy_fd ;
1109- if (fd < 0 || !_PyVerify_fd (fd ))
1110- return 0 ;
1111- dummy_fd = dup (fd );
1112- if (dummy_fd < 0 )
1113- return 0 ;
1114- close (dummy_fd );
1115- return 1 ;
1121+ if (PyErr_ExceptionMatches (PyExc_OSError ) && !is_valid_fd (fd )) {
1122+ /* Issue #24891: the file descriptor was closed after the first
1123+ is_valid_fd() check was called. Ignore the OSError and set the
1124+ stream to None. */
1125+ PyErr_Clear ();
1126+ Py_RETURN_NONE ;
1127+ }
1128+ return NULL ;
11161129}
11171130
11181131/* Initialize sys.stdin, stdout, stderr and builtins.open */
@@ -1188,46 +1201,28 @@ initstdio(void)
11881201 * and fileno() may point to an invalid file descriptor. For example
11891202 * GUI apps don't have valid standard streams by default.
11901203 */
1191- if (!is_valid_fd (fd )) {
1192- std = Py_None ;
1193- Py_INCREF (std );
1194- }
1195- else {
1196- std = create_stdio (iomod , fd , 0 , "<stdin>" , encoding , errors );
1197- if (std == NULL )
1198- goto error ;
1199- } /* if (fd < 0) */
1204+ std = create_stdio (iomod , fd , 0 , "<stdin>" , encoding , errors );
1205+ if (std == NULL )
1206+ goto error ;
12001207 PySys_SetObject ("__stdin__" , std );
12011208 _PySys_SetObjectId (& PyId_stdin , std );
12021209 Py_DECREF (std );
12031210
12041211 /* Set sys.stdout */
12051212 fd = fileno (stdout );
1206- if (!is_valid_fd (fd )) {
1207- std = Py_None ;
1208- Py_INCREF (std );
1209- }
1210- else {
1211- std = create_stdio (iomod , fd , 1 , "<stdout>" , encoding , errors );
1212- if (std == NULL )
1213- goto error ;
1214- } /* if (fd < 0) */
1213+ std = create_stdio (iomod , fd , 1 , "<stdout>" , encoding , errors );
1214+ if (std == NULL )
1215+ goto error ;
12151216 PySys_SetObject ("__stdout__" , std );
12161217 _PySys_SetObjectId (& PyId_stdout , std );
12171218 Py_DECREF (std );
12181219
12191220#if 1 /* Disable this if you have trouble debugging bootstrap stuff */
12201221 /* Set sys.stderr, replaces the preliminary stderr */
12211222 fd = fileno (stderr );
1222- if (!is_valid_fd (fd )) {
1223- std = Py_None ;
1224- Py_INCREF (std );
1225- }
1226- else {
1227- std = create_stdio (iomod , fd , 1 , "<stderr>" , encoding , "backslashreplace" );
1228- if (std == NULL )
1229- goto error ;
1230- } /* if (fd < 0) */
1223+ std = create_stdio (iomod , fd , 1 , "<stderr>" , encoding , "backslashreplace" );
1224+ if (std == NULL )
1225+ goto error ;
12311226
12321227 /* Same as hack above, pre-import stderr's codec to avoid recursion
12331228 when import.c tries to write to stderr in verbose mode. */
0 commit comments