Kevin Yin
Kevin Yin

Reputation: 21

C++ boost beast websocket crash after running a while

I am using boost beast to connect to a server's websocket.

I only rewrite the on_read function in order to make the websocket never disconnect, the other parts are just copied from beast example code.

Here is my code:

void
on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type ep){

    beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(10));

    if(! SSL_set_tlsext_host_name(
            ws_.next_layer().native_handle(),
            host_.c_str())){
        ec = beast::error_code(static_cast<int>(::ERR_get_error()),
            net::error::get_ssl_category());
        return fail(ec, "connect");
    }

    ws_.next_layer().async_handshake(
        ssl::stream_base::client,
        beast::bind_front_handler(
            &session::on_ssl_handshake,
            shared_from_this()));
}

void
on_ssl_handshake(beast::error_code ec){

    beast::get_lowest_layer(ws_).expires_never();

    ws_.set_option(
        websocket::stream_base::timeout::suggested(
            beast::role_type::client));

    ws_.set_option(websocket::stream_base::decorator(
        [](websocket::request_type& req){
            req.set(http::field::user_agent,
                std::string(BOOST_BEAST_VERSION_STRING) +
                    " websocket-client-async-ssl");
        }));

    ws_.async_handshake(host_, "/mix/v1/stream",
       beast::bind_front_handler(
           &session::on_handshake,
            shared_from_this()));
}

void
on_handshake(beast::error_code ec){

    // Send the message
    ws_.async_write(
        net::buffer("{\"op\": \"subscribe\",\"args\": [{\"instType\":\"mc\",\"channel\":\"books1\",\"instId\":\"BTCUSDT\"}]}"),
        beast::bind_front_handler(
            &session::on_write,
            shared_from_this()));
}

void
on_write(
    beast::error_code ec,
    std::size_t bytes_transferred){

    boost::ignore_unused(bytes_transferred);

    ws_.async_read(
        buffer_,
        beast::bind_front_handler(
            &session::on_read,
            shared_from_this()));
}

void
on_read(
    beast::error_code ec,
    std::size_t bytes_transferred){
    boost::ignore_unused(bytes_transferred);

    if(ec)
       return fail(ec, "read");

    auto j = json::parse(beast::buffers_to_string(buffer_.data()));
    if (j.contains(std::string{ "data" })){
        asks = std::stof(std::string(j["data"][0]["asks"][0][0]));
        bids = std::stof(std::string(j["data"][0]["bids"][0][0]));
    }
    buffer_.clear();
                        
    ws_.async_read(
       buffer_,
       beast::bind_front_handler(
           &session::on_read,
           shared_from_this()));
}

void
on_close(beast::error_code ec){
    if(ec)
        return fail(ec, "close");
    std::cout << beast::make_printable(buffer_.data()) << std::endl;
}

/*************************************************************************************************/

int main(int argc, char **argv) {
    
    net::io_context ioc;

    ssl::context ctx{ssl::context::tlsv12_client};

    ctx.set_verify_mode(ssl::verify_peer);
    ctx.set_default_verify_paths();

    auto const host = "ws.bitget.com";
    auto const port = "443";

    std::make_shared<session>(ioc, ctx)->run(host, port, "");

    ioc.run();
    return 0;
}

The websocket connect successfully and can get the correct data from the server. But it can only run for few seconds, the following error will occur:

