summaryrefslogtreecommitdiffstats
path: root/Python/pythonrun.c
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2024-10-24 16:27:30 (GMT)
committerGitHub <noreply@github.com>2024-10-24 16:27:30 (GMT)
commit52b57eee64c1fe98306fae9643f7c8b66548fab0 (patch)
tree4093685486b127193a8da622014213a23ff46331 /Python/pythonrun.c
parent5c2696bc261905b84c71ddbee3f439af568a8795 (diff)
downloadcpython-52b57eee64c1fe98306fae9643f7c8b66548fab0.zip
cpython-52b57eee64c1fe98306fae9643f7c8b66548fab0.tar.gz
cpython-52b57eee64c1fe98306fae9643f7c8b66548fab0.tar.bz2
[3.13] gh-125842: Fix `sys.exit(0xffff_ffff)` on Windows (GH-125896) (GH-125925)
On Windows, `long` is a signed 32-bit integer so it can't represent `0xffff_ffff` without overflow. Windows exit codes are unsigned 32-bit integers, so if a child process exits with `-1`, it will be represented as `0xffff_ffff`. Also fix a number of other possible cases where `_Py_HandleSystemExit` could return with an exception set, leading to a `SystemError` (or fatal error in debug builds) later on during shutdown. (cherry picked from commit ad6110a93ffa82cae71af6c78692de065d3871b5) Co-authored-by: Sam Gross <colesbury@gmail.com>
Diffstat (limited to 'Python/pythonrun.c')
-rw-r--r--Python/pythonrun.c80
1 files changed, 47 insertions, 33 deletions
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index b675971..5891d50 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -563,6 +563,30 @@ PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags)
return _PyRun_SimpleStringFlagsWithName(command, NULL, flags);
}
+static int
+parse_exit_code(PyObject *code, int *exitcode_p)
+{
+ if (PyLong_Check(code)) {
+ // gh-125842: Use a long long to avoid an overflow error when `long`
+ // is 32-bit. We still truncate the result to an int.
+ int exitcode = (int)PyLong_AsLongLong(code);
+ if (exitcode == -1 && PyErr_Occurred()) {
+ // On overflow or other error, clear the exception and use -1
+ // as the exit code to match historical Python behavior.
+ PyErr_Clear();
+ *exitcode_p = -1;
+ return 1;
+ }
+ *exitcode_p = exitcode;
+ return 1;
+ }
+ else if (code == Py_None) {
+ *exitcode_p = 0;
+ return 1;
+ }
+ return 0;
+}
+
int
_Py_HandleSystemExit(int *exitcode_p)
{
@@ -579,50 +603,40 @@ _Py_HandleSystemExit(int *exitcode_p)
fflush(stdout);
- int exitcode = 0;
-
PyObject *exc = PyErr_GetRaisedException();
- if (exc == NULL) {
- goto done;
- }
- assert(PyExceptionInstance_Check(exc));
+ assert(exc != NULL && PyExceptionInstance_Check(exc));
- /* The error code should be in the `code' attribute. */
PyObject *code = PyObject_GetAttr(exc, &_Py_ID(code));
- if (code) {
+ if (code == NULL) {
+ // If the exception has no 'code' attribute, print the exception below
+ PyErr_Clear();
+ }
+ else if (parse_exit_code(code, exitcode_p)) {
+ Py_DECREF(code);
+ Py_CLEAR(exc);
+ return 1;
+ }
+ else {
+ // If code is not an int or None, print it below
Py_SETREF(exc, code);
- if (exc == Py_None) {
- goto done;
- }
}
- /* If we failed to dig out the 'code' attribute,
- * just let the else clause below print the error.
- */
- if (PyLong_Check(exc)) {
- exitcode = (int)PyLong_AsLong(exc);
+ PyThreadState *tstate = _PyThreadState_GET();
+ PyObject *sys_stderr = _PySys_GetAttr(tstate, &_Py_ID(stderr));
+ if (sys_stderr != NULL && sys_stderr != Py_None) {
+ if (PyFile_WriteObject(exc, sys_stderr, Py_PRINT_RAW) < 0) {
+ PyErr_Clear();
+ }
}
else {
- PyThreadState *tstate = _PyThreadState_GET();
- PyObject *sys_stderr = _PySys_GetAttr(tstate, &_Py_ID(stderr));
- /* We clear the exception here to avoid triggering the assertion
- * in PyObject_Str that ensures it won't silently lose exception
- * details.
- */
- PyErr_Clear();
- if (sys_stderr != NULL && sys_stderr != Py_None) {
- PyFile_WriteObject(exc, sys_stderr, Py_PRINT_RAW);
- } else {
- PyObject_Print(exc, stderr, Py_PRINT_RAW);
- fflush(stderr);
+ if (PyObject_Print(exc, stderr, Py_PRINT_RAW) < 0) {
+ PyErr_Clear();
}
- PySys_WriteStderr("\n");
- exitcode = 1;
+ fflush(stderr);
}
-
-done:
+ PySys_WriteStderr("\n");
Py_CLEAR(exc);
- *exitcode_p = exitcode;
+ *exitcode_p = 1;
return 1;
}