Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6fb5bae

Browse files
committed
Fix race condition in create_stdio()
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.
1 parent 17227a7 commit 6fb5bae

3 files changed

Lines changed: 42 additions & 40 deletions

File tree

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,7 @@ Jan Palus
10371037
Yongzhi Pan
10381038
Martin Panter
10391039
Mathias Panzenböck
1040+
Marco Paolini
10401041
M. Papillon
10411042
Peter Parente
10421043
Alexandre Parenteau

Misc/NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ Release date: tba
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #24891: Fix a race condition at Python startup if the file descriptor
14+
of stdin (0), stdout (1) or stderr (2) is closed while Python is creating
15+
sys.stdin, sys.stdout and sys.stderr objects. These attributes are now set
16+
to None if the creation of the object failed, instead of raising an OSError
17+
exception. Initial patch written by Marco Paolini.
18+
1319
- Issue #21167: NAN operations are now handled correctly when python is
1420
compiled with ICC even if -fp-model strict is not specified.
1521

Python/pythonrun.c

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -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 */
10061021
static PyObject*
10071022
create_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

Comments
 (0)