python / python/cpython

subtype_dealloc for heap types is a footgun

Đang mở
#138,870 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

interpreter-core topic-C-API type-bug
Ngôn ngữ chính
Python
Star
77.2k
Fork
35.9k
Chỉ số merge pull request
Chỉ số pull request đang chờ

Mô tả

Bug report

Bug description:
#include <Python.h>

static PyType_Slot base_slots[] = {
    {0, 0}
};

static PyType_Spec base_spec = {
    .name = "Base",
    .basicsize = 0,
    .itemsize = 0,
    .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
    .slots = base_slots,
};

static void derived_dealloc(PyObject *o) {
    // call base class dealloc
    destructor base_slot = PyType_GetSlot(Py_TYPE(o), Py_tp_dealloc);
    base_slot(o);
}

static PyType_Slot derived_slots[] = {
    {Py_tp_dealloc, derived_dealloc},
    {0, 0}
};

static PyType_Spec derived_spec = {
    .name = "Derived",
    .basicsize = 0,
    .itemsize = 0,
    .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
    .slots = derived_slots,
};

static int
deallocdemo_module_exec(PyObject *m)
{
    PyObject *base_type = PyType_FromModuleAndSpec(
        m,
        &base_spec,
        NULL
    );
    if (!base_type) return -1;
    if (PyModule_AddObjectRef(m, "Base", base_type) < 0) {
        Py_DECREF(base_type);
        return -1;
    }

    PyObject *derived_type = PyType_FromModuleAndSpec(
        m,
        &derived_spec,
        base_type
    );
    Py_DECREF(base_type);
    if (!derived_type) return -1;
    int add_object_res = PyModule_AddObjectRef(m, "Derived", derived_type);
    Py_DECREF(derived_type);

    return add_object_res;
}

static PyModuleDef_Slot deallocdemo_module_slots[] = {
    {Py_mod_exec, deallocdemo_module_exec},
    {0, NULL}
};

static struct PyModuleDef deallocdemo_module = {
    .m_base = PyModuleDef_HEAD_INIT,
    .m_name = "deallocdemo",
    .m_size = 0,
    .m_slots = deallocdemo_module_slots,
};

PyMODINIT_FUNC
PyInit_deallocdemo(void)
{
    return PyModuleDef_Init(&deallocdemo_module);
}

Essentially, subtype_dealloc prevents any derived type from implementing its own dealloc function. In the example above, trying to destroy a Derived object will cause an infinite loop because subtype_dealloc will call derived_dealloc (which calls subtype_dealloc).

This is something that works better for static-types, where the default was to inherit the tp_dealloc of the base class. I appreciate heap types are a little more complex in the inheritance they allow though.


I suspect this is now too late to change easily, and I can't personally think of any obvious solutions to it. It's obvious possible to work around (by creating a custom tp_dealloc at all levels) so the issue is more "it's a footgun" than "it's unworkable". But it seems worth raising the issue in case it can be improved somehow.

CPython versions tested on:

3.13

Operating systems tested on:

Linux

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Reproducer sử dụng PyType_FromModuleAndSpec, PyType_GetSlot, Py_tp_dealloc và subtype_dealloc; hãy bắt đầu bằng cách lần theo đường dẫn giải phân bổ đó trong phần triển khai kiểu của CPython. Xác lập và kiểm thử hành vi an toàn cho các bộ giải phân bổ của kiểu heap bằng cách sử dụng extension Base/Derived được cung cấp làm trường hợp hồi quy; issue không nêu tên tệp kiểm thử và cũng không quy định giải pháp.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
c, python
Lĩnh vực
backend-api-design
Loại issue
Lỗi
Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Cần làm rõ
Mức phù hợp với người mới
30/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.