Skip to content

Commit 5493663

Browse files
fix: prevent crash in hamt
1 parent 81518ce commit 5493663

1 file changed

Lines changed: 13 additions & 1 deletion

File tree

‎Python/context.c‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -866,8 +866,16 @@ contextvar_set(PyContextVar *var, PyObject *val)
866866
return -1;
867867
}
868868

869+
// gh-148891: _PyHamt_Assoc can allocate HAMT nodes, which may trigger
870+
// GC. A weakref callback or finalizer could then re-enter
871+
// contextvar_set, replacing ctx->ctx_vars via Py_SETREF and freeing
872+
// the old HAMT while _PyHamt_Assoc is still traversing it.
873+
// Prevent this by holding a strong reference to the current vars.
874+
PyHamtObject *old_vars = context_get_current_vars(ctx);
875+
Py_INCREF(old_vars);
869876
PyHamtObject *new_vars = _PyHamt_Assoc(
870-
context_get_current_vars(ctx), (PyObject *)var, val);
877+
old_vars, (PyObject *)var, val);
878+
Py_DECREF(old_vars);
871879
if (new_vars == NULL) {
872880
return -1;
873881
}
@@ -894,8 +902,12 @@ contextvar_del(PyContextVar *var)
894902
return -1;
895903
}
896904

905+
// gh-148891: same borrowed-reference hazard as contextvar_set:
906+
// _PyHamt_Without allocates nodes, which may trigger GC re-entrancy.
897907
PyHamtObject *vars = context_get_current_vars(ctx);
908+
Py_INCREF(vars);
898909
PyHamtObject *new_vars = _PyHamt_Without(vars, (PyObject *)var);
910+
Py_DECREF(vars);
899911
if (new_vars == NULL) {
900912
return -1;
901913
}

0 commit comments

Comments
 (0)