Skip to content

Commit c353764

Browse files
authored
bpo-40826: Fix GIL usage in PyOS_Readline() (GH-20579)
Fix GIL usage in PyOS_Readline(): lock the GIL to set an exception. Pass tstate to my_fgets() and _PyOS_WindowsConsoleReadline(). Cleanup these functions.
1 parent b4d5a5c commit c353764

File tree

2 files changed

+73
-29
lines changed

2 files changed

+73
-29
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix GIL usage in :c:func:`PyOS_Readline`: lock the GIL to set an exception.

Parser/myreadline.c

+72-29
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,24 @@ int (*PyOS_InputHook)(void) = NULL;
2727
except if PyOS_InterruptOccurred() returns true. */
2828

2929
static int
30-
my_fgets(char *buf, int len, FILE *fp)
30+
my_fgets(PyThreadState* tstate, char *buf, int len, FILE *fp)
3131
{
3232
#ifdef MS_WINDOWS
3333
HANDLE hInterruptEvent;
3434
#endif
35-
char *p;
36-
int err;
3735
while (1) {
38-
if (PyOS_InputHook != NULL)
36+
if (PyOS_InputHook != NULL) {
3937
(void)(PyOS_InputHook)();
38+
}
39+
4040
errno = 0;
4141
clearerr(fp);
42-
p = fgets(buf, len, fp);
43-
if (p != NULL)
42+
char *p = fgets(buf, len, fp);
43+
if (p != NULL) {
4444
return 0; /* No error */
45-
err = errno;
45+
}
46+
int err = errno;
47+
4648
#ifdef MS_WINDOWS
4749
/* Ctrl-C anywhere on the line or Ctrl-Z if the only character
4850
on a line will set ERROR_OPERATION_ABORTED. Under normal
@@ -68,22 +70,26 @@ my_fgets(char *buf, int len, FILE *fp)
6870
}
6971
}
7072
#endif /* MS_WINDOWS */
73+
7174
if (feof(fp)) {
7275
clearerr(fp);
7376
return -1; /* EOF */
7477
}
78+
7579
#ifdef EINTR
7680
if (err == EINTR) {
77-
int s;
78-
PyEval_RestoreThread(_PyOS_ReadlineTState);
79-
s = PyErr_CheckSignals();
81+
PyEval_RestoreThread(tstate);
82+
int s = PyErr_CheckSignals();
8083
PyEval_SaveThread();
81-
if (s < 0)
82-
return 1;
83-
/* try again */
84+
85+
if (s < 0) {
86+
return 1;
87+
}
88+
/* try again */
8489
continue;
8590
}
8691
#endif
92+
8793
if (PyOS_InterruptOccurred()) {
8894
return 1; /* Interrupt */
8995
}
@@ -98,7 +104,7 @@ my_fgets(char *buf, int len, FILE *fp)
98104
extern char _get_console_type(HANDLE handle);
99105

100106
char *
101-
_PyOS_WindowsConsoleReadline(HANDLE hStdIn)
107+
_PyOS_WindowsConsoleReadline(PyThreadState *tstate, HANDLE hStdIn)
102108
{
103109
static wchar_t wbuf_local[1024 * 16];
104110
const DWORD chunk_size = 1024;
@@ -133,11 +139,12 @@ _PyOS_WindowsConsoleReadline(HANDLE hStdIn)
133139
if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
134140
== WAIT_OBJECT_0) {
135141
ResetEvent(hInterruptEvent);
136-
PyEval_RestoreThread(_PyOS_ReadlineTState);
142+
PyEval_RestoreThread(tstate);
137143
s = PyErr_CheckSignals();
138144
PyEval_SaveThread();
139-
if (s < 0)
145+
if (s < 0) {
140146
goto exit;
147+
}
141148
}
142149
break;
143150
}
@@ -150,17 +157,22 @@ _PyOS_WindowsConsoleReadline(HANDLE hStdIn)
150157
if (wbuf == wbuf_local) {
151158
wbuf[total_read] = '\0';
152159
wbuf = (wchar_t*)PyMem_RawMalloc(wbuflen * sizeof(wchar_t));
153-
if (wbuf)
160+
if (wbuf) {
154161
wcscpy_s(wbuf, wbuflen, wbuf_local);
162+
}
155163
else {
164+
PyEval_RestoreThread(tstate);
156165
PyErr_NoMemory();
166+
PyEval_SaveThread();
157167
goto exit;
158168
}
159169
}
160170
else {
161171
wchar_t *tmp = PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t));
162172
if (tmp == NULL) {
173+
PyEval_RestoreThread(tstate);
163174
PyErr_NoMemory();
175+
PyEval_SaveThread();
164176
goto exit;
165177
}
166178
wbuf = tmp;
@@ -169,33 +181,45 @@ _PyOS_WindowsConsoleReadline(HANDLE hStdIn)
169181

170182
if (wbuf[0] == '\x1a') {
171183
buf = PyMem_RawMalloc(1);
172-
if (buf)
184+
if (buf) {
173185
buf[0] = '\0';
186+
}
174187
else {
188+
PyEval_RestoreThread(tstate);
175189
PyErr_NoMemory();
190+
PyEval_SaveThread();
176191
}
177192
goto exit;
178193
}
179194

