Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-96046: Initialize ht_cached_keys in PyType_Ready() #96047

Merged
merged 10 commits into from
Aug 22, 2022
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:c:func:`PyType_Ready` now initializes ``ht_cached_keys`` and performs
tiran marked this conversation as resolved.
Show resolved Hide resolved
additional checks to ensure that type objects are properly configured. This
avoids crashes in 3rd party packages that don't use regular API to create
new types.
62 changes: 39 additions & 23 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3285,11 +3285,6 @@ type_new_impl(type_new_ctx *ctx)
// Put the proper slots in place
fixup_slot_dispatchers(type);

if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
PyHeapTypeObject *et = (PyHeapTypeObject*)type;
et->ht_cached_keys = _PyDict_NewKeysForClass();
}

if (type_new_set_names(type) < 0) {
goto error;
}
Expand Down Expand Up @@ -3493,11 +3488,11 @@ get_bases_tuple(PyObject *bases_in, PyType_Spec *spec)
}

static inline int
check_basicsize_includes_size_and_offsets(PyTypeObject* type)
type_check_basicsize_includes_size_and_offsets(PyTypeObject* type)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to use a shorter name (it's just more convenient), but you can add a comment listing the few checks done by the function.

Suggested change
type_check_basicsize_includes_size_and_offsets(PyTypeObject* type)
type_check_basicsize(PyTypeObject* type)

{
if (type->tp_alloc != PyType_GenericAlloc) {
tiran marked this conversation as resolved.
Show resolved Hide resolved
// Custom allocators can ignore tp_basicsize
return 1;
return 0;
}
Py_ssize_t max = (Py_ssize_t)type->tp_basicsize;

Expand All @@ -3506,30 +3501,30 @@ check_basicsize_includes_size_and_offsets(PyTypeObject* type)
"tp_basicsize for type '%s' (%d) is too small for base '%s' (%d)",
type->tp_name, type->tp_basicsize,
type->tp_base->tp_name, type->tp_base->tp_basicsize);
tiran marked this conversation as resolved.
Show resolved Hide resolved
return 0;
return -1;
}
if (type->tp_weaklistoffset + (Py_ssize_t)sizeof(PyObject*) > max) {
PyErr_Format(PyExc_TypeError,
"weaklist offset %d is out of bounds for type '%s' (tp_basicsize = %d)",
"weaklist offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)",
type->tp_weaklistoffset,
type->tp_name, type->tp_basicsize);
tiran marked this conversation as resolved.
Show resolved Hide resolved
return 0;
return -1;
}
if (type->tp_dictoffset + (Py_ssize_t)sizeof(PyObject*) > max) {
PyErr_Format(PyExc_TypeError,
"dict offset %d is out of bounds for type '%s' (tp_basicsize = %d)",
"dict offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)",
type->tp_dictoffset,
type->tp_name, type->tp_basicsize);
tiran marked this conversation as resolved.
Show resolved Hide resolved
return 0;
return -1;
}
if (type->tp_vectorcall_offset + (Py_ssize_t)sizeof(vectorcallfunc*) > max) {
PyErr_Format(PyExc_TypeError,
"vectorcall offset %d is out of bounds for type '%s' (tp_basicsize = %d)",
"vectorcall offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)",
type->tp_vectorcall_offset,
type->tp_name, type->tp_basicsize);
return 0;
return -1;
}
return 1;
return 0;
}

PyObject *
Expand Down Expand Up @@ -3830,14 +3825,6 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
goto finally;
}

if (!check_basicsize_includes_size_and_offsets(type)) {
goto finally;
}

if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
res->ht_cached_keys = _PyDict_NewKeysForClass();
}

if (type->tp_doc) {
PyObject *__doc__ = PyUnicode_FromString(_PyType_DocWithoutSignature(type->tp_name, type->tp_doc));
if (!__doc__) {
Expand Down Expand Up @@ -6772,6 +6759,29 @@ type_ready_set_new(PyTypeObject *type)
return 0;
}

static int
type_ready_managed_dict(PyTypeObject *type)
{
if (!(type->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
tiran marked this conversation as resolved.
Show resolved Hide resolved
tiran marked this conversation as resolved.
Show resolved Hide resolved
return 0;
}
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
PyErr_Format(PyExc_SystemError,
"type %s has the Py_TPFLAGS_MANAGED_DICT flag "
"but not Py_TPFLAGS_HEAPTYPE flag",
type->tp_name);
return -1;
}
PyHeapTypeObject* et = (PyHeapTypeObject*)type;
if (et->ht_cached_keys == NULL) {
et->ht_cached_keys = _PyDict_NewKeysForClass();
if (et->ht_cached_keys == NULL) {
PyErr_NoMemory();
tiran marked this conversation as resolved.
Show resolved Hide resolved
return -1;
}
}
return 0;
}

static int
type_ready_post_checks(PyTypeObject *type)
Expand Down Expand Up @@ -6850,6 +6860,12 @@ type_ready(PyTypeObject *type)
if (type_ready_add_subclasses(type) < 0) {
return -1;
}
if (type_ready_managed_dict(type) < 0) {
return -1;
}
if (type_check_basicsize_includes_size_and_offsets(type) < 0) {
return -1;
}
if (type_ready_post_checks(type) < 0) {
return -1;
}
Expand Down