read: stream truncated
==12771==ERROR: AddressSanitizer: heap-use-after-free on address 0x000109e02da0 at pc 0x00010302a8e4 bp 0x00016d78d2f0 sp 0x00016d78d2e8
READ of size 8 at 0x000109e02da0 thread T0
    #0 0x10302a8e0 in boost::intrusive::rbtree_node<void*>* boost::intrusive::bstree_algorithms<boost::intrusive::rbtree_node_traits<void*, false> >::find<void const*, boost::intrusive::detail::key_nodeptr_comp<std::__1::less<void const*>, boost::intrusive::mhtraits<binapi::ws::websocket, boost::intrusive::set_member_hook<>, &(binapi::ws::websocket::m_intrusive_set_hook)>, binapi::ws::websocket_id_getter> >(boost::intrusive::rbtree_node<void*> const*, void const* const&, boost::intrusive::detail::key_nodeptr_comp<std::__1::less<void const*>, boost::intrusive::mhtraits<binapi::ws::websocket, boost::intrusive::set_member_hook<>, &(binapi::ws::websocket::m_intrusive_set_hook)>, binapi::ws::websocket_id_getter>) bstree_algorithms.hpp:759
    #1 0x10302a42c in boost::intrusive::tree_iterator<boost::intrusive::mhtraits<binapi::ws::websocket, boost::intrusive::set_member_hook<>, &(binapi::ws::websocket::m_intrusive_set_hook)>, false> boost::intrusive::bstbase2<boost::intrusive::mhtraits<binapi::ws::websocket, boost::intrusive::set_member_hook<>, &(binapi::ws::websocket::m_intrusive_set_hook)>, binapi::ws::websocket_id_getter, void, (boost::intrusive::algo_types)5, void>::find<void const*, std::__1::less<void const*> >(void const* const&, std::__1::less<void const*>) bstree.hpp:383
    #2 0x1032c4260 in void* binapi::ws::websockets::impl::start_channel<std::__1::function<bool (char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, binapi::ws::book_ticker_t)> >(char const*, char const*, std::__1::function<bool (char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, binapi::ws::book_ticker_t)>)::'lambda'(binapi::ws::websocket*)::operator()(binapi::ws::websocket*) const websocket.cpp:276
    #3 0x1032c4cbc in std::__1::__shared_ptr_pointer<binapi::ws::websocket*, void* binapi::ws::websockets::impl::start_channel<std::__1::function<bool (char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, binapi::ws::book_ticker_t)> >(char const*, char const*, std::__1::function<bool (char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, binapi::ws::book_ticker_t)>)::'lambda'(binapi::ws::websocket*), std::__1::allocator<binapi::ws::websocket> >::__on_zero_shared() shared_ptr.h:267
    #4 0x1026a05ac in std::__1::__shared_count::__release_shared() shared_ptr.h:177
    #5 0x1026a04ec in std::__1::__shared_weak_count::__release_shared() shared_ptr.h:219
    #6 0x1026a04bc in std::__1::shared_ptr<boost::beast::http::detail::chunk_size::sequence>::~shared_ptr() shared_ptr.h:959
    #7 0x102677b5c in std::__1::shared_ptr<std::__1::vector<boost::asio::ip::basic_resolver_entry<boost::asio::ip::tcp>, std::__1::allocator<boost::asio::ip::basic_resolver_entry<boost::asio::ip::tcp> > > >::~shared_ptr() shared_ptr.h:957
    #8 0x103196b18 in binapi::ws::websocket::on_async_ssl_handshake(std::__1::function<bool (char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, unsigned long)>, std::__1::shared_ptr<binapi::ws::websocket>)::'lambda'(boost::system::error_code)::~() websocket.cpp:187
    #9 0x1030c8a8c in binapi::ws::websocket::async_connect(boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp>, std::__1::function<bool (char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, unsigned long)>, std::__1::shared_ptr<binapi::ws::websocket>)::'lambda'(boost::system::error_code, boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>)::~() websocket.cpp:102
    #10 0x103263850 in boost::asio::detail::binder2<binapi::ws::websocket::async_start(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<bool (char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, unsigned long)>, std::__1::shared_ptr<binapi::ws::websocket>)::'lambda'(boost::system::error_code, boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp>), boost::system::error_code, boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp> >::~binder2() bind_handler.hpp:252
    #11 0x1030cb2a8 in boost::asio::detail::binder2<binapi::ws::websocket::async_start(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<bool (char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, unsigned long)>, std::__1::shared_ptr<binapi::ws::websocket>)::'lambda'(boost::system::error_code, boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp>), boost::system::error_code, boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp> >::~binder2() bind_handler.hpp:252
    #12 0x1030cab70 in boost::asio::detail::resolve_query_op<boost::asio::ip::tcp, binapi::ws::websocket::async_start(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<bool (char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, unsigned long)>, std::__1::shared_ptr<binapi::ws::websocket>)::'lambda'(boost::system::error_code, boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp>), boost::asio::any_io_executor>::do_complete(void*, boost::asio::detail::scheduler_operation*, boost::system::error_code const&, unsigned long) resolve_query_op.hpp:134
    #13 0x102687c14 in boost::asio::detail::scheduler_operation::destroy() scheduler_operation.hpp:45
    #14 0x10268badc in boost::asio::detail::scheduler::shutdown() scheduler.ipp:176
    #15 0x102691a50 in boost::asio::detail::service_registry::shutdown_services() service_registry.ipp:44
    #16 0x102691920 in boost::asio::execution_context::shutdown() execution_context.ipp:41
    #17 0x10267b0b4 in boost::asio::execution_context::~execution_context() execution_context.ipp:34
    #18 0x102c67788 in boost::asio::io_context::~io_context() io_context.ipp:58
    #19 0x102677bb4 in boost::asio::io_context::~io_context() io_context.ipp:57
    #20 0x1026758e4 in main main.cpp:508
    #21 0x1072d5084 in start+0x200 (dyld:arm64e+0x5084)

