summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2022-08-15 11:29:27 (GMT)
committerGitHub <noreply@github.com>2022-08-15 11:29:27 (GMT)
commit3ef3c6306def489ba9115f0a8a57ab1e99795a5c (patch)
treef74aa199f7051f57b4577f9a920cf982766a65b4 /Objects
parent4a7f5a55dc88c14cef880ae38a96018514ca9d83 (diff)
downloadcpython-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.c1
-rw-r--r--Objects/typeobject.c70
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;
}