diff options
author | Mark Shannon <mark@hotpy.org> | 2022-08-15 11:29:27 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-15 11:29:27 (GMT) |
commit | 3ef3c6306def489ba9115f0a8a57ab1e99795a5c (patch) | |
tree | f74aa199f7051f57b4577f9a920cf982766a65b4 /Objects | |
parent | 4a7f5a55dc88c14cef880ae38a96018514ca9d83 (diff) | |
download | cpython-3ef3c6306def489ba9115f0a8a57ab1e99795a5c.zip cpython-3ef3c6306def489ba9115f0a8a57ab1e99795a5c.tar.gz cpython-3ef3c6306def489ba9115f0a8a57ab1e99795a5c.tar.bz2 |
GH-95707: Fix uses of `Py_TPFLAGS_MANAGED_DICT` (GH-95854)
* Make sure that tp_dictoffset is correct with Py_TPFLAGS_MANAGED_DICT is set.
* Avoid traversing managed dict twice when subclassing class with Py_TPFLAGS_MANAGED_DICT set.
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/object.c | 1 | ||||
-rw-r--r-- | Objects/typeobject.c | 70 |
2 files changed, 54 insertions, 17 deletions
diff --git a/Objects/object.c b/Objects/object.c index a90c6fa..9bbe0ee 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1064,6 +1064,7 @@ _PyObject_ComputedDictPointer(PyObject *obj) if (dictoffset == 0) return NULL; if (dictoffset < 0) { + assert(dictoffset != -1); Py_ssize_t tsize = Py_SIZE(obj); if (tsize < 0) { tsize = -tsize; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 27b12a0..67dfc6f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1312,16 +1312,21 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg) assert(base); } - if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - int err = _PyObject_VisitManagedDict(self, visit, arg); - if (err) { - return err; + if (type->tp_dictoffset != base->tp_dictoffset) { + assert(base->tp_dictoffset == 0); + if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { + assert(type->tp_dictoffset == -1); + int err = _PyObject_VisitManagedDict(self, visit, arg); + if (err) { + return err; + } + } + else { + PyObject **dictptr = _PyObject_ComputedDictPointer(self); + if (dictptr && *dictptr) { + Py_VISIT(*dictptr); + } } - } - else if (type->tp_dictoffset != base->tp_dictoffset) { - PyObject **dictptr = _PyObject_ComputedDictPointer(self); - if (dictptr && *dictptr) - Py_VISIT(*dictptr); } if (type->tp_flags & Py_TPFLAGS_HEAPTYPE @@ -1380,7 +1385,9 @@ subtype_clear(PyObject *self) /* Clear the instance dict (if any), to break cycles involving only __dict__ slots (as in the case 'self.__dict__ is self'). */ if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - _PyObject_ClearManagedDict(self); + if ((base->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { + _PyObject_ClearManagedDict(self); + } } else if (type->tp_dictoffset != base->tp_dictoffset) { PyObject **dictptr = _PyObject_ComputedDictPointer(self); @@ -3085,20 +3092,15 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type) } } - if (ctx->add_dict && ctx->base->tp_itemsize) { - type->tp_dictoffset = -(long)sizeof(PyObject *); - slotoffset += sizeof(PyObject *); - } - if (ctx->add_weak) { assert(!ctx->base->tp_itemsize); type->tp_weaklistoffset = slotoffset; slotoffset += sizeof(PyObject *); } - if (ctx->add_dict && ctx->base->tp_itemsize == 0) { + if (ctx->add_dict) { assert((type->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0); type->tp_flags |= Py_TPFLAGS_MANAGED_DICT; - type->tp_dictoffset = -slotoffset - sizeof(PyObject *)*3; + type->tp_dictoffset = -1; } type->tp_basicsize = slotoffset; @@ -6161,6 +6163,7 @@ inherit_special(PyTypeObject *type, PyTypeObject *base) COPYVAL(tp_itemsize); COPYVAL(tp_weaklistoffset); COPYVAL(tp_dictoffset); + #undef COPYVAL /* Setup fast subclass flags */ @@ -6567,6 +6570,21 @@ type_ready_fill_dict(PyTypeObject *type) return 0; } +static int +type_ready_dict_offset(PyTypeObject *type) +{ + if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { + if (type->tp_dictoffset > 0 || type->tp_dictoffset < -1) { + PyErr_Format(PyExc_TypeError, + "type %s has the Py_TPFLAGS_MANAGED_DICT flag " + "but tp_dictoffset is set", + type->tp_name); + return -1; + } + type->tp_dictoffset = -1; + } + return 0; +} static int type_ready_mro(PyTypeObject *type) @@ -6775,6 +6793,21 @@ type_ready_post_checks(PyTypeObject *type) type->tp_name); return -1; } + if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { + if (type->tp_dictoffset != -1) { + PyErr_Format(PyExc_SystemError, + "type %s has the Py_TPFLAGS_MANAGED_DICT flag " + "but tp_dictoffset is set to incompatible value", + type->tp_name); + return -1; + } + } + else if (type->tp_dictoffset < sizeof(PyObject)) { + if (type->tp_dictoffset + type->tp_basicsize <= 0) { + PyErr_Format(PyExc_SystemError, + "type %s has a tp_dictoffset that is too small"); + } + } return 0; } @@ -6814,6 +6847,9 @@ type_ready(PyTypeObject *type) if (type_ready_inherit(type) < 0) { return -1; } + if (type_ready_dict_offset(type) < 0) { + return -1; + } if (type_ready_set_hash(type) < 0) { return -1; } |