0x000109e02da0 is located 96 bytes inside of 128-byte region [0x000109e02d40,0x000109e02dc0)
freed by thread T0 here:
    #0 0x107ad5018 in wrap__ZdlPv+0x74 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x4d018)
    #1 0x1030b3580 in std::__1::default_delete<binapi::ws::websockets::impl>::operator()(binapi::ws::websockets::impl*) const unique_ptr.h:57
    #2 0x1030b34f4 in std::__1::unique_ptr<binapi::ws::websockets::impl, std::__1::default_delete<binapi::ws::websockets::impl> >::reset(binapi::ws::websockets::impl*) unique_ptr.h:318
    #3 0x1030b3428 in std::__1::unique_ptr<binapi::ws::websockets::impl, std::__1::default_delete<binapi::ws::websockets::impl> >::~unique_ptr() unique_ptr.h:272
    #4 0x103020750 in std::__1::unique_ptr<binapi::ws::websockets::impl, std::__1::default_delete<binapi::ws::websockets::impl> >::~unique_ptr() unique_ptr.h:272
    #5 0x103020724 in binapi::ws::websockets::~websockets() websocket.cpp:408
    #6 0x10302077c in binapi::ws::websockets::~websockets() websocket.cpp:408
    #7 0x1026758cc in main main.cpp:508
    #8 0x1072d5084 in start+0x200 (dyld:arm64e+0x5084)

previously allocated by thread T0 here:
    #0 0x107ad4bd8 in wrap__Znwm+0x74 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x4cbd8)
    #1 0x103020464 in std::__1::__unique_if<binapi::ws::websockets::impl>::__unique_single std::__1::make_unique<binapi::ws::websockets::impl, boost::asio::io_context&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::function<void (char const*, char const*, unsigned long)> >(boost::asio::io_context&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&, std::__1::function<void (char const*, char const*, unsigned long)>&&) unique_ptr.h:728
    #2 0x1030202e8 in binapi::ws::websockets::websockets(boost::asio::io_context&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::function<void (char const*, char const*, unsigned long)>) websocket.cpp:404
    #3 0x1030206f8 in binapi::ws::websockets::websockets(boost::asio::io_context&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::function<void (char const*, char const*, unsigned long)>) websocket.cpp:405
    #4 0x102674fd8 in main main.cpp:326
    #5 0x1072d5084 in start+0x200 (dyld:arm64e+0x5084)

SUMMARY: AddressSanitizer: heap-use-after-free bstree_algorithms.hpp:759 in boost::intrusive::rbtree_node<void*>* boost::intrusive::bstree_algorithms<boost::intrusive::rbtree_node_traits<void*, false> >::find<void const*, boost::intrusive::detail::key_nodeptr_comp<std::__1::less<void const*>, boost::intrusive::mhtraits<binapi::ws::websocket, boost::intrusive::set_member_hook<>, &(binapi::ws::websocket::m_intrusive_set_hook)>, binapi::ws::websocket_id_getter> >(boost::intrusive::rbtree_node<void*> const*, void const* const&, boost::intrusive::detail::key_nodeptr_comp<std::__1::less<void const*>, boost::intrusive::mhtraits<binapi::ws::websocket, boost::intrusive::set_member_hook<>, &(binapi::ws::websocket::m_intrusive_set_hook)>, binapi::ws::websocket_id_getter>)
Shadow bytes around the buggy address:
  0x0070213e0560: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0070213e0570: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0070213e0580: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
  0x0070213e0590: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0070213e05a0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
=>0x0070213e05b0: fd fd fd fd[fd]fd fd fd fa fa fa fa fa fa fa fa
  0x0070213e05c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0070213e05d0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0070213e05e0: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa
  0x0070213e05f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0070213e0600: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==12771==ABORTING
zsh: abort 

I can confirm that the server is not disconnected or sending abnormal messages.

And my message buffer also clears after each read.

I've also checked the websocket code written by others, it seems to be similar to mine.

Upvotes: 0

Views: 291

Answers (0)

Related Questions