Skip to content

_PyObject_CAST breaks code which assumed a static/dynamic casting #94731

Closed
@mhammond

Description

@mhammond

pywin32 has, since the 90s, used c++ to "inherit" from a PyObject in some cases. In short, it ends up something like:

class MyObject : public PyObject {
  virtual void some_helper();
}

With a layout like this, a reinterpret_cast between a MyObject * and a PyObject * will create invalid pointers.

Consider something like the following:

void MyObject::some_helper() {
  Py_DECREF(this);
}

This ends up being a macro which uses _PyObject_CAST on the param passed to Py_DECREF. In 3.10, this uses a traditional "c" cast , but in 3.11 it turned into a reinterpret_cast. In the above example, this causes Py_DECREF to be called with an invalid PyObject * - which typically doesn't crash at the time, but instead corrupts memory causing a difficult to diagnose crash some time later.

Changing the code to use explicit static_cast<>, or the old-school (PyObject *) cast makes things work, but it would be error prone as it would be easy to miss places where it's used via non-obvious nested macros. It also shouldn't be necessary to make this kind of change - c++ code which has worked for this long should continue to work.

Metadata

Metadata

Labels

3.11only security fixes3.12only security fixestopic-C-APItype-bugAn unexpected behavior, bug, or error

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions