https://github.com/python/cpython/commit/7cfede6859586f77f786bda56af4698ae22... commit: 7cfede6859586f77f786bda56af4698ae2245f56 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> committer: GitHub <noreply@github.com> date: 2020-07-28T18:16:19-07:00 summary: closes bpo-38156: Always handle interrupts in PyOS_StdioReadline. (GH-21569) This consolidates the handling of my_fgets return values, so that interrupts are always handled, even if they come after EOF. I believe PyOS_StdioReadline is still buggy in that I/O errors will not result in a proper Python exception being set. However, that is a separate issue. (cherry picked from commit a74eea238f5baba15797e2e8b570d153bc8690a7) Co-authored-by: Benjamin Peterson <benjamin@python.org> files: A Misc/NEWS.d/next/Core and Builtins/2020-07-20-17-01-17.bpo-38156.ptcdRy.rst M Parser/myreadline.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-20-17-01-17.bpo-38156.ptcdRy.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-20-17-01-17.bpo-38156.ptcdRy.rst new file mode 100644 index 0000000000000..254d13cf3ed3a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-07-20-17-01-17.bpo-38156.ptcdRy.rst @@ -0,0 +1 @@ +Handle interrupts that come after EOF correctly in ``PyOS_StdioReadline``. diff --git a/Parser/myreadline.c b/Parser/myreadline.c index d7ed357faa383..29b254f771f16 100644 --- a/Parser/myreadline.c +++ b/Parser/myreadline.c @@ -292,37 +292,16 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) } #endif - n = 100; - p = (char *)PyMem_RawMalloc(n); - if (p == NULL) { - PyEval_RestoreThread(tstate); - PyErr_NoMemory(); - PyEval_SaveThread(); - return NULL; - } - fflush(sys_stdout); if (prompt) { fprintf(stderr, "%s", prompt); } fflush(stderr); - switch (my_fgets(tstate, p, (int)n, sys_stdin)) { - case 0: /* Normal case */ - break; - case 1: /* Interrupt */ - PyMem_RawFree(p); - return NULL; - case -1: /* EOF */ - case -2: /* Error */ - default: /* Shouldn't happen */ - *p = '\0'; - break; - } - - n = strlen(p); - while (n > 0 && p[n-1] != '\n') { - size_t incr = n+2; + n = 0; + p = NULL; + do { + size_t incr = (n > 0) ? n + 2 : 100; if (incr > INT_MAX) { PyMem_RawFree(p); PyEval_RestoreThread(tstate); @@ -330,7 +309,6 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) PyEval_SaveThread(); return NULL; } - pr = (char *)PyMem_RawRealloc(p, n + incr); if (pr == NULL) { PyMem_RawFree(p); @@ -340,12 +318,18 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) return NULL; } p = pr; - - if (my_fgets(tstate, p+n, (int)incr, sys_stdin) != 0) { + int err = my_fgets(tstate, p + n, incr, sys_stdin); + if (err == 1) { + // Interrupt + PyMem_RawFree(p); + return NULL; + } else if (err != 0) { + // EOF or error + p[n] = '\0'; break; } - n += strlen(p+n); - } + n += strlen(p + n); + } while (p[n-1] != '\n'); pr = (char *)PyMem_RawRealloc(p, n+1); if (pr == NULL) {