C-API: การจัดสรรส่วนขยาย PyTypeObject

ฉันพบโค้ดบางส่วนใน PyCXX ที่อาจมีข้อผิดพลาด

มันเป็นข้อผิดพลาดจริงหรือไม่ และหากเป็นเช่นนั้น มีวิธีแก้ไขที่ถูกต้องอย่างไร

นี่คือปัญหา:

struct PythonClassInstance
{
    PyObject_HEAD
    ExtObjBase* m_pycxx_object;
}
:
{
    :
    table->tp_new = extension_object_new; // PyTypeObject
    :
}
:
static PyObject* extension_object_new( 
                 PyTypeObject* subtype, PyObject* args, PyObject* kwds )
{
    PythonClassInstance* o = reinterpret_cast<PythonClassInstance *>
                                       ( subtype->tp_alloc(subtype,0) );
    if( ! o )
        return nullptr;

    o->m_pycxx_object = nullptr;

    PyObject* self = reinterpret_cast<PyObject* >( o );

    return self;
}

ตอนนี้ PyObject_HEAD ขยายเป็น "PyObject ob_base;" ดังนั้น PythonClassInstance จึงขยาย PyObject เล็กน้อยเพื่อให้มีตัวชี้พิเศษ (ซึ่งจะชี้ไปที่การเป็นตัวแทนของ PyCXX สำหรับ PyObject นี้)

tp_alloc จัดสรรหน่วยความจำสำหรับการจัดเก็บ PyObject

จากนั้นโค้ดจะพิมพ์ตัวชี้นี้ไปที่ PythonClassInstance โดยอ้างสิทธิ์ในไบต์พิเศษ 4 (หรือ 8?) ที่ไม่ได้เป็นเจ้าของ!

จากนั้นจะตั้งค่าหน่วยความจำพิเศษนี้เป็น 0

สิ่งนี้ดูอันตรายมาก และฉันแปลกใจที่ไม่มีใครสังเกตเห็นข้อผิดพลาดนี้เลย ความเสี่ยงคือวัตถุในอนาคตบางส่วนจะถูกวางในตำแหน่งนี้ (ซึ่งมีไว้เพื่อจัดเก็บ ExtObjBase*)

จะแก้ไขได้อย่างไร?

PythonClassInstance foo{};

PyObject* tmp = subtype->tp_alloc(subtype,0);

// !!! memcpy sizeof(PyObject) bytes starting from location tmp into location (void*)foo

แต่ฉันคิดว่าตอนนี้บางทีฉันอาจจะต้องปล่อย tmp และฉันไม่คิดว่าฉันควรจะเล่นกับหน่วยความจำโดยตรงแบบนี้ ฉันรู้สึกว่ามันอาจจะเป็นอันตรายต่อเครื่องจักรในตัวการจัดการหน่วยความจำ/การรวบรวมขยะของ Python

ตัวเลือกอื่นคือบางทีฉันสามารถชักชวน tp_alloc ให้จัดสรร 4 ไบต์พิเศษ (หรือตอนนี้เป็น 8 เพียงพอสำหรับตัวชี้) โดยข้ามใน 1 แทนที่จะเป็น 0

เอกสารระบุว่าพารามิเตอร์ตัวที่สองนี้คือ "Py_ssize_t nitems" และ:

หาก tp_itemsize ของประเภทไม่เป็นศูนย์ ฟิลด์ ob_size ของวัตถุควรเริ่มต้นเป็น nitems และความยาวของบล็อกหน่วยความจำที่จัดสรรควรเป็น tp_basicsize + nitemstp_itemsize โดยปัดเศษขึ้นเป็นจำนวนทวีคูณของ sizeof(void) ; มิฉะนั้น จะไม่ได้ใช้ nitems และความยาวของบล็อกควรเป็น tp_basicsize

ดูเหมือนว่าฉันควรจะตั้งค่า:

table->tp_itemsize = sizeof(void*);
:
PyObject* tmp = subtype->tp_alloc(subtype,1);

แก้ไข: เพิ่งลองสิ่งนี้และมันก็ทำให้เกิดข้อขัดข้อง

แต่แล้วเอกสารก็กล่าวต่อไปว่า:

อย่าใช้ฟังก์ชันนี้เพื่อเริ่มต้นอินสแตนซ์อื่นๆ แม้แต่เพื่อจัดสรรหน่วยความจำเพิ่มเติม ที่ควรทำโดย tp_new

ตอนนี้ฉันไม่แน่ใจว่ารหัสนี้เป็นของ tp_new หรือ tp_init

ที่เกี่ยวข้อง:

การส่งผ่านอาร์กิวเมนต์ไปยัง tp_new และ tp_init จากประเภทย่อยใน Python C API

การจัดสรรวัตถุ Python C-API


person P i    schedule 16.11.2014    source แหล่งที่มา


คำตอบ (2)


รหัสถูกต้อง

ตราบใดที่ PyTypeObject สำหรับวัตถุส่วนขยายได้รับการเริ่มต้นอย่างถูกต้อง มันก็ควรจะใช้งานได้

คลาสพื้นฐาน tp_alloc ได้รับ subtype ดังนั้นจึงควรทราบจำนวนหน่วยความจำที่จะจัดสรรโดยการตรวจสอบสมาชิก tp_basicsize

นี่เป็นรูปแบบ Python C/API ทั่วไปตามที่แสดงในบทช่วยสอน

person sterin    schedule 19.11.2014

จริงๆแล้วนี่คือ ข้อผิดพลาดใน PyCXX

ดังนั้นต้องการแปลงคำตอบนี้เป็นความคิดเห็น ซึ่งไม่สมเหตุสมผลเลยที่ฉันไม่สามารถให้เครื่องหมายสีเขียวแสดงว่าสำเร็จได้ ดังนั้นฉันจึงแสดงความคิดเห็น ดังนั้นฉันจึงต้องเดินเตร่เพื่อที่จะผ่านเข้ารอบ เบลอร์

person P i    schedule 18.01.2015