180-
u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, NULL, 0, NULL, NULL);
195+
u8len = WideCharToMultiByte(CP_UTF8, 0,
196+
wbuf, total_read,
197+
NULL, 0,
198+
NULL, NULL);
181199
buf = PyMem_RawMalloc(u8len + 1);
182200
if (buf == NULL) {
201+
PyEval_RestoreThread(tstate);
183202
PyErr_NoMemory();
203+
PyEval_SaveThread();
184204
goto exit;
185205
}
186-
u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, buf, u8len, NULL, NULL);
206+
207+
u8len = WideCharToMultiByte(CP_UTF8, 0,
208+
wbuf, total_read,
209+
buf, u8len,
210+
NULL, NULL);
187211
buf[u8len] = '\0';
188212

189213
exit:
190-
if (wbuf != wbuf_local)
214+
if (wbuf != wbuf_local) {
191215
PyMem_RawFree(wbuf);
216+
}
192217

193218
if (err) {
194-
PyEval_RestoreThread(_PyOS_ReadlineTState);
219+
PyEval_RestoreThread(tstate);
195220
PyErr_SetFromWindowsErr(err);
196221
PyEval_SaveThread();
197222
}
198-
199223
return buf;
200224
}
201225

@@ -209,6 +233,8 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
209233
{
210234
size_t n;
211235
char *p, *pr;
236+
PyThreadState *tstate = _PyOS_ReadlineTState;
237+
assert(tstate != NULL);
212238

213239
#ifdef MS_WINDOWS
214240
if (!Py_LegacyWindowsStdioFlag && sys_stdin == stdin) {
@@ -230,7 +256,9 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
230256
if (wlen) {
231257
wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t));
232258
if (wbuf == NULL) {
259+
PyEval_RestoreThread(tstate);
233260
PyErr_NoMemory();
261+
PyEval_SaveThread();
234262
return NULL;
235263
}
236264
wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
@@ -249,24 +277,27 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
249277
}
250278
}
251279
clearerr(sys_stdin);
252-
return _PyOS_WindowsConsoleReadline(hStdIn);
280+
return _PyOS_WindowsConsoleReadline(tstate, hStdIn);
253281
}
254282
}
255283
#endif
256284

257285
n = 100;
258286
p = (char *)PyMem_RawMalloc(n);
259287
if (p == NULL) {
288+
PyEval_RestoreThread(tstate);
260289
PyErr_NoMemory();
290+
PyEval_SaveThread();
261291
return NULL;
262292
}
263293

264294
fflush(sys_stdout);
265-
if (prompt)
295+
if (prompt) {
266296
fprintf(stderr, "%s", prompt);
297+
}
267298
fflush(stderr);
268299

269-
switch (my_fgets(p, (int)n, sys_stdin)) {
300+
switch (my_fgets(tstate, p, (int)n, sys_stdin)) {
270301
case 0: /* Normal case */
271302
break;
272303
case 1: /* Interrupt */
@@ -278,29 +309,40 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
278309
*p = '\0';
279310
break;
280311
}
312+
281313
n = strlen(p);
282314
while (n > 0 && p[n-1] != '\n') {
283315
size_t incr = n+2;
284316
if (incr > INT_MAX) {
285317
PyMem_RawFree(p);
318+
PyEval_RestoreThread(tstate);
286319
PyErr_SetString(PyExc_OverflowError, "input line too long");
320+
PyEval_SaveThread();
287321
return NULL;
288322
}
323+
289324
pr = (char *)PyMem_RawRealloc(p, n + incr);
290325
if (pr == NULL) {
291326
PyMem_RawFree(p);
327+
PyEval_RestoreThread(tstate);
292328
PyErr_NoMemory();
329+
PyEval_SaveThread();
293330
return NULL;
294331
}
295332
p = pr;
296-
if (my_fgets(p+n, (int)incr, sys_stdin) != 0)
333+
334+
if (my_fgets(tstate, p+n, (int)incr, sys_stdin) != 0) {
297335
break;
336+
}
298337
n += strlen(p+n);
299338
}
339+
300340
pr = (char *)PyMem_RawRealloc(p, n+1);
301341
if (pr == NULL) {
302342
PyMem_RawFree(p);
343+
PyEval_RestoreThread(tstate);
303344
PyErr_NoMemory();
345+
PyEval_SaveThread();
304346
return NULL;
305347
}
306348
return pr;
@@ -323,7 +365,8 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
323365
char *rv, *res;
324366
size_t len;
325367

326-
if (_PyOS_ReadlineTState == _PyThreadState_GET()) {
368+
PyThreadState *tstate = _PyThreadState_GET();
369+
if (_PyOS_ReadlineTState == tstate) {
327370
PyErr_SetString(PyExc_RuntimeError,
328371
"can't re-enter readline");
329372
return NULL;
@@ -342,7 +385,7 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
342385
}
343386
}
344387

345-
_PyOS_ReadlineTState = _PyThreadState_GET();
388+
_PyOS_ReadlineTState = tstate;
346389
Py_BEGIN_ALLOW_THREADS
347390
PyThread_acquire_lock(_PyOS_ReadlineLock, 1);
348391

0 commit comments

Comments
 (0)