asio icon indicating copy to clipboard operation
asio copied to clipboard

unhandled exception in select_reactor thread leads to process termination

Open arvidn opened this issue 2 years ago • 6 comments

The select_reactor has a socket_select_interrupter object used to signal an interruption via. It has a long-lived socket that's added to the select() call to make it wake up on an interruption.

This long lived socket can sometimes be closed and has to be recreated. For example if the computer hibernates or if the network stack is restarted. To handle this, the interrupter sockets are recreated if reading from it fails. This is socket_select_interrupter::recreate().

Recreating this socket may fail with an exception. This exception will flow out of the call to run(), expected to be handled by the application.

The problem occurs in the select_reactor, which on windows spawns a thread. The thread calls run() and post_deferred_completions() in a loop until stop_thread_ is set. (socket_reactor::run_thread())

There is nothing catching exceptions at the top level of this thread. So when recreate() fails, the process is terminated.

An example stack trace from windows:

#  0 qbittorrent.exe      0x00007ff61e1499ec straceWin::getBacktrace()[ app\stacktrace_win.h : 220 ]
#  1 qbittorrent.exe      0x00007ff61e14ab6b sigAbnormalHandler(signum)[ app\main.cpp : 368 ]
#  2 qbittorrent.exe      0x00007ff61ef7a365 raise(signum)[ minkernel\crts\ucrt\src\appcrt\misc\signal.cpp : 541 ]
#  3 qbittorrent.exe      0x00007ff61ef86a94 abort()[ minkernel\crts\ucrt\src\appcrt\startup\abort.cpp : 64 ]
#  4 qbittorrent.exe      0x00007ff61ef7dd73 terminate()[ minkernel\crts\ucrt\src\appcrt\misc\terminate.cpp : 58 ]
#  5 qbittorrent.exe      0x00007ff61ef551b7 __scrt_unhandled_exception_filter(pointers)[ d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility_desktop.cpp : 91 ]
#  6 KERNELBASE.dll       0x00007ffb5f236391 UnhandledExceptionFilter()
#  7 ntdll.dll            0x00007ffb617aaeec memset()
#  8 ntdll.dll            0x00007ffb61793eb6 _C_specific_handler()
#  9 ntdll.dll            0x00007ffb617a8e4f _chkstk()
# 10 ntdll.dll            0x00007ffb61735e9a RtlRestoreContext()
# 11 ntdll.dll            0x00007ffb61733163 RtlRaiseException()
# 12 KERNELBASE.dll       0x00007ffb5f13474c RaiseException()
# 13 qbittorrent.exe      0x00007ff61ef73611 _CxxThrowException(pExceptionObject, pThrowInfo)[ d:\agent\_work\1\s\src\vctools\crt\vcruntime\src\eh\throw.cpp : 129 ]
# 14 qbittorrent.exe      0x00007ff61e14279b boost::throw_exception(e)[ g:\qbittorrent\boost_1_78_0\boost\throw_exception.hpp : 148 ]
# 15 qbittorrent.exe      0x00007ff61e13c8ed boost::asio::detail::do_throw_error(err, location)[ g:\qbittorrent\boost_1_78_0\boost\asio\detail\impl\throw_error.ipp : 38 ]
# 16 qbittorrent.exe      0x00007ff61e47929e boost::asio::detail::socket_select_interrupter::open_descriptors()[ g:\qbittorrent\boost_1_78_0\boost\asio\detail\impl\socket_select_interrupter.ipp : 66 ]
# 17 qbittorrent.exe      0x00007ff61e479e3e boost::asio::detail::select_reactor::run(ops, ops)[ g:\qbittorrent\boost_1_78_0\boost\asio\detail\impl\select_reactor.ipp : 266 ]
# 18 qbittorrent.exe      0x00007ff61e47a099 boost::asio::detail::select_reactor::run_thread()[ g:\qbittorrent\boost_1_78_0\boost\asio\detail\impl\select_reactor.ipp : 303 ]
# 19 qbittorrent.exe      0x00007ff61e4126cf boost::asio::detail::win_thread_function(arg)[ g:\qbittorrent\boost_1_78_0\boost\asio\detail\impl\win_thread.ipp : 119 ]
# 20 qbittorrent.exe      0x00007ff61ef864a0 thread_start(parameter, parameter)[ minkernel\crts\ucrt\src\appcrt\startup\thread.cpp : 115 ]
# 21 KERNEL32.DLL         0x00007ffb5f9154e0 BaseThreadInitThunk()
# 22 ntdll.dll            0x00007ffb6170485b RtlUserThreadStart()

related ticket: https://github.com/qbittorrent/qBittorrent/issues/17082

arvidn avatar May 23 '22 08:05 arvidn

@chriskohlhoff sorry for the ping but please take a look into this issue. A lot of qBittorrent users are suffering from crash on VPN/Network interface disconnect due to this bug.

ghost avatar Jul 19 '22 17:07 ghost

@arvidn I may have missed some, but as far as I can tell these are the only places that libtorrent uses reactor-style operations on sockets:

  • https://github.com/arvidn/libtorrent/blob/6e03a8307dd3fcef97628c310fa57103ee2b6469/src/upnp.cpp#L193
  • https://github.com/arvidn/libtorrent/blob/6e03a8307dd3fcef97628c310fa57103ee2b6469/src/upnp.cpp#L205
  • https://github.com/arvidn/libtorrent/blob/6e03a8307dd3fcef97628c310fa57103ee2b6469/src/upnp.cpp#L510
  • https://github.com/arvidn/libtorrent/blob/6e03a8307dd3fcef97628c310fa57103ee2b6469/src/lsd.cpp#L139
  • https://github.com/arvidn/libtorrent/blob/6e03a8307dd3fcef97628c310fa57103ee2b6469/src/lsd.cpp#L222

