-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
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
_PyObject_CAST breaks code which assumed a static/dynamic casting #94731
Comments
The code is in
AFAICS, C-style cast, with its special-snowflake semantics, is the only kind of cast we can switch to:
And since avoiding C-style casts ( @mhammond, do you happen to have a complete reproducer? |
Yes, pywin32 on 3.11 - pythonwin will not start, see the linked issue. I'm confident a smaller repro could be constructed from the snippets above. |
If you have the time to construct it, I'd appreciate it. I'll try, but I have neither much C++ practice, nor a Windows box. |
You don't actually need a cast here at all, because You could do it easily in C++11 with |
Right, which is why the example above is not doing a cast. The |
A C++11-only reproducer would help as well. |
I believe the following should work:
Essentially the first version of However, this whole mechanism seems to miss the point of using the C++ casts - the reason they're preferred over C casts is because you've narrowed down the list of operations they can perform (so have kind of checked your assumptions). When you create elaborate mechanisms to call whatever type of cast is required, there''s really no advantage over using a C cast, so why bother? |
I can submit a PR for the overly elaborate but working mechanism if useful. Should be easy enough to create a test. |
Other comments imply this mechanism is to enable capabilities inside Python itself - maybe another option is to work out how to make a more formal distinction between external code which is already doing the right thing vs these internal requirements? |
AFAIK, the reason the elaborate mechanism was added was to avoid "old-style cast" warnings. |
I'm not sure what you mean - the code in question never generated warnings in either 3.10 or 3.11. |
Old-style cast warnings need to be turned on with e.g. |
Personally I agree. The code needed to avoid the warning seems worse than the warning to me.
I think it depends on the compiler and the warning level |
So it seems that the fix is simple -- delete the complexity and use |
@encukou Reproducer (somewhat): Definition code:
and code to run it (goes in
On my PC this doesn't crash, but However, the test_cppext test is only a compile test so doesn't run any of the module. Therefore it doesn't actually fail the test - it only fails when you build it yourself and run it manually. Hopefully that's helpful though |
I'd appreciate a review for fixing that, if you have time: #94754 |
Co-Authored-By: da-woods <dw-git@d-woods.co.uk>
I can't test that entire PR because pywin32 doesn't build on 3.12 for unrelated reasons (still working on removing use of the the deprecated unicode apis!) but I cherry-picked 3faf13d into 3.11 and it does indeed work, thanks! |
Co-authored-by: da-woods <dw-git@d-woods.co.uk>
Co-authored-by: da-woods <dw-git@d-woods.co.uk> (cherry picked from commit 6cbb57f) Co-authored-by: Petr Viktorin <encukou@gmail.com>
The fix is now merged. Thank you for the help! |
pywin32 has, since the 90s, used c++ to "inherit" from a PyObject in some cases. In short, it ends up something like:
With a layout like this, a
reinterpret_cast
between aMyObject *
and aPyObject *
will create invalid pointers.Consider something like the following:
This ends up being a macro which uses
_PyObject_CAST
on the param passed toPy_DECREF
. In 3.10, this uses a traditional "c" cast , but in 3.11 it turned into areinterpret_cast
. In the above example, this causesPy_DECREF
to be called with an invalidPyObject *
- 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.The text was updated successfully, but these errors were encountered: