summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2024-06-24 18:33:39 (GMT)
committerGitHub <noreply@github.com>2024-06-24 18:33:39 (GMT)
commit6aee5edb84cfa23f430091270a4118e51894c767 (patch)
tree14d49722bc9e1d20a7a7d1462872965ced66ea4b /Objects
parent9769b7ae064a0546a98cbcbec2561dbaba20cd23 (diff)
downloadcpython-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.c78
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);