Is there a reason they cannot be changed to receive directly into a buffer and endpoint as class data members? On modern Windows these would be the only operations that trigger creation of the select_reactor.

chriskohlhoff avatar Jul 20 '22 07:07 chriskohlhoff

Commit 4709210bc94f6f1a0b76f6f5156e7b41da912c9c moves the recreate() so that the exception can escape through io_context::run().

N.B. I still recommend changing those places to use async_receive_from, to avoid instantiating the select_reactor in the first place.

chriskohlhoff avatar Aug 03 '22 07:08 chriskohlhoff

@chriskohlhoff Has this made it in to upcoming Boost 1.80?

xavier2k6 avatar Aug 03 '22 08:08 xavier2k6

It will.

chriskohlhoff avatar Aug 03 '22 09:08 chriskohlhoff

It seems there are still related crashes albeit the user says it occurs less than before:

This is using boost 1.80 with libtorrent 7bb4b410d704e13d73d0f02d85192d59dff1fb5e on RC_2_0. Note that libtorrent already include https://github.com/arvidn/libtorrent/commit/1f09a847d0a331b598b6e1f4433d32d4ec2a25c7 and https://github.com/arvidn/libtorrent/commit/d6af6159f87732bbfc80042c8b1bafa54298f468.

Taken from https://github.com/qbittorrent/qBittorrent/issues/17082#issuecomment-1230845334:

#  0 qbittorrent.exe      0x00007ff62fb59dbc straceWin::getBacktrace()[ app\stacktrace_win.h : 220 ]
#  1 qbittorrent.exe      0x00007ff62fb5af4b sigAbnormalHandler(signum)[ app\main.cpp : 367 ]
#  2 qbittorrent.exe      0x00007ff6309953f3 raise(signum)[ minkernel\crts\ucrt\src\appcrt\misc\signal.cpp : 541 ]
#  3 qbittorrent.exe      0x00007ff6309a2f58 abort()[ minkernel\crts\ucrt\src\appcrt\startup\abort.cpp : 64 ]
#  4 qbittorrent.exe      0x00007ff6309994a5 terminate()[ minkernel\crts\ucrt\src\appcrt\misc\terminate.cpp : 58 ]
#  5 qbittorrent.exe      0x00007ff6309c0a1c __C_specific_handler_noexcept(ExceptionRecord, EstablisherFrame, ContextRecord, DispatcherContext)[ d:\agent\_work\2\s\src\vctools\crt\vcruntime\src\eh\chandler_noexcept.cpp : 65 ]
#  6 ntdll.dll            0x00007ffee9a68fcf _chkstk()
#  7 ntdll.dll            0x00007ffee99f5e9a RtlRestoreContext()
#  8 ntdll.dll            0x00007ffee99f3163 RtlRaiseException()
#  9 KERNELBASE.dll       0x00007ffee746474c RaiseException()
# 10 qbittorrent.exe      0x00007ff630988ed1 _CxxThrowException(pExceptionObject, pThrowInfo)[ d:\agent\_work\2\s\src\vctools\crt\vcruntime\src\eh\throw.cpp : 129 ]
# 11 qbittorrent.exe      0x00007ff62fb52923 boost::throw_exception(e)[ g:\qbittorrent\boost_1_80_0\boost\throw_exception.hpp : 157 ]
# 12 qbittorrent.exe      0x00007ff62fb4c98d boost::asio::detail::do_throw_error(err, location)[ g:\qbittorrent\boost_1_80_0\boost\asio\detail\impl\throw_error.ipp : 42 ]
# 13 qbittorrent.exe      0x00007ff62fe88b1b boost::asio::detail::socket_select_interrupter::open_descriptors()[ g:\qbittorrent\boost_1_80_0\boost\asio\detail\impl\socket_select_interrupter.ipp : 66 ]
# 14 qbittorrent.exe      0x00007ff62fe89c06 boost::asio::detail::select_reactor::restart_reactor::do_complete(owner, base, __formal, __formal)[ g:\qbittorrent\boost_1_80_0\boost\asio\detail\impl\select_reactor.ipp : 334 ]
# 15 qbittorrent.exe      0x00007ff62fe23648 boost::asio::detail::win_iocp_io_context::do_one(this_thread, ec)[ g:\qbittorrent\boost_1_80_0\boost\asio\detail\impl\win_iocp_io_context.ipp : 473 ]
# 16 qbittorrent.exe      0x00007ff62fe22edf boost::asio::detail::win_iocp_io_context::run(ec)[ g:\qbittorrent\boost_1_80_0\boost\asio\detail\impl\win_iocp_io_context.ipp : 204 ]
# 17 qbittorrent.exe      0x00007ff62fe238bc boost::asio::io_context::run()[ g:\qbittorrent\boost_1_80_0\boost\asio\impl\io_context.ipp : 63 ]
# 18 qbittorrent.exe      0x00007ff62fe250e7 std::_LaunchPad >,std::default_delete > > > >::_Go()[ c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\thr\xthread : 230 ]
# 19 qbittorrent.exe      0x00007ff62fe21f69 std::_Pad::_Call_func(_Data)[ c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\thr\xthread : 208 ]
# 20 qbittorrent.exe      0x00007ff6309a266a thread_start(parameter, parameter)[ minkernel\crts\ucrt\src\appcrt\startup\thread.cpp : 97 ]
# 21 KERNEL32.DLL         0x00007ffee92454e0 BaseThreadInitThunk()
# 22 ntdll.dll            0x00007ffee99c485b RtlUserThreadStart()

Chocobo1 avatar Aug 30 '22 03:08 Chocobo1