@@ -697,17 +697,29 @@ class internals_pp_manager {
697697 // this could be called without an active interpreter, just use what was cached
698698 if (!tstate || tstate->interp == last_istate_tls ()) {
699699 auto tpp = internals_p_tls ();
700-
700+ pps_have_created_content_. erase (tpp);
701701 delete tpp;
702702 }
703703 unref ();
704704 return ;
705705 }
706706#endif
707+ pps_have_created_content_.erase (internals_singleton_pp_);
707708 delete internals_singleton_pp_;
708709 unref ();
709710 }
710711
712+ static void fail_if_internals_recreated (std::unique_ptr<InternalsType> *pp) {
713+ // Prevent re-creation of internals after destruction during interpreter shutdown.
714+ // If pybind11 code (e.g., tp_traverse/tp_clear calling py::cast) runs after internals
715+ // have been destroyed, a new empty internals would be created, causing type lookup
716+ // failures. See https://github.com/pybind/pybind11/pull/5958#discussion_r2717645230.
717+ if (pps_have_created_content_.find (pp) != pps_have_created_content_.end ()) {
718+ pybind11_fail (" Reentrant call detected while fetching pybind11 internals!" );
719+ }
720+ pps_have_created_content_.insert (pp);
721+ }
722+
711723private:
712724 internals_pp_manager (char const *id, on_fetch_function *on_fetch)
713725 : holder_id_(id), on_fetch_(on_fetch) {}
@@ -748,6 +760,9 @@ class internals_pp_manager {
748760 // Pointer-to-pointer to the singleton internals for the first seen interpreter (may not be the
749761 // main interpreter)
750762 std::unique_ptr<InternalsType> *internals_singleton_pp_ = nullptr ;
763+
764+ // Tracks pointer-to-pointers whose internals have been created, to detect re-entrancy.
765+ inline static std::unordered_set<void *> pps_have_created_content_{};
751766};
752767
753768// If We loaded the internals through `state_dict`, our `error_already_set`
@@ -788,6 +803,7 @@ PYBIND11_NOINLINE internals &get_internals() {
788803 // Slow path, something needs fetched from the state dict or created
789804 gil_scoped_acquire_simple gil;
790805 error_scope err_scope;
806+ internals_pp_manager<internals>::fail_if_internals_recreated (&internals_ptr);
791807 internals_ptr.reset (new internals ());
792808
793809 if (!internals_ptr->instance_base ) {
@@ -847,6 +863,7 @@ inline local_internals &get_local_internals() {
847863 auto &internals_ptr = *ppmgr.get_pp ();
848864 if (!internals_ptr) {
849865 gil_scoped_acquire_simple gil;
866+ internals_pp_manager<local_internals>::fail_if_internals_recreated (&internals_ptr);
850867 internals_ptr.reset (new local_internals ());
851868 }
852869 return *internals_ptr;
0 commit comments