boostorg/beast

Calling the destructor of a websocket stream throws system error 22 (EINVAL)

MeanSquaredError opened this issue · 2 comments

Hello,
I have a custom STOMP implementation that uses an underlying stream of type

boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::tcp_stream>>

After connecting the STOMP implementation reads from/writes to the websocket and then closes the STOMP connection by sending the STOMP command DISCONNECT. Then the STOMP implementation reads from the socket waiting for the remote command RECEIPT. However it seems that the remote side just closes the websocket without sending the RECEIPT, so at one point the websocket read throws a boost system error with error code boost::beast::websocket::error::closed

This is a GDB print of the exception is as follows

(gdb) p e
$5 = (const boost::system::system_error &) @0x7fffffffd500: {<std::runtime_error> = {<std::exception> = {_vptr.exception = 0x6c69b8 <vtable for boost::system::system_error+16>}, 
    _M_msg = "The WebSocket stream was gracefully closed at both endpoints [boost.beast.websocket:1 at /usr/include/boost/beast/websocket/impl/stream.hpp:365:9 in function 'void boost::beast::websocket::stream< <te"...}, code_ = {{
      d1_ = {val_ = 1, cat_ = 0x80f400 <boost::beast::websocket::make_error_code(boost::beast::websocket::error)::cat>}, d2_ = "\001\000\000\000\377\177\000\000\000\364\200\000\000\000\000"}, lc_flags_ = 7379585}}

After that point attempt to call

close (boost::beast::websocket::close_code::normal)

on the websocket throw boost::system::errc::operation_canceled

Regardless of whether close has been called on the socket, any following attempt to call the destructor of the websocket cause it to throw boost::system::errc::invalid_argument

This is the relevant part of the GDB backtrace

(gdb) bt
#0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
#1  0x00007ffff72a8513 in __pthread_kill_internal (threadid=<optimized out>, signo=6) at pthread_kill.c:78
#2  0x00007ffff724fc4e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#3  0x00007ffff7237902 in __GI_abort () at abort.c:79
#4  0x00007ffff74a5da9 in __gnu_cxx::__verbose_terminate_handler () at ../../../../libstdc++-v3/libsupc++/vterminate.cc:95
#5  0x00007ffff74b7c4c in __cxxabiv1::__terminate (handler=<optimized out>) at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:48
#6  0x00007ffff74a5849 in __cxa_call_terminate (ue_header_in=0xa37c70) at ../../../../libstdc++-v3/libsupc++/eh_call.cc:56
#7  0x00007ffff74b74b0 in __cxxabiv1::__gxx_personality_v0 (version=<optimized out>, actions=6, exception_class=5138137972254386944, ue_header=0xa37c70, context=0x7fffffffd150)
    at ../../../../libstdc++-v3/libsupc++/eh_personality.cc:692
#8  0x00007ffff7cf4b59 in _Unwind_RaiseException_Phase2 (exc=exc@entry=0xa37c70, context=context@entry=0x7fffffffd150, frames_p=frames_p@entry=0x7fffffffd240) at ../../../libgcc/unwind.inc:64
#9  0x00007ffff7cf5231 in _Unwind_RaiseException (exc=0xa37c70) at ../../../libgcc/unwind.inc:136
#10 0x00007ffff74b7ecb in __cxxabiv1::__cxa_throw (obj=<optimized out>, tinfo=0x7ffff7655378 <typeinfo for std::system_error>, dest=0x7ffff74e7270 <std::system_error::~system_error()>)
    at ../../../../libstdc++-v3/libsupc++/eh_throw.cc:93
#11 0x00007ffff74a9319 in std::__throw_system_error (__i=22) at /usr/src/debug/gcc-14.1.1-7.fc40.x86_64/obj-x86_64-redhat-linux/x86_64-redhat-linux/libstdc++-v3/include/system_error:233
#12 0x0000000000441d39 in std::mutex::lock (this=0xa35e08) at /usr/include/c++/14/bits/std_mutex.h:117
#13 0x000000000044869a in std::lock_guard<std::mutex>::lock_guard (this=0x7fffffffd460, __m=...) at /usr/include/c++/14/bits/std_mutex.h:250
#14 0x000000000048b1d6 in boost::beast::websocket::detail::service::impl_type::remove (this=0xa38680) at /usr/include/boost/beast/websocket/detail/service.ipp:35
#15 0x000000000048c2c2 in boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::any_io_executor, boost::beast::unlimited_rate_policy> >, true>::~stream (this=0xa37900, 
    __in_chrg=<optimized out>) at /usr/include/boost/beast/websocket/impl/stream.hpp:47
#16 0x000000000048c01e in std::default_delete<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::any_io_executor, boost::beast::unlimited_rate_policy> >, true> >::operator() (this=0x824138, __ptr=0xa37900) at /usr/include/c++/14/bits/unique_ptr.h:93
#17 0x00000000005b9348 in std::__uniq_ptr_impl<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::any_io_executor, boost::beast::unlimited_rate_policy> >, true>, std::default_delete<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::any_io_executor, boost::beast::unlimited_rate_policy> >, true> > >::reset (this=0x824138, __p=0x0)
    at /usr/include/c++/14/bits/unique_ptr.h:205
#18 0x00000000005b9be5 in std::unique_ptr<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::any_io_executor, boost::beast::unlimited_rate_policy> >, true>, std::default_delete<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::any_io_executor, boost::beast::unlimited_rate_policy> >, true> > >::reset (this=0x824138, __p=0x0)
    at /usr/include/c++/14/bits/unique_ptr.h:503
#19 0x00000000005b74df in std::unique_ptr<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::any_io_executor, boost::beast::unlimited_rate_policy> >, true>, std::default_delete<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::any_io_executor, boost::beast::unlimited_rate_policy> >, true> > >::operator=(decltype(nullptr)) (
    this=0x824138) at /usr/include/c++/14/bits/unique_ptr.h:436

It seems that the exception is thrown when the websocket tries to use an invalid mutex, one which is already destroyed or never initialized.

My GCC version

root@fedora:~# g++ --version
g++ (GCC) 14.1.1 20240701 (Red Hat 14.1.1-7)
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

My Boost version is 1.83

root@fedora:~# rpm -q boost-devel
boost-devel-1.83.0-5.fc40.x86_64

Could you provide a minimal reproducible example?
This looks like a concurrency bug. Are you using a multi-threaded executor? Are you passing a concurrency hint to the io_context constructor?

Everything is single-threaded. I decided to write my own implementation of websocket, so I close the bugreport.