diff options
author | Antoine Pitrou <antoine@python.org> | 2021-03-05 09:32:50 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-05 09:32:50 (GMT) |
commit | 68245b7a1030287294c65c298975ab9026543fd2 (patch) | |
tree | 629f43bc1fe007f83456358633362358b51535e9 /Modules | |
parent | 02ac6f41e5569ec28d625bb005155903f64cc9ee (diff) | |
download | cpython-68245b7a1030287294c65c298975ab9026543fd2.zip cpython-68245b7a1030287294c65c298975ab9026543fd2.tar.gz cpython-68245b7a1030287294c65c298975ab9026543fd2.tar.bz2 |
bpo-43406: Fix possible race condition where ``PyErr_CheckSignals`` tries to execute a non-Python signal handler (GH-24756)
We can receive signals (at the C level, in `trip_signal()` in signalmodule.c) while `signal.signal` is being called to modify the corresponding handler. Later when `PyErr_CheckSignals()` is called to handle the given signal, the handler may be a non-callable object and would raise a cryptic asynchronous exception.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/signalmodule.c | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 7ac797a..c6564be 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1706,10 +1706,34 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) } _Py_atomic_store_relaxed(&Handlers[i].tripped, 0); + /* Signal handlers can be modified while a signal is received, + * and therefore the fact that trip_signal() or PyErr_SetInterrupt() + * was called doesn't guarantee that there is still a Python + * signal handler for it by the time PyErr_CheckSignals() is called + * (see bpo-43406). + */ + PyObject *func = Handlers[i].func; + if (func == NULL || func == Py_None || func == IgnoreHandler || + func == DefaultHandler) { + /* No Python signal handler due to aforementioned race condition. + * We can't call raise() as it would break the assumption + * that PyErr_SetInterrupt() only *simulates* an incoming + * signal (i.e. it will never kill the process). + * We also don't want to interrupt user code with a cryptic + * asynchronous exception, so instead just write out an + * unraisable error. + */ + PyErr_Format(PyExc_OSError, + "Signal %i ignored due to race condition", + i); + PyErr_WriteUnraisable(Py_None); + continue; + } + PyObject *arglist = Py_BuildValue("(iO)", i, frame); PyObject *result; if (arglist) { - result = _PyObject_Call(tstate, Handlers[i].func, arglist, NULL); + result = _PyObject_Call(tstate, func, arglist, NULL); Py_DECREF(arglist); } else { |