]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-118331: Handle errors in _PyObject_SetManagedDict (#118334)
authorSam Gross <colesbury@gmail.com>
Mon, 29 Apr 2024 19:49:01 +0000 (15:49 -0400)
committerGitHub <noreply@github.com>
Mon, 29 Apr 2024 19:49:01 +0000 (15:49 -0400)
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.

Include/cpython/object.h
Objects/dictobject.c
Objects/typeobject.c

index a8f57827a964cdff5d968d66b6f78124bb88697f..a6b93b93ab0f7a74f5d3f5840e82d01c30a6a6c4 100644 (file)
@@ -493,7 +493,7 @@ do { \
 PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj);
 
 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);
 
 #define TYPE_MAX_WATCHERS 8
index 43cb2350b7fc71b3814812709147afec63662c36..1f21f70f149c80344cdd7a5e20a8e92972a166e4 100644 (file)
@@ -7056,11 +7056,12 @@ set_dict_inline_values(PyObject *obj, PyDictObject *new_dict)
     }
 }
 
-void
+int
 _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
 {
     assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
     assert(_PyObject_InlineValuesConsistencyCheck(obj));
+    int err = 0;
     PyTypeObject *tp = Py_TYPE(obj);
     if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
         PyDictObject *dict = _PyObject_GetManagedDict(obj);
@@ -7076,11 +7077,11 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
             Py_END_CRITICAL_SECTION();
 
             if (dict == NULL) {
-                return;
+                return 0;
             }
 #else
             set_dict_inline_values(obj, (PyDictObject *)new_dict);
-            return;
+            return 0;
 #endif
         }
 
@@ -7089,15 +7090,16 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
         // We've locked dict, but the actual dict could have changed
         // since we locked it.
         dict = _PyObject_ManagedDictPointer(obj)->dict;
-
-        FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
-                            (PyDictObject *)Py_XNewRef(new_dict));
-
-        _PyDict_DetachFromObject(dict, obj);
-
+        err = _PyDict_DetachFromObject(dict, obj);
+        if (err == 0) {
+            FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
+                                (PyDictObject *)Py_XNewRef(new_dict));
+        }
         Py_END_CRITICAL_SECTION2();
 
-        Py_XDECREF(dict);
+        if (err == 0) {
+            Py_XDECREF(dict);
+        }
     }
     else {
         PyDictObject *dict;
@@ -7114,18 +7116,23 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
         Py_XDECREF(dict);
     }
     assert(_PyObject_InlineValuesConsistencyCheck(obj));
+    return err;
 }
 
 void
 PyObject_ClearManagedDict(PyObject *obj)
 {
-    _PyObject_SetManagedDict(obj, NULL);
+    if (_PyObject_SetManagedDict(obj, NULL) < 0) {
+        PyErr_WriteUnraisable(NULL);
+    }
 }
 
 int
 _PyDict_DetachFromObject(PyDictObject *mp, PyObject *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)) {
         return 0;
index 07e0a5a02da87ff53028f56d85f2aa906055e0a2..50efbb6e182df050e3d053fae7ae8e6e5aef48a1 100644 (file)
@@ -3167,7 +3167,7 @@ subtype_setdict(PyObject *obj, PyObject *value, void *context)
     }
 
     if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
-        _PyObject_SetManagedDict(obj, value);
+        return _PyObject_SetManagedDict(obj, value);
     }
     else {
         dictptr = _PyObject_ComputedDictPointer(obj);