Search code examples
c++boostboost-asio

Simple Boost UDP receiver gest heap-use-after-free when using std::vector


I am struggeling to understand why my quite simple UDP receiver is getting heap-free-after-use error (diagnosed by ASAN). The idea is listen to a configurable number of local ports for incoming packets.

I am here posting a simplified version of the class

UdpReceiver.hpp

class UdpReceiver
{
  public:
    UdpReceiver(std::vector<int> listen_ports);
    void run();
  protected:
    boost::asio::io_service m_io;
    char m_receive_buffer[MAX_RECEIVE_LENGTH];
    std::vector<udp::endpoint> m_endpoints;
    std::vector<udp::socket> m_sockets;
    void handleUdpData(const boost::system::error_code& error, size_t bytes_recvd, int idx);

}; 

UdpReceiver.cpp

UdpReceiver::UdpReceiver(std::vector<int> listen_ports) : 
  m_io()
{ 
  
  int idx = 0;
  try {
  for (auto port: listen_ports) {
    m_endpoints.push_back(udp::endpoint(udp::v4(), port));
    m_sockets.push_back(udp::socket(m_io, m_endpoints[idx]));
    m_sockets[idx].async_receive_from(
      boost::asio::buffer(m_receive_buffer, MAX_RECEIVE_LENGTH), m_endpoints[idx],
      boost::bind(&MessageParser::handleUdpData, this,
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred,
      idx)
    );
    idx++;
  }
  } catch(const std::exception &exc) 
  {
    std::cerr <<exc.what();
    exit(-1);
  }
}

According to ASAN m_endpoints.push_back(udp::endpoint(udp::v4(), port)) allocates some dynamic memory which is again freed by a later iteration. This eventually gives me use-after-free which messes up my application in a unpredictable way.

I cant really understand how the use of std::vector should not work in this case. Any ideas?


Solution

  • The documentation for async_receive_from says: "Ownership of the sender_endpoint object is retained by the caller, which must guarantee that it is valid until the handler is called."

    Your push_backs may reallocate the underlying storage, leaving async_receive_from with a dangling reference.

    To avoid reallocation, reserve space for the necessary amount of elements before entering the loop:

    m_endpoints.reserve(listen_ports.size());
    m_sockets.reserve(listen_ports.size());