chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.85k stars 1.2k forks source link

heap-use-after-free in boost::asio::buffer() by address sanitizer #1229

Closed lijh8 closed 1 year ago

lijh8 commented 1 year ago

I turn on address sanitizer on debug and it reports heap-use-after-free on boost::asio::buffer() on server side.

But I do not see increasing memory usage in server side in top command when I turn off address sanitizer. Do not know if it is false report.

boost::asio::async_write(socket,
    boost::asio::buffer(msg + (std::to_string(cnt++) + "\n").c_str()),
    [this, self](boost::system::error_code ec, std::size_t /*length*/)
    {
    if (!ec) {
        handle_write(ec);
    } else {
        std::cout << ec.message() << "\n";
    }
    });
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                                    
    595 Debian-+  20   0   15.1m   8.8m   7.3m S   0.0   0.2   0:00.11 /lib/systemd/systemd --user                                                                
   1021 ljh       20   0   15.1m   8.9m   7.4m S   0.0   0.2   0:00.17 /lib/systemd/systemd --user                                                                
   1417 ljh       20   0    5.7m   1.5m   1.4m R  48.5   0.0   6:32.56 ./build/server 9995                                                                        
   1449 ljh       20   0    5.7m   1.5m   1.4m S  14.5   0.0   2:20.46 ./build/client 192.168.1.4 9995 bbb                                                        
   1419 ljh       20   0    5.7m   1.5m   1.4m S  17.5   0.0   3:28.82 ./build/client 192.168.1.4 9995 aaa                                                        
   1050 ljh       20   0   15.8m   5.1m   3.4m S   0.0   0.1   0:00.08 -bash                                                                                      
   1257 ljh       20   0   15.8m   5.2m   3.5m S   0.0   0.1   0:00.03 -bash                     

// server.cpp

// ./boost_1_81_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp

#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
#include <sanitizer/lsan_interface.h>

using boost::asio::ip::tcp;

void handlerCont(int signum){
  if (signum == SIGCONT) {
    printf("Got SIGCONT\n");
  }
#ifndef NDEBUG
  __lsan_do_recoverable_leak_check();
#endif
}

struct session
  : public std::enable_shared_from_this<session>
{
  session(tcp::socket socket)
    : socket(std::move(socket)) { }

  void start() {
    start_read();
    start_write();
  }

  void start_read() {
    auto self(shared_from_this());
    memset(data, 0, sizeof(data));
    socket.async_read_some(boost::asio::buffer(data, max_length),
      [this, self](boost::system::error_code ec, std::size_t length) {
        if (!ec) {
          handle_read(ec, length);
        } else {
          std::cout << ec.message() << "\n";
        }
      });
  }

  void handle_read(const boost::system::error_code& error,
    std::size_t n) {
    if (!error) {
      std::cout << data;
      start_read();
    } else {
      std::cout << error.message() << "\n";
    }
  }

  void start_write() {
    auto self(shared_from_this());
    boost::asio::async_write(socket,
      boost::asio::buffer(msg + (std::to_string(cnt++) + "\n").c_str()),
      [this, self](boost::system::error_code ec, std::size_t /*length*/)
      {
        if (!ec) {
          handle_write(ec);
        } else {
          std::cout << ec.message() << "\n";
        }
      });
  }

  void handle_write(const boost::system::error_code& error) {
    if (!error) {
      // sleep(1); //test
      start_write();
    } else {
      std::cout << error.message() << "\n";
    }
  }

  tcp::socket socket;
  enum { max_length = 1024 };
  char data[max_length];
  std::string msg = "hello client ";
  size_t cnt = 0;
};

struct server {
  server(boost::asio::io_context& io_context, short port)
    : acceptor(io_context, tcp::endpoint(tcp::v4(), port))
  {
    std::cout << "listen on port: " << port << " \n";
    do_accept();
  }

  void do_accept() {
    acceptor.async_accept(
      [this](boost::system::error_code ec, tcp::socket socket) {
        if (!ec) {
          std::cout << "accept connection: "
            << socket.remote_endpoint() << "\n";
          std::make_shared<session>(std::move(socket))->start();
        } else {
          std::cout << ec.message() << ", " <<
            socket.remote_endpoint() << "\n";
        }
        do_accept();
      });
  }

  tcp::acceptor acceptor;
};

int main(int argc, char* argv[]) {
  if (argc != 2) {
    std::cerr << "Usage: server <port>\n";
    return 1;
  }

  signal(SIGCONT, handlerCont); // $ man 7 signal
  boost::asio::io_context io_context;
  server s(io_context, std::atoi(argv[1]));
  io_context.run();

  return 0;
}

//client.cpp

// ./boost_1_81_0/doc/html/boost_asio/example/cpp11/timeouts/async_tcp_client.cpp

#include <boost/asio/buffer.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/write.hpp>
#include <functional>
#include <iostream>
#include <string>
#include <sanitizer/lsan_interface.h>

using boost::asio::steady_timer;
using boost::asio::ip::tcp;
using std::placeholders::_1;
using std::placeholders::_2;

