asio
asio copied to clipboard
Shared resources crash on MacOS caused by addons using different ASIO versions
I've been having some crash issues on MacOS when software plugins that link their own ASIO are installed. Basically, one plugin might statically link 1.12.1, while another will link 1.21.0. When one opens a socket, it's fine. But when the second plugin opens a socket, we get a SIGSEGV in the kqueue_reactor code. This only happens when there's an ASIO version mismatch between the two plugins. Keep in mind, these plugins do not share resources from our end. websocketpp and asio are both statically linked.
Here's a stacktrace:
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 obs-websocket.so 0x000000013afbfda2 asio::detail::kqueue_reactor::descriptor_state* asio::detail::object_pool<asio::detail::kqueue_reactor::descriptor_state>::alloc<bool>(bool) + 12 (object_pool.hpp:111) [inlined]
1 obs-websocket.so 0x000000013afbfda2 asio::detail::kqueue_reactor::allocate_descriptor_state() + 50 (kqueue_reactor.ipp:527)
2 obs-websocket.so 0x000000013afbfb1f asio::detail::kqueue_reactor::register_descriptor(int, asio::detail::kqueue_reactor::descriptor_state*&) + 5 (kqueue_reactor.ipp:144) [inlined]
3 obs-websocket.so 0x000000013afbfb1f asio::detail::reactive_socket_service_base::do_open(asio::detail::reactive_socket_service_base::base_implementation_type&, int, int, int, std::__1::error_code&) + 111 (reactive_socket_service_base.ipp:185)
4 obs-websocket.so 0x000000013afbf83f asio::detail::reactive_socket_service<asio::ip::tcp>::open(asio::detail::reactive_socket_service<asio::ip::tcp>::implementation_type&, asio::ip::tcp const&, std::__1::error_code&) + 33 (reactive_socket_service.hpp:127) [inlined]
5 obs-websocket.so 0x000000013afbf83f asio::basic_socket_acceptor<asio::ip::tcp, asio::execution::any_executor<asio::execution::context_as_t<asio::execution_context&>, asio::execution::detail::blocking::never_t<0>, asio::execution::prefer_only<asio::execution::detail::blocking::possibly_t<0> >, asio::execution::prefer_only<asio::execution::detail::outstanding_work::tracked_t<0> >, asio::execution::prefer_only<asio::execution::detail::outstanding_work::untracked_t<0> >, asio::execution::prefer_only<asio::execution::detail::relationship::fork_t<0> >, asio::execution::prefer_only<asio::execution::detail::relationship::continuation_t<0> > > >::open(asio::ip::tcp const&, std::__1::error_code&) + 36 (basic_socket_acceptor.hpp:490) [inlined]
6 obs-websocket.so 0x000000013afbf83f websocketpp::transport::asio::endpoint<websocketpp::config::asio::transport_config>::listen(asio::ip::basic_endpoint<asio::ip::tcp> const&, std::__1::error_code&) + 143 (endpoint.hpp:426)
7 obs-websocket.so 0x000000013afb63c7 void websocketpp::transport::asio::endpoint<websocketpp::config::asio::transport_config>::listen<asio::ip::tcp>(asio::ip::tcp const&, unsigned short, std::__1::error_code&) + 46 (endpoint.hpp:485) [inlined]
8 obs-websocket.so 0x000000013afb63c7 WebSocketServer::Start() + 599 (WebSocketServer.cpp:132)
9 obs-websocket.so 0x000000013afb5b00 WebSocketServer::onObsLoaded() + 64 (WebSocketServer.cpp:232)
10 com.obsproject.obs-studio 0x0000000107c5652f OBSStudioAPI::on_event(obs_frontend_event) + 111
11 com.obsproject.obs-studio 0x0000000107c6e82c OBSBasic::OBSInit() + 5068
12 com.obsproject.obs-studio 0x0000000107c47669 OBSApp::OBSInit() + 681
13 com.obsproject.obs-studio 0x0000000107c4b9e8 main + 5176
14 libdyld.dylib 0x00007fff203f8f3d start + 1
This appears to be possibly related to #641
This is keeping multiple independent plugin developers for OBS from updating their ASIO versions, because updating one plugin means all other plugins using ASIO must update to the same exact version.
Any news on this issue? I think I have a similar problem with my AU plugin on MacOS when running it inside Ableton Live 10 With latest version of asio (asio-1-22-1 ) I get a crash but with a previous version I don't.
I've taken some time today to dive into this and while I haven't been able to find the exact root cause, I did gain some insights:
- When
asio
is added as a header-only library into multiple dynamic libraries which are loaded at runtime (i.e. viadlopen
), duplicate symbols will be resolved in the global address space of the binary loading the library - In the case of
obs-studio
this means that whichever dynamic library (a "plugin" in that case) is loaded first gets to define the symbol/function pointer - Whichever dynamic library is loaded later will have its duplicate function pointer relocated to the identical function that was already resolved by the linker
- Only functions decorated with
ASIO_DECL
are truly "private" because they are inlined
In the specific case of the "advanced scene switcher" plugin, the core (but not root) issue is that when a "scheduler" service is needed, the execution context's service registry already has a scheduler service (its type_info
is correctly set), but it is not properly initialised.
So when a kqueue_generator
is initialised (which requires a scheduler as part of its initialiser), it crashes when it attempts to call the empty/non-existent get_task
callback.
I haven't dug into why there is a wrong/badly initialised scheduler already present in the service registry, but given that any call to a templated function from a consecutive dynamic library will end up in the address space of the first dynamic library that has asio
included might always lead to issues on version mismatches.
[!NOTE] Possibly fixed in the project via https://github.com/obsproject/obs-studio/pull/9623. Implementing
asio
as a header-only library in dynamic libraries and loading both at runtime indirectly violates C++' One-Definition-Rule, though I dunno if decorating more functions withASIO_DECL
to inline them might also fix this issue (as inline functions seem to be moved into theTEXT
section of the library).