diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2024-06-24 18:33:39 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-24 18:33:39 (GMT) |
commit | 6aee5edb84cfa23f430091270a4118e51894c767 (patch) | |
tree | 14d49722bc9e1d20a7a7d1462872965ced66ea4b /Objects | |
parent | 9769b7ae064a0546a98cbcbec2561dbaba20cd23 (diff) | |
download | cpython-6aee5edb84cfa23f430091270a4118e51894c767.zip cpython-6aee5edb84cfa23f430091270a4118e51894c767.tar.gz cpython-6aee5edb84cfa23f430091270a4118e51894c767.tar.bz2 |
[3.13] gh-120860: Fix a few bugs in `type_setattro` error paths. (GH-120861) (#120963)
Moves the logic to update the type's dictionary to its own function in order
to make the lock scoping more clear.
Also, ensure that `name` is decref'd on the error path.
(cherry picked from commit dee63cb35971b87a09ddda5d6f29cd941f570720)
Co-authored-by: Sam Gross <colesbury@gmail.com>
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/typeobject.c | 78 |
1 files changed, 41 insertions, 37 deletions
diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 964e0cd..63902eb 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5466,6 +5466,42 @@ _Py_type_getattro(PyObject *type, PyObject *name) } static int +type_update_dict(PyTypeObject *type, PyDictObject *dict, PyObject *name, + PyObject *value, PyObject **old_value) +{ + // We don't want any re-entrancy between when we update the dict + // and call type_modified_unlocked, including running the destructor + // of the current value as it can observe the cache in an inconsistent + // state. Because we have an exact unicode and our dict has exact + // unicodes we know that this will all complete without releasing + // the locks. + if (_PyDict_GetItemRef_Unicode_LockHeld(dict, name, old_value) < 0) { + return -1; + } + + /* Clear the VALID_VERSION flag of 'type' and all its + subclasses. This could possibly be unified with the + update_subclasses() recursion in update_slot(), but carefully: + they each have their own conditions on which to stop + recursing into subclasses. */ + type_modified_unlocked(type); + + if (_PyDict_SetItem_LockHeld(dict, name, value) < 0) { + PyErr_Format(PyExc_AttributeError, + "type object '%.50s' has no attribute '%U'", + ((PyTypeObject*)type)->tp_name, name); + _PyObject_SetAttributeErrorContext((PyObject *)type, name); + return -1; + } + + if (is_dunder_name(name)) { + return update_slot(type, name); + } + + return 0; +} + +static int type_setattro(PyObject *self, PyObject *name, PyObject *value) { PyTypeObject *type = (PyTypeObject *)self; @@ -5507,12 +5543,11 @@ type_setattro(PyObject *self, PyObject *name, PyObject *value) assert(!_PyType_HasFeature(metatype, Py_TPFLAGS_INLINE_VALUES)); assert(!_PyType_HasFeature(metatype, Py_TPFLAGS_MANAGED_DICT)); - PyObject *old_value; + PyObject *old_value = NULL; PyObject *descr = _PyType_LookupRef(metatype, name); if (descr != NULL) { descrsetfunc f = Py_TYPE(descr)->tp_descr_set; if (f != NULL) { - old_value = NULL; res = f(descr, (PyObject *)type, value); goto done; } @@ -5528,47 +5563,16 @@ type_setattro(PyObject *self, PyObject *name, PyObject *value) } END_TYPE_LOCK(); if (dict == NULL) { - return -1; + res = -1; + goto done; } } - // We don't want any re-entrancy between when we update the dict - // and call type_modified_unlocked, including running the destructor - // of the current value as it can observe the cache in an inconsistent - // state. Because we have an exact unicode and our dict has exact - // unicodes we know that this will all complete without releasing - // the locks. BEGIN_TYPE_DICT_LOCK(dict); - - if (_PyDict_GetItemRef_Unicode_LockHeld((PyDictObject *)dict, name, &old_value) < 0) { - return -1; - } - - /* Clear the VALID_VERSION flag of 'type' and all its - subclasses. This could possibly be unified with the - update_subclasses() recursion in update_slot(), but carefully: - they each have their own conditions on which to stop - recursing into subclasses. */ - type_modified_unlocked(type); - - res = _PyDict_SetItem_LockHeld((PyDictObject *)dict, name, value); - - if (res == 0) { - if (is_dunder_name(name)) { - res = update_slot(type, name); - } - } - else if (PyErr_ExceptionMatches(PyExc_KeyError)) { - PyErr_Format(PyExc_AttributeError, - "type object '%.50s' has no attribute '%U'", - ((PyTypeObject*)type)->tp_name, name); - - _PyObject_SetAttributeErrorContext((PyObject *)type, name); - } - + res = type_update_dict(type, (PyDictObject *)dict, name, value, &old_value); assert(_PyType_CheckConsistency(type)); - END_TYPE_DICT_LOCK(); + done: Py_DECREF(name); Py_XDECREF(descr); |