diff options
author | Sam Gross <colesbury@gmail.com> | 2024-06-24 18:08:23 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-24 18:08:23 (GMT) |
commit | dee63cb35971b87a09ddda5d6f29cd941f570720 (patch) | |
tree | d85aeddbf77b7f2f990ec570688fb716a6e85375 /Objects/typeobject.c | |
parent | 0153fd094019b84e18b8e8451019694595f67f9e (diff) | |
download | cpython-dee63cb35971b87a09ddda5d6f29cd941f570720.zip cpython-dee63cb35971b87a09ddda5d6f29cd941f570720.tar.gz cpython-dee63cb35971b87a09ddda5d6f29cd941f570720.tar.bz2 |
gh-120860: Fix a few bugs in `type_setattro` error paths. (#120861)
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.
Diffstat (limited to 'Objects/typeobject.c')
-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 53c4088..71fcb65 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5657,6 +5657,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; @@ -5698,12 +5734,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; } @@ -5719,47 +5754,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); |