summaryrefslogtreecommitdiffstats
path: root/Objects/funcobject.c
diff options
context:
space:
mode:
authorCarl Meyer <carl@oddbird.net>2023-03-08 00:10:58 (GMT)
committerGitHub <noreply@github.com>2023-03-08 00:10:58 (GMT)
commit1e703a473343ed198c9a06a876b25d7d69d4bbd0 (patch)
tree0ca3e31d0953a8ec475a159e19b5570e104eeefa /Objects/funcobject.c
parenta33ca2ad1fcf857817cba505a788e15cf9d6ed0c (diff)
downloadcpython-1e703a473343ed198c9a06a876b25d7d69d4bbd0.zip
cpython-1e703a473343ed198c9a06a876b25d7d69d4bbd0.tar.gz
cpython-1e703a473343ed198c9a06a876b25d7d69d4bbd0.tar.bz2
gh-102381: don't call watcher callback with dead object (#102382)
Co-authored-by: T. Wouters <thomas@python.org>
Diffstat (limited to 'Objects/funcobject.c')
-rw-r--r--Objects/funcobject.c38
1 files changed, 37 insertions, 1 deletions
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index 91a6b3d..99048ea 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -8,6 +8,20 @@
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "structmember.h" // PyMemberDef
+static PyObject* func_repr(PyFunctionObject *op);
+
+static const char *
+func_event_name(PyFunction_WatchEvent event) {
+ switch (event) {
+ #define CASE(op) \
+ case PyFunction_EVENT_##op: \
+ return "PyFunction_EVENT_" #op;
+ PY_FOREACH_FUNC_EVENT(CASE)
+ #undef CASE
+ }
+ Py_UNREACHABLE();
+}
+
static void
notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
PyFunctionObject *func, PyObject *new_value)
@@ -21,7 +35,21 @@ notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
// callback must be non-null if the watcher bit is set
assert(cb != NULL);
if (cb(event, func, new_value) < 0) {
- PyErr_WriteUnraisable((PyObject *) func);
+ // Don't risk resurrecting the func if an unraisablehook keeps a
+ // reference; pass a string as context.
+ PyObject *context = NULL;
+ PyObject *repr = func_repr(func);
+ if (repr != NULL) {
+ context = PyUnicode_FromFormat(
+ "%s watcher callback for %U",
+ func_event_name(event), repr);
+ Py_DECREF(repr);
+ }
+ if (context == NULL) {
+ context = Py_NewRef(Py_None);
+ }
+ PyErr_WriteUnraisable(context);
+ Py_DECREF(context);
}
}
i++;
@@ -33,6 +61,7 @@ static inline void
handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func,
PyObject *new_value)
{
+ assert(Py_REFCNT(func) > 0);
PyInterpreterState *interp = _PyInterpreterState_GET();
assert(interp->_initialized);
if (interp->active_func_watchers) {
@@ -766,7 +795,14 @@ func_clear(PyFunctionObject *op)
static void
func_dealloc(PyFunctionObject *op)
{
+ assert(Py_REFCNT(op) == 0);
+ Py_SET_REFCNT(op, 1);
handle_func_event(PyFunction_EVENT_DESTROY, op, NULL);
+ if (Py_REFCNT(op) > 1) {
+ Py_SET_REFCNT(op, Py_REFCNT(op) - 1);
+ return;
+ }
+ Py_SET_REFCNT(op, 0);
_PyObject_GC_UNTRACK(op);
if (op->func_weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject *) op);