-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Fix smart_holder
multiple/virtual inheritance bugs in shared_ptr
and unique_ptr
to-Python conversions
#5836
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
base: master
Are you sure you want to change the base?
Conversation
``` /__w/pybind11/pybind11/tests/test_class_sh_mi_thunks.cpp:44:13: error: prefer using 'override' or (rarely) 'final' instead of 'virtual' [modernize-use-override,-warnings-as-errors] 44 | virtual ~Left() = default; | ~~~~~~~ ^ | override /__w/pybind11/pybind11/tests/test_class_sh_mi_thunks.cpp:48:13: error: prefer using 'override' or (rarely) 'final' instead of 'virtual' [modernize-use-override,-warnings-as-errors] 48 | virtual ~Right() = default; | ~~~~~~~ ^ | override ```
CI results @ commit 5f77da3: 66 failing, 2 skipped, 17 successful checks CI: https://github.com/pybind/pybind11/actions/runs/17707681525?pr=5836 |
CI results @ commit 3620ceb: 20 failing, 2 skipped, 63 successful checks CI: https://github.com/pybind/pybind11/actions/runs/17713020523?pr=5836 |
ChatGPT: * shared_ptr’s ctor can throw (control-block alloc). Using get() keeps unique_ptr owning the memory if that happens, so no leak. * Only after the shared_ptr is successfully constructed do you release(), transferring ownership exactly once.
…ampoline code (which often uses the term "alias", too)
…ithin test_class_sh_mi_thunks.cpp
``` /__w/pybind11/pybind11/tests/test_class_sh_mi_thunks.cpp:67:5: error: 'auto ptr' can be declared as 'auto *ptr' [readability-qualified-auto,-warnings-as-errors] 67 | auto ptr = new Diamond; | ^~~~ | auto * ```
smart_holder
multiple/virtual inheritance bugs in shared_ptr
and unique_ptr
to-Python conversions
@henryiii This PR is ready for review. @MannixYang If you get a chance to try this out, please report back. |
@rwgk Thank you very much indeed. Yes, I will give it a try. It should be within the next two days. |
@rwgk Great! This is very useful. My project can now run. Thank you again. |
Thanks a lot for confirming! |
Description
Closes #5786.
This PR fixes two independent bugs in the
smart_holder
machinery, both exposed by new tests involving virtual inheritance.1. Fix in
pybind11/detail/type_caster_base.h
The return-value caster for
std::shared_ptr<T>
was incorrectly usingsrc.get()
(the most-derived pointer) instead ofst.first
(the adjusted subobject pointer appropriate for the registeredtinfo
).On MSVC with virtual inheritance, this mismatch led to passing an
Animal*
(see below) toregister_instance
when bindingTiger
, which caused the virtual-base offset walker to dereference a bogus vbptr.This is the bug reported in #5786. Using
st.first
ensures the correct subobject is registered.2. Fix in
pybind11/detail/struct_smart_holder.h
smart_holder::from_unique_ptr
was storing the subobject pointer as the owned resource in its control block. Under MSVC, destroying through that misaligned pointer caused NX faults in the virtual destructor path.The fix mirrors the
shared_ptr
path: always own the realT*
object start with the deleter, and, if needed, create an aliasingshared_ptr<void>
at the subobject pointer for registration/identity.Tests
test_class_sh_mi_thunks
) was added. This was designed to also fail on Linux/Clang/GCC without the fix (not just on MSVC). Indeed, the diamond case fails across allci.yml
jobs and severaltests-cibw.yml
jobs pre-fix, and passes after fix (see comment).smart_holder_from_unique_ptr
, which immediately exposed the second bug. That test produced 20 MSVC failures pre-fix (comment), and passes after the fix.test_virtual_base_at_offset_0
is included to make it explicit when a compiler/layout places the virtual base at offset 0. This isn’t a true skip, but a way to document in the pytest summary when the test can’t exercise the MI/VI code path. In practice, this skip has never been triggered in any GitHub Actions job.assert
s remain inpybind11/detail/class.h
. They were invaluable during debugging and experimentation, have no impact on non-debug builds, and may be useful again if MI/VI edge cases resurface.Context
The changes here are in the same spirit as PR #4380, which fixed earlier oversights in
smart_holder
MI handling. As noted there, MI use cases are uncommon in downstream projects (and even discouraged in some organizations), so coverage has historically been thin. The new tests give us much better confidence that bothshared_ptr
andunique_ptr
adoption paths are now robust under virtual and multiple inheritance.Full ChatGPT conversation guiding the work on the fixes (very long and involved): https://chatgpt.com/share/68c7950a-61b8-8008-8678-53b96643ce7e
A lot of the credit for the fixes goes to ChatGPT 5 Pro
Suggested changelog entry:
Fixed two
smart_holder
bugs inshared_ptr
andunique_ptr
adoption with multiple/virtual inheritance:shared_ptr
to-Python caster now registers the correct subobject pointer (fixes [BUG]: Using smart_holder in virtual inheritance relationship error #5786).unique_ptr
adoption now owns the proper object start while aliasing subobject pointers for registration, fixing MSVC crashes during destruction.