void handlerCont(int signum){
  if (signum == SIGCONT) {
    printf("Got SIGCONT\n");
  }
#ifndef NDEBUG
  __lsan_do_recoverable_leak_check();
#endif
}

struct client {
  client(boost::asio::io_context& io_context)
    : socket(io_context) { }

  void start(tcp::resolver::results_type endpoints,
    const std::string& msg)
  {
    endpoints_ = endpoints;
    msg_ = msg;
    start_connect(endpoints_.begin());
  }

  void start_connect(tcp::resolver::results_type::iterator
    endpoint_iter)
  {
    if (endpoint_iter != endpoints_.end()) {
      socket.async_connect(endpoint_iter->endpoint(),
        std::bind(&client::handle_connect,
          this, _1, endpoint_iter));
    }
  }

  void handle_connect(const boost::system::error_code& error,
      tcp::resolver::results_type::iterator endpoint_iter)
  {
    if (!socket.is_open()) {
      std::cout << "Connect timed out\n";
      start_connect(++endpoint_iter);
    } else if (error) {
      std::cout << "Connect error: " << error.message() << "\n";
      socket.close();
      start_connect(++endpoint_iter);
    } else {
      std::cout << "Connected to " << endpoint_iter->endpoint() << "\n";

      //do not have to write before read
      start_write();
      start_read();
    }
  }

  void start_write() {
    boost::asio::async_write(socket,
      boost::asio::buffer("hello server " + msg_ + " " +
        std::to_string(cnt++) + "\n"),
      std::bind(&client::handle_write, this, _1));
  }

  void handle_write(const boost::system::error_code& error) {
    if (!error) {
      // sleep(1); //test
      start_write();
    } else {
      std::cout << error.message() << "\n";
    }
  }

  void start_read() {
    boost::asio::async_read_until(socket,
      boost::asio::dynamic_buffer(input_buffer), '\n',
      std::bind(&client::handle_read, this, _1, _2));
  }

  void handle_read(const boost::system::error_code& error,
    std::size_t n)
  {
    if (!error) {
      std::string line(input_buffer.substr(0, n - 1));
      input_buffer.erase(0, n);

      if (!line.empty()) {
        std::cout << line << "\n";
      }

      start_read();
    } else {
      std::cout << error.message() << "\n";
    }
  }

  tcp::resolver::results_type endpoints_;
  tcp::socket socket;
  std::string input_buffer;
  std::string msg_;
  size_t cnt = 0;
};

int main(int argc, char* argv[]) {
  if (argc != 4) {
    std::cerr << "Usage: client <host> <port> <msg>\n";
    return 1;
  }

  signal(SIGCONT, handlerCont); // $ man 7 signal
  boost::asio::io_context io_context;
  tcp::resolver r(io_context);
  client c(io_context);
  c.start(r.resolve(argv[1], argv[2]), argv[3]);
  io_context.run();

  return 0;
}

=================================================================
==1271==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000fa3a80 at pc 0x7eff5cc8a1ff bp 0x7fffe5826d70 sp 0x7fffe5826520
READ of size 20 at 0x603000fa3a80 thread T0
    #0 0x7eff5cc8a1fe in __interceptor_send ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:6439
    #1 0x55a4f9e1024f in boost::asio::detail::socket_ops::send1(int, void const*, unsigned long, int, boost::system::error_code&) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/impl/socket_ops.ipp:1426
    #2 0x55a4f9e1037a in boost::asio::detail::socket_ops::non_blocking_send1(int, void const*, unsigned long, int, boost::system::error_code&, unsigned long&) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/impl/socket_ops.ipp:1569
    #3 0x55a4f9e382f8 in boost::asio::detail::reactive_socket_send_op_base<boost::asio::const_buffers_1>::do_perform(boost::asio::detail::reactor_op*) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/reactive_socket_send_op.hpp:63
    #4 0x55a4f9e05709 in boost::asio::detail::reactor_op::perform() /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/reactor_op.hpp:47
    #5 0x55a4f9e0b52a in boost::asio::detail::epoll_reactor::descriptor_state::perform_io(unsigned int) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/impl/epoll_reactor.ipp:773
    #6 0x55a4f9e0b756 in boost::asio::detail::epoll_reactor::descriptor_state::do_complete(void*, boost::asio::detail::scheduler_operation*, boost::system::error_code const&, unsigned long) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/impl/epoll_reactor.ipp:804
    #7 0x55a4f9e030ea in boost::asio::detail::scheduler_operation::complete(void*, boost::system::error_code const&, unsigned long) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/scheduler_operation.hpp:40
    #8 0x55a4f9e0de08 in boost::asio::detail::scheduler::do_run_one(boost::asio::detail::conditionally_enabled_mutex::scoped_lock&, boost::asio::detail::scheduler_thread_info&, boost::system::error_code const&) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/impl/scheduler.ipp:492
    #9 0x55a4f9e0d0d4 in boost::asio::detail::scheduler::run(boost::system::error_code&) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/impl/scheduler.ipp:210
    #10 0x55a4f9e0e800 in boost::asio::io_context::run() /home/ljh/Downloads/boost_1_81_0/boost/asio/impl/io_context.ipp:63
    #11 0x55a4f9dfac21 in main /home/ljh/Documents/hello_asio/src/server.cpp:120
    #12 0x7eff5c87bd09 in __libc_start_main ../csu/libc-start.c:308
    #13 0x55a4f9dfa9d9 in _start (/home/ljh/Documents/hello_asio/src/build/server+0xa9d9)

