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.
This commit is contained in:
Mark Shannon 2022-08-15 12:29:27 +01:00 committed by GitHub
parent 4a7f5a55dc
commit 3ef3c6306d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 137 additions and 25 deletions

View file

@ -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;
}