Skip to content

Commit 5ee9edf

Browse files
committed
Fix races when updating function annotations
Store func_annotations and func_annotate together before releasing the old values. Take the function's critical section in PyFunction_GetAnnotations(). Use an acquire load in PyFunction_GET_ANNOTATIONS().
1 parent 7837be5 commit 5ee9edf

2 files changed

Lines changed: 39 additions & 12 deletions

File tree

‎Include/cpython/funcobject.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,12 @@ static inline PyObject* PyFunction_GET_CLOSURE(PyObject *func) {
123123
#define PyFunction_GET_CLOSURE(func) PyFunction_GET_CLOSURE(_PyObject_CAST(func))
124124

125125
static inline PyObject* PyFunction_GET_ANNOTATIONS(PyObject *func) {
126+
#ifdef Py_GIL_DISABLED
127+
return (PyObject *)_Py_atomic_load_ptr_acquire(
128+
&_PyFunction_CAST(func)->func_annotations);
129+
#else
126130
return _PyFunction_CAST(func)->func_annotations;
131+
#endif
127132
}
128133
#define PyFunction_GET_ANNOTATIONS(func) PyFunction_GET_ANNOTATIONS(_PyObject_CAST(func))
129134

‎Objects/funcobject.c‎

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -536,9 +536,29 @@ PyFunction_SetClosure(PyObject *op, PyObject *closure)
536536
return 0;
537537
}
538538

539+
static void
540+
func_set_annotations_and_annotate(PyFunctionObject *op, PyObject *annotations,
541+
PyObject *annotate)
542+
{
543+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
544+
// Care is needed, finalizers can run when references are dropped. Store
545+
// both new values before dropping references to the old.
546+
PyObject *old_annotations = op->func_annotations;
547+
PyObject *old_annotate = op->func_annotate;
548+
FT_ATOMIC_STORE_PTR_RELEASE(op->func_annotations, annotations);
549+
FT_ATOMIC_STORE_PTR_RELEASE(op->func_annotate, annotate);
550+
if (old_annotations != NULL && !_Py_IsImmortal(old_annotations)) {
551+
_PyObject_XDecRefDelayed(old_annotations);
552+
}
553+
if (old_annotate != NULL && !_Py_IsImmortal(old_annotate)) {
554+
_PyObject_XDecRefDelayed(old_annotate);
555+
}
556+
}
557+
539558
static PyObject *
540559
func_get_annotation_dict(PyFunctionObject *op)
541560
{
561+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
542562
if (op->func_annotations == NULL) {
543563
if (op->func_annotate == NULL || !PyCallable_Check(op->func_annotate)) {
544564
Py_RETURN_NONE;
@@ -555,7 +575,7 @@ func_get_annotation_dict(PyFunctionObject *op)
555575
Py_DECREF(ann_dict);
556576
return NULL;
557577
}
558-
Py_XSETREF(op->func_annotations, ann_dict);
578+
_PyObject_XSetRefDelayed(&op->func_annotations, ann_dict);
559579
return ann_dict;
560580
}
561581
if (PyTuple_CheckExact(op->func_annotations)) {
@@ -577,7 +597,7 @@ func_get_annotation_dict(PyFunctionObject *op)
577597
return NULL;
578598
}
579599
}
580-
Py_SETREF(op->func_annotations, ann_dict);
600+
_PyObject_XSetRefDelayed(&op->func_annotations, ann_dict);
581601
}
582602
assert(PyDict_Check(op->func_annotations));
583603
return op->func_annotations;
@@ -590,7 +610,11 @@ PyFunction_GetAnnotations(PyObject *op)
590610
PyErr_BadInternalCall();
591611
return NULL;
592612
}
593-
return func_get_annotation_dict((PyFunctionObject *)op);
613+
PyObject *d;
614+
Py_BEGIN_CRITICAL_SECTION(op);
615+
d = func_get_annotation_dict((PyFunctionObject *)op);
616+
Py_END_CRITICAL_SECTION();
617+
return d;
594618
}
595619

596620
int
@@ -612,8 +636,7 @@ PyFunction_SetAnnotations(PyObject *op, PyObject *annotations)
612636
}
613637
PyFunctionObject *func = (PyFunctionObject *)op;
614638
Py_BEGIN_CRITICAL_SECTION(func);
615-
Py_XSETREF(func->func_annotations, annotations);
616-
Py_CLEAR(func->func_annotate);
639+
func_set_annotations_and_annotate(func, annotations, NULL);
617640
Py_END_CRITICAL_SECTION();
618641
return 0;
619642
}
@@ -920,12 +943,11 @@ function___annotate___set_impl(PyFunctionObject *self, PyObject *value)
920943
/*[clinic end generated code: output=05b7dfc07ada66cd input=eb6225e358d97448]*/
921944
{
922945
if (Py_IsNone(value)) {
923-
Py_XSETREF(self->func_annotate, Py_NewRef(value));
946+
_PyObject_XSetRefDelayed(&self->func_annotate, Py_NewRef(value));
924947
return 0;
925948
}
926949
else if (PyCallable_Check(value)) {
927-
Py_XSETREF(self->func_annotate, Py_NewRef(value));
928-
Py_CLEAR(self->func_annotations);
950+
func_set_annotations_and_annotate(self, NULL, Py_NewRef(value));
929951
return 0;
930952
}
931953
else {
@@ -950,9 +972,10 @@ function___annotations___get_impl(PyFunctionObject *self)
950972
PyObject *d = NULL;
951973
if (self->func_annotations == NULL &&
952974
(self->func_annotate == NULL || !PyCallable_Check(self->func_annotate))) {
953-
self->func_annotations = PyDict_New();
954-
if (self->func_annotations == NULL)
975+
PyObject *ann_dict = PyDict_New();
976+
if (ann_dict == NULL)
955977
return NULL;
978+
FT_ATOMIC_STORE_PTR_RELEASE(self->func_annotations, ann_dict);
956979
}
957980
d = func_get_annotation_dict(self);
958981
return Py_XNewRef(d);
@@ -979,8 +1002,7 @@ function___annotations___set_impl(PyFunctionObject *self, PyObject *value)
9791002
"__annotations__ must be set to a dict object");
9801003
return -1;
9811004
}
982-
Py_XSETREF(self->func_annotations, Py_XNewRef(value));
983-
Py_CLEAR(self->func_annotate);
1005+
func_set_annotations_and_annotate(self, Py_XNewRef(value), NULL);
9841006
return 0;
9851007
}
9861008

0 commit comments

Comments
 (0)