0x603000fa3a80 is located 0 bytes inside of 31-byte region [0x603000fa3a80,0x603000fa3a9f)
freed by thread T0 here:
    #0 0x7eff5cce2017 in operator delete(void*) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:160
    #1 0x55a4f9e1738a in session::start_write() /home/ljh/Documents/hello_asio/src/server.cpp:58
    #2 0x55a4f9e17680 in session::handle_write(boost::system::error_code const&) /home/ljh/Documents/hello_asio/src/server.cpp:72
    #3 0x55a4f9e16e6a in session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}::operator()(boost::system::error_code, unsigned long) const /home/ljh/Documents/hello_asio/src/server.cpp:62
    #4 0x55a4f9e27058 in boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>::operator()(boost::system::error_code, unsigned long, int) /home/ljh/Downloads/boost_1_81_0/boost/asio/impl/write.hpp:362
    #5 0x55a4f9e3a417 in boost::asio::detail::binder2<boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::system::error_code, unsigned long>::operator()() /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/bind_handler.hpp:289
    #6 0x55a4f9e39eaf in void boost::asio::asio_handler_invoke<boost::asio::detail::binder2<boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::system::error_code, unsigned long> >(boost::asio::detail::binder2<boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::system::error_code, unsigned long>&, ...) /home/ljh/Downloads/boost_1_81_0/boost/asio/handler_invoke_hook.hpp:88
    #7 0x55a4f9e399e1 in void boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::binder2<boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::system::error_code, unsigned long>, {lambda(boost::system::error_code, unsigned long)#1}>(boost::asio::detail::binder2<boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::system::error_code, unsigned long>&, {lambda(boost::system::error_code, unsigned long)#1}&) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/handler_invoke_helpers.hpp:54
    #8 0x55a4f9e397ea in void boost::asio::detail::asio_handler_invoke<boost::asio::detail::binder2<boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::system::error_code, unsigned long>, boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, {lambda(boost::system::error_code, unsigned long)#1}>(boost::asio::detail::binder2<boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::system::error_code, unsigned long>&, boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, {lambda(boost::system::error_code, unsigned long)#1}>*) /home/ljh/Downloads/boost_1_81_0/boost/asio/impl/write.hpp:430
    #9 0x55a4f9e39053 in void boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::binder2<boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::system::error_code, unsigned long>, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>(boost::asio::detail::binder2<boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::system::error_code, unsigned long>&, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}&) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/handler_invoke_helpers.hpp:54
    #10 0x55a4f9e380b3 in void boost::asio::detail::handler_work<boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::asio::any_io_executor, void>::complete<boost::asio::detail::binder2<session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}, boost::system::error_code, unsigned long> >(boost::asio::detail::binder2<session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}, boost::system::error_code, unsigned long>&, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}&) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/handler_work.hpp:520
    #11 0x55a4f9e36209 in boost::asio::detail::reactive_socket_send_op<boost::asio::const_buffers_1, boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::const_buffers_1, boost::asio::const_buffer const*, boost::asio::detail::transfer_all_t, session::start_write()::{lambda(boost::system::error_code, unsigned long)#1}>, boost::asio::any_io_executor>::do_complete(void*, boost::asio::detail::scheduler_operation*, boost::system::error_code const&, unsigned long) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/reactive_socket_send_op.hpp:150
    #12 0x55a4f9e030ea in boost::asio::detail::scheduler_operation::complete(void*, boost::system::error_code const&, unsigned long) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/scheduler_operation.hpp:40
    #13 0x55a4f9e0de08 in boost::asio::detail::scheduler::do_run_one(boost::asio::detail::conditionally_enabled_mutex::scoped_lock&, boost::asio::detail::scheduler_thread_info&, boost::system::error_code const&) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/impl/scheduler.ipp:492
    #14 0x55a4f9e0d0d4 in boost::asio::detail::scheduler::run(boost::system::error_code&) /home/ljh/Downloads/boost_1_81_0/boost/asio/detail/impl/scheduler.ipp:210
    #15 0x55a4f9e0e800 in boost::asio::io_context::run() /home/ljh/Downloads/boost_1_81_0/boost/asio/impl/io_context.ipp:63
    #16 0x55a4f9dfac21 in main /home/ljh/Documents/hello_asio/src/server.cpp:120
    #17 0x7eff5c87bd09 in __libc_start_main ../csu/libc-start.c:308

previously allocated by thread T0 here:
    #0 0x7eff5cce1647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x7eff5cb7a859 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) (/lib/x86_64-linux-gnu/libstdc++.so.6+0x133859)

SUMMARY: AddressSanitizer: heap-use-after-free ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:6439 in __interceptor_send
lijh8 commented 1 year ago

sorry, i used the buffer incorrectly