fix: eliminate cross-DSO RTTI reliance in smart_holder functionality (for platforms like macOS).#5728
Conversation
…smart_holder.h). ``` git checkout b194891~1 include/pybind11/detail/struct_smart_holder.h ```
…TE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct.
|
@henryiii b/o release @b-pass b/o internals expertise — The main objective of this PR is to undo the production code change under #5700, because it undermines the This PR kind-of works: test_cross_module_rtti passes without that one-of-a-kind export of ... but, I don't think this is a complete solution. I believe (but I'm not sure!) it's possible to construct other tests that still don't work, depending on
Assuming that's true, we need something a little more involved. I have this super long ChatGPT conversation. Don't read, but JIC you want to poke in there: Near the (current) end of it, I asked it to write up the latest idea: 🧩 Proposal: Cross-DSO-safe retrieval of
This is fragile across DSOs, because: RTTI ( So We already mitigate this (in this PR) by defining: and calling it from the same DSO that created the 🧠 New idea: Keep a list of known At Then, at call site: Because each DSO gets its own instantiation of ✅ Advantages
WDYT? I believe it's not difficult to implement, based on what you see here already. |
Seems reasonable to me. Whenever |
49ed882 to
91bca10
Compare
|
@b-pass Thanks a lot for the feedback. It was reassuring! @henryiii I think the This PR is changing |
Suggested by ChatGPT for these reasons: * Prevents runtime RTTI lookups on nullptr. * Helps avoid undefined behavior if users pass in nulls from failed casts or optional paths. * Ensures consistent return value semantics and no accidental access to vtable.
…SOs: * Replace RTTI-based detection of std::default_delete<T> with a constexpr check to avoid RTTI reliance * Add type_info_equal_across_dso_boundaries() fallback using type_info::name() for RTTI equality across macOS DSOs * Rename related flags and functions for clarity (e.g., builtin → std_default) * Improves ABI robustness and clarity of ownership checks in smart_holder
|
@henryiii FYI, there is one more thing I hope to fix in this PR: This needs to be replaced, so that the I have an idea I want to try, which involves adding a new member to I'll continue to work on this today. |
…tead of `detail::internals` (no searching across DSOs required).
…rees this isn't needed.
std::get_deleter<guarded_delete>() through internalssmart_holder functionality (for platforms like macOS).
|
Seems OK to me, though I'm not that familiar with the smart holder. |
Thank you @b-pass! |
There was a problem hiding this comment.
Pull Request Overview
This PR removes cross‐DSO RTTI reliance in smart_holder and trampoline_self_life_support functionality to improve platform compatibility (especially on macOS). Key changes include:
- Incorporating cross-DSO-safe function pointers for guarded_delete and trampoline_self_life_support.
- Updating smart_holder API to require explicit get_guarded_delete parameters for deleter operations.
- Adjusting type caster and cast routines to pass type_info explicitly for safe pointer operations.
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/pure_cpp/smart_holder_poc_test.cpp | Updated test calls to disown/reclaim_disowned with get_guarded_delete. |
| tests/pure_cpp/smart_holder_poc.h | Updated unique_ptr conversion functions to use the new deleter API. |
| include/pybind11/trampoline_self_life_support.h | Added function pointer alias for trampoline self life support. |
| include/pybind11/pybind11.h | Registered the trampoline self life support getter in the type record. |
| include/pybind11/detail/type_caster_base.h | Updated all calls to use tinfo->get_memory_guarded_delete and trampoline accessor. |
| include/pybind11/detail/struct_smart_holder.h | Renamed deleter checks and modified API to require get_guarded_delete_fn. |
| include/pybind11/detail/internals.h | Bumped the PYBIND11_INTERNALS_VERSION to 11. |
| include/pybind11/cast.h | Altered cast path to pass type_info when loading smart_holder pointers. |
| include/pybind11/attr.h | Added default trampoline_self_life_support getter for attribute casting. |
Comments suppressed due to low confidence (2)
include/pybind11/detail/struct_smart_holder.h:311
- Consider expanding the documentation on the new get_guarded_delete_fn parameter for functions like disown, reclaim_disowned, and release_ownership to clarify its role in ensuring cross-DSO safety.
void disown(const get_guarded_delete_fn ggd_fn) {
include/pybind11/detail/struct_smart_holder.h:125
- [nitpick] Since the naming has changed from 'builtin_delete' to 'std_default_delete', it is recommended to update comments and any relevant documentation to ensure consistency and clarity for future maintainers.
guarded_delete make_guarded_std_default_delete(bool armed_flag) {
smart_holder functionality (for platforms like macOS).smart_holder functionality (for platforms like macOS).
Description
Follow-on to PR #5700, specifically this comment.
This PR addresses fragile RTTI-based operations across dynamic shared object (DSO) boundaries, which commonly cause failures on macOS (due to libc++'s RTTI implementation). This is achieved by capturing required RTTI-dependent behavior in the DSO that defines the type, and using only function pointers elsewhere.
The three main changes:
Cross-DSO-safe
get_guarded_deleteThe
pybind11::detail::type_infostruct now stores a function pointer (get_guarded_delete) for retrieving theguarded_deletefrom ashared_ptr<void>.This avoids direct use of
std::get_deleter<T>across DSOs, where RTTI comparisons may fail.Cross-DSO-safe
get_trampoline_self_life_supportSimilarly,
pybind11::detail::type_infonow includes aget_trampoline_self_life_supportfunction pointer.The lambda is defined in the same DSO that instantiates
class_, where the alias type (aka trampoline) is known and the cast is valid.This replaces
dynamic_cast<trampoline_self_life_support *>(...)with a safer, cross-DSO-compatible mechanism.Resilient handling of
std::unique_ptrdeleters instruct smart_holderRTTI comparisons are made tolerant to DSO boundaries by comparing
typeid(...).name()as a fallback.The boolean
vptr_is_using_std_default_deletetracks whetherstd::default_delete<T>(or itsconstvariant) was used.This allows correct ownership transfer and deletion logic even when RTTI identity fails across DSOs.
Other details:
This PR removes
PYBIND11_EXPORT_GUARDED_DELETEthat was introduced as a stop-gap with PR fix: expose required symbol using clang #5700.PYBIND11_INTERNALS_VERSIONwas bumped from10to11to reflect these ABI-relevant changes.All uses of
std::get_deleter<guarded_delete>have been replaced bytype_info->get_memory_guarded_delete(...).Unit tests in
smart_holder_poc_test.cppand elsewhere were updated to pass inget_guarded_deleteexplicitly where required.These changes make trampoline support and smart_holder-based ownership transfer robust on macOS and other environments with strict DSO isolation.
See also: claude.ai full review
For completeness: Extremely long ChatGPT conversation related to the work on this PR.
Suggested changelog entry:
trampoline_self_life_supportfunctionality,smart_holderdeleter detection, and othersmart_holderbookkeeping. Resolves platform-specific issues on macOS related to cross-DSOdynamic_castandtypeidmismatches.