From c353764fd564e401cf47a5d9efab18c72c60014e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 1 Jun 2020 20:59:35 +0200 Subject: [PATCH] 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. --- .../2020-06-01-20-31-07.bpo-40826.XCI4M2.rst | 1 + Parser/myreadline.c | 101 +++++++++++++----- 2 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-06-01-20-31-07.bpo-40826.XCI4M2.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-01-20-31-07.bpo-40826.XCI4M2.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-01-20-31-07.bpo-40826.XCI4M2.rst new file mode 100644 index 00000000000..f79f20d21d4 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-06-01-20-31-07.bpo-40826.XCI4M2.rst @@ -0,0 +1 @@ +Fix GIL usage in :c:func:`PyOS_Readline`: lock the GIL to set an exception. diff --git a/Parser/myreadline.c b/Parser/myreadline.c index 04c2793225c..d2787f0d345 100644 --- a/Parser/myreadline.c +++ b/Parser/myreadline.c @@ -27,22 +27,24 @@ int (*PyOS_InputHook)(void) = NULL; except if PyOS_InterruptOccurred() returns true. */ static int -my_fgets(char *buf, int len, FILE *fp) +my_fgets(PyThreadState* tstate, char *buf, int len, FILE *fp) { #ifdef MS_WINDOWS HANDLE hInterruptEvent; #endif - char *p; - int err; while (1) { - if (PyOS_InputHook != NULL) + if (PyOS_InputHook != NULL) { (void)(PyOS_InputHook)(); + } + errno = 0; clearerr(fp); - p = fgets(buf, len, fp); - if (p != NULL) + char *p = fgets(buf, len, fp); + if (p != NULL) { return 0; /* No error */ - err = errno; + } + int err = errno; + #ifdef MS_WINDOWS /* Ctrl-C anywhere on the line or Ctrl-Z if the only character on a line will set ERROR_OPERATION_ABORTED. Under normal @@ -68,22 +70,26 @@ my_fgets(char *buf, int len, FILE *fp) } } #endif /* MS_WINDOWS */ + if (feof(fp)) { clearerr(fp); return -1; /* EOF */ } + #ifdef EINTR if (err == EINTR) { - int s; - PyEval_RestoreThread(_PyOS_ReadlineTState); - s = PyErr_CheckSignals(); + PyEval_RestoreThread(tstate); + int s = PyErr_CheckSignals(); PyEval_SaveThread(); - if (s < 0) - return 1; - /* try again */ + + if (s < 0) { + return 1; + } + /* try again */ continue; } #endif + if (PyOS_InterruptOccurred()) { return 1; /* Interrupt */ } @@ -98,7 +104,7 @@ my_fgets(char *buf, int len, FILE *fp) extern char _get_console_type(HANDLE handle); char * -_PyOS_WindowsConsoleReadline(HANDLE hStdIn) +_PyOS_WindowsConsoleReadline(PyThreadState *tstate, HANDLE hStdIn) { static wchar_t wbuf_local[1024 * 16]; const DWORD chunk_size = 1024; @@ -133,11 +139,12 @@ _PyOS_WindowsConsoleReadline(HANDLE hStdIn) if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE) == WAIT_OBJECT_0) { ResetEvent(hInterruptEvent); - PyEval_RestoreThread(_PyOS_ReadlineTState); + PyEval_RestoreThread(tstate); s = PyErr_CheckSignals(); PyEval_SaveThread(); - if (s < 0) + if (s < 0) { goto exit; + } } break; } @@ -150,17 +157,22 @@ _PyOS_WindowsConsoleReadline(HANDLE hStdIn) if (wbuf == wbuf_local) { wbuf[total_read] = '\0'; wbuf = (wchar_t*)PyMem_RawMalloc(wbuflen * sizeof(wchar_t)); - if (wbuf) + if (wbuf) { wcscpy_s(wbuf, wbuflen, wbuf_local); + } else { + PyEval_RestoreThread(tstate); PyErr_NoMemory(); + PyEval_SaveThread(); goto exit; } } else { wchar_t *tmp = PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t)); if (tmp == NULL) { + PyEval_RestoreThread(tstate); PyErr_NoMemory(); + PyEval_SaveThread(); goto exit; } wbuf = tmp; @@ -169,33 +181,45 @@ _PyOS_WindowsConsoleReadline(HANDLE hStdIn) if (wbuf[0] == '\x1a') { buf = PyMem_RawMalloc(1); - if (buf) + if (buf) { buf[0] = '\0'; + } else { + PyEval_RestoreThread(tstate); PyErr_NoMemory(); + PyEval_SaveThread(); } goto exit; } - u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, NULL, 0, NULL, NULL); + u8len = WideCharToMultiByte(CP_UTF8, 0, + wbuf, total_read, + NULL, 0, + NULL, NULL); buf = PyMem_RawMalloc(u8len + 1); if (buf == NULL) { + PyEval_RestoreThread(tstate); PyErr_NoMemory(); + PyEval_SaveThread(); goto exit; } - u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, buf, u8len, NULL, NULL); + + u8len = WideCharToMultiByte(CP_UTF8, 0, + wbuf, total_read, + buf, u8len, + NULL, NULL); buf[u8len] = '\0'; exit: - if (wbuf != wbuf_local) + if (wbuf != wbuf_local) { PyMem_RawFree(wbuf); + } if (err) { - PyEval_RestoreThread(_PyOS_ReadlineTState); + PyEval_RestoreThread(tstate); PyErr_SetFromWindowsErr(err); PyEval_SaveThread(); } - return buf; } @@ -209,6 +233,8 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) { size_t n; char *p, *pr; + PyThreadState *tstate = _PyOS_ReadlineTState; + assert(tstate != NULL); #ifdef MS_WINDOWS if (!Py_LegacyWindowsStdioFlag && sys_stdin == stdin) { @@ -230,7 +256,9 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) if (wlen) { wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t)); if (wbuf == NULL) { + PyEval_RestoreThread(tstate); PyErr_NoMemory(); + PyEval_SaveThread(); return NULL; } wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1, @@ -249,7 +277,7 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) } } clearerr(sys_stdin); - return _PyOS_WindowsConsoleReadline(hStdIn); + return _PyOS_WindowsConsoleReadline(tstate, hStdIn); } } #endif @@ -257,16 +285,19 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) n = 100; p = (char *)PyMem_RawMalloc(n); if (p == NULL) { + PyEval_RestoreThread(tstate); PyErr_NoMemory(); + PyEval_SaveThread(); return NULL; } fflush(sys_stdout); - if (prompt) + if (prompt) { fprintf(stderr, "%s", prompt); + } fflush(stderr); - switch (my_fgets(p, (int)n, sys_stdin)) { + switch (my_fgets(tstate, p, (int)n, sys_stdin)) { case 0: /* Normal case */ break; case 1: /* Interrupt */ @@ -278,29 +309,40 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) *p = '\0'; break; } + n = strlen(p); while (n > 0 && p[n-1] != '\n') { size_t incr = n+2; if (incr > INT_MAX) { PyMem_RawFree(p); + PyEval_RestoreThread(tstate); PyErr_SetString(PyExc_OverflowError, "input line too long"); + PyEval_SaveThread(); return NULL; } + pr = (char *)PyMem_RawRealloc(p, n + incr); if (pr == NULL) { PyMem_RawFree(p); + PyEval_RestoreThread(tstate); PyErr_NoMemory(); + PyEval_SaveThread(); return NULL; } p = pr; - if (my_fgets(p+n, (int)incr, sys_stdin) != 0) + + if (my_fgets(tstate, p+n, (int)incr, sys_stdin) != 0) { break; + } n += strlen(p+n); } + pr = (char *)PyMem_RawRealloc(p, n+1); if (pr == NULL) { PyMem_RawFree(p); + PyEval_RestoreThread(tstate); PyErr_NoMemory(); + PyEval_SaveThread(); return NULL; } return pr; @@ -323,7 +365,8 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) char *rv, *res; size_t len; - if (_PyOS_ReadlineTState == _PyThreadState_GET()) { + PyThreadState *tstate = _PyThreadState_GET(); + if (_PyOS_ReadlineTState == tstate) { PyErr_SetString(PyExc_RuntimeError, "can't re-enter readline"); return NULL; @@ -342,7 +385,7 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) } } - _PyOS_ReadlineTState = _PyThreadState_GET(); + _PyOS_ReadlineTState = tstate; Py_BEGIN_ALLOW_THREADS PyThread_acquire_lock(_PyOS_ReadlineLock, 1);