chriskohlhoff / asio

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

async_read documentation does not mention that AsyncReadStream must exist until the handler is invoked #1312

Open fabiorossetto opened 1 year ago

fabiorossetto commented 1 year ago

Consider the following code:

  using namespace boost::asio;
  io_context context;
  auto socket = std::make_unique<ip::tcp::socket>(context);
  socket.connect(ip::tcp::endpoint{ip::address_v4::loopback(), 8011});

  std::vector<uint8_t> aBuffer(100);
  async_read(
      buffer(aBuffer),
      [](const boost::system::error_code& err, size_t bytesTransferred)
      {
        std::cout << "Read " << bytesTransferred << " bytes. Err: " << err << std::endl;
      });
  post(context, [&socket]() { socket.reset(); });
  context.run();  

The completion handler passed to async_read does not access the socket in any way. Destroying the socket cancels all outstanding asynchronous operations, so one would expect that the following usage is valid and may cause the completion handler to be invoked with error code operation_aborted.

However async_read internally stores a reference to socket within the read_dynbuf_v1_op struct, and it may happen that async_read_some is called on the socket despite the socket being deleted already, causing a segmentation fault.

The documentation correctly reports that the buffer passed to async_read must stay valid until the completion of the operation, but the same requirement is not listed for the stream. This is different from a plain call to async_read_some which, to the best of my knowledge, can be safely used as follows:

  socket->async_read_some(buffer(aBuffer),
      [](const boost::system::error_code& err, size_t bytesTransferred)
      {
        std::cout << "Read " << bytesTransferred << " bytes. Err: " << err << std::endl;
      });
  post(context, [&socket]() { socket.reset(); });
  context.run();