gh-118331: Handle errors in _PyObject_SetManagedDict (#118334)

When detaching a dict, the `copy_values` call may fail due to
out-of-memory errors. This can be triggered by test_no_memory in
test_repl.
This commit is contained in:
Sam Gross 2024-04-29 15:49:01 -04:00 committed by GitHub
parent ee3413c1c7
commit 79688b5b0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 20 additions and 13 deletions

View File

@ -493,7 +493,7 @@ do { \
PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj); PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj);
PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg); PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg);
PyAPI_FUNC(void) _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict); PyAPI_FUNC(int) _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict);
PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj); PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj);
#define TYPE_MAX_WATCHERS 8 #define TYPE_MAX_WATCHERS 8

View File

@ -7056,11 +7056,12 @@ set_dict_inline_values(PyObject *obj, PyDictObject *new_dict)
} }
} }
void int
_PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict) _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
{ {
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
assert(_PyObject_InlineValuesConsistencyCheck(obj)); assert(_PyObject_InlineValuesConsistencyCheck(obj));
int err = 0;
PyTypeObject *tp = Py_TYPE(obj); PyTypeObject *tp = Py_TYPE(obj);
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
PyDictObject *dict = _PyObject_GetManagedDict(obj); PyDictObject *dict = _PyObject_GetManagedDict(obj);
@ -7076,11 +7077,11 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
Py_END_CRITICAL_SECTION(); Py_END_CRITICAL_SECTION();
if (dict == NULL) { if (dict == NULL) {
return; return 0;
} }
#else #else
set_dict_inline_values(obj, (PyDictObject *)new_dict); set_dict_inline_values(obj, (PyDictObject *)new_dict);
return; return 0;
#endif #endif
} }
@ -7089,15 +7090,16 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
// We've locked dict, but the actual dict could have changed // We've locked dict, but the actual dict could have changed
// since we locked it. // since we locked it.
dict = _PyObject_ManagedDictPointer(obj)->dict; dict = _PyObject_ManagedDictPointer(obj)->dict;
err = _PyDict_DetachFromObject(dict, obj);
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, if (err == 0) {
(PyDictObject *)Py_XNewRef(new_dict)); FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
(PyDictObject *)Py_XNewRef(new_dict));
_PyDict_DetachFromObject(dict, obj); }
Py_END_CRITICAL_SECTION2(); Py_END_CRITICAL_SECTION2();
Py_XDECREF(dict); if (err == 0) {
Py_XDECREF(dict);
}
} }
else { else {
PyDictObject *dict; PyDictObject *dict;
@ -7114,18 +7116,23 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
Py_XDECREF(dict); Py_XDECREF(dict);
} }
assert(_PyObject_InlineValuesConsistencyCheck(obj)); assert(_PyObject_InlineValuesConsistencyCheck(obj));
return err;
} }
void void
PyObject_ClearManagedDict(PyObject *obj) PyObject_ClearManagedDict(PyObject *obj)
{ {
_PyObject_SetManagedDict(obj, NULL); if (_PyObject_SetManagedDict(obj, NULL) < 0) {
PyErr_WriteUnraisable(NULL);
}
} }
int int
_PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj) _PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
{ {
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj); _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj);
assert(_PyObject_ManagedDictPointer(obj)->dict == mp);
assert(_PyObject_InlineValuesConsistencyCheck(obj));
if (FT_ATOMIC_LOAD_PTR_RELAXED(mp->ma_values) != _PyObject_InlineValues(obj)) { if (FT_ATOMIC_LOAD_PTR_RELAXED(mp->ma_values) != _PyObject_InlineValues(obj)) {
return 0; return 0;

View File

@ -3167,7 +3167,7 @@ subtype_setdict(PyObject *obj, PyObject *value, void *context)
} }
if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) { if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
_PyObject_SetManagedDict(obj, value); return _PyObject_SetManagedDict(obj, value);
} }
else { else {
dictptr = _PyObject_ComputedDictPointer(obj); dictptr = _PyObject_ComputedDictPointer(obj);