skip to Main Content

I made some modifications to this code snippet which is used to sendrecieve ICMP packets through ASIO APIs.

Here are the modifications that I made(full code snippet is seen below):

  1. using the standard version API instead of the corresponding API provided by Boost

    • using std::bind instead of boost::bind
  2. starting to use shared pointer

    • using a shared pointer which points to pinger(i.e. std::shared_ptr<pinger> sp{new pinger(io_service, argv[1])};) instead of contructing an instance directly by the default constructor of pinger (i.e. pinger p(io_service, argv[1]);).
    • class pinger derives from std::enabled_shared_from_this<pinger> now
    • the second argument passed to boost::bind is not a this pointer anymore,I use shared_from_this() to pass a std::shared_pointer to std::bind.For exmple: my code is timer_.async_wait(std::bind(&pinger::start_send, shared_from_this()));, whereas the orignal one is timer_.async_wait(boost::bind(&pinger::handle_timeout, this));.
    • to avoid calling shared_from_this() in the constructor of class pinger, the start_send(); start_receive(); is moved out of the constructer and a new function named Init() is used to invoke these two aforemetioned functions.
  3. using the non-throwing version asio::raw_socket::send_to and retrying to send ICMP packets when some error occurs.

    • socket_.send_to(request_buffer.data(), destination_); is replaced by socket_.send_to(request_buffer.data(), destination_, flags, error);
    • retry to send ICMP packets(by calling timer_.async_wait(std::bind(&pinger::start_send, shared_from_this())); when socket_.send_to faces to an error.

Here is my code snippet(icmp_header.hpp & ipv4_header.hpp are seen here):

#if 1
#include "icmp_header.hpp"
#include "ipv4_header.hpp"
#include <boost/asio.hpp>
#include <functional>
#include <iostream>
#include <istream>
#include <memory>
#include <ostream>

using boost::asio::deadline_timer;
using boost::asio::ip::icmp;
namespace posix_time = boost::posix_time;

class pinger : public std::enable_shared_from_this<pinger> {
public:
  pinger(boost::asio::io_service &io_service, const char *destination)
      : resolver_(io_service), socket_(io_service, icmp::v4()),
        timer_(io_service), sequence_number_(0), num_replies_(0) {
    icmp::resolver::query query(icmp::v4(), destination, "");
    destination_ = *resolver_.resolve(query);
  }

  void init() {
    start_send();
    start_receive();
  }

private:
  void start_send() {
    std::string body(""Hello!" from Asio ping.");

    // Create an ICMP header for an echo request.
    icmp_header echo_request;
    echo_request.type(icmp_header::echo_request);
    echo_request.code(0);
    echo_request.identifier(get_identifier());
    echo_request.sequence_number(++sequence_number_);
    compute_checksum(echo_request, body.begin(), body.end());

    // Encode the request packet.
    boost::asio::streambuf request_buffer;
    std::ostream os(&request_buffer);
    os << echo_request << body;

    // Send the request.
    time_sent_ = posix_time::microsec_clock::universal_time();

    boost::system::error_code error;
    boost::asio::socket_base::message_flags flags;
    socket_.send_to(request_buffer.data(), destination_, flags, error);
    if (error) {
      std::cout << "send_to failed1:" << error.message() << std::endl;
      std::cout << "send_to failed2:" << std::endl;
      timer_.expires_at(time_sent_ + posix_time::seconds(1));
      timer_.async_wait(std::bind(&pinger::start_send, shared_from_this()));
    } else {
      // Wait up to five seconds for a reply.
      num_replies_ = 0;
      timer_.expires_at(time_sent_ + posix_time::seconds(5));
      timer_.async_wait(std::bind(&pinger::handle_timeout, shared_from_this()));
    }
  }

  void handle_timeout() {
    if (num_replies_ == 0)
      std::cout << "Request timed out" << std::endl;

    // Requests must be sent no less than one second apart.
    timer_.expires_at(time_sent_ + posix_time::seconds(1));
    timer_.async_wait(std::bind(&pinger::start_send, shared_from_this()));
  }

  void start_receive() {
    // Discard any data already in the buffer.
    reply_buffer_.consume(reply_buffer_.size());

    // Wait for a reply. We prepare the buffer to receive up to 64KB.
    socket_.async_receive(reply_buffer_.prepare(65536),
                          std::bind(&pinger::handle_receive, shared_from_this(),
                                    std::placeholders::_1,
                                    std::placeholders::_2));
  }

  void handle_receive(boost::system::error_code error, std::size_t length) {
    // The actual number of bytes received is committed to the buffer so that we
    // can extract it using a std::istream object.
    if (error) {
      std::cout << "error in handle_receive:"
                << boost::system::system_error(error).what() << std::endl;
    }

    reply_buffer_.commit(length);

    // Decode the reply packet.
    std::istream is(&reply_buffer_);
    ipv4_header ipv4_hdr;
    icmp_header icmp_hdr;
    is >> ipv4_hdr >> icmp_hdr;

    // We can receive all ICMP packets received by the host, so we need to
    // filter out only the echo replies that match the our identifier and
    // expected sequence number.
    if (is && icmp_hdr.type() == icmp_header::echo_reply &&
        icmp_hdr.identifier() == get_identifier() &&
        icmp_hdr.sequence_number() == sequence_number_) {
      // If this is the first reply, interrupt the five second timeout.
      if (num_replies_++ == 0)
        timer_.cancel();

      // Print out some information about the reply packet.
      posix_time::ptime now = posix_time::microsec_clock::universal_time();
      std::cout << length - ipv4_hdr.header_length() << " bytes from "
                << ipv4_hdr.source_address()
                << ": icmp_seq=" << icmp_hdr.sequence_number()
                << ", ttl=" << ipv4_hdr.time_to_live()
                << ", time=" << (now - time_sent_).total_milliseconds() << " ms"
                << std::endl;
    }

    start_receive();
  }

  static unsigned short get_identifier() {
#if defined(BOOST_WINDOWS)
    return static_cast<unsigned short>(::GetCurrentProcessId());
#else
    return static_cast<unsigned short>(::getpid());
#endif
  }

  icmp::resolver resolver_;
  icmp::endpoint destination_;
  icmp::socket socket_;
  deadline_timer timer_;
  unsigned short sequence_number_;
  posix_time::ptime time_sent_;
  boost::asio::streambuf reply_buffer_;
  std::size_t num_replies_;
};

int main(int argc, char *argv[]) {
  try {
    if (argc != 2) {
      std::cerr << "Usage: ping <host>" << std::endl;
#if !defined(BOOST_WINDOWS)
      std::cerr << "(You may need to run this program as root.)" << std::endl;
#endif
      return 1;
    }

    boost::asio::io_service io_service;
    std::shared_ptr<pinger> sp{new pinger(io_service, argv[1])};
    sp->init();
    io_service.run();
  } catch (std::exception &e) {
    std::cerr << "Exception: " << e.what() << std::endl;
  }
}
#endif

The code snippet above works well when my local network is ok.Here is the output:

32 bytes from 192.168.1.51: icmp_seq=1, ttl=58, time=4 ms
32 bytes from 192.168.1.51: icmp_seq=2, ttl=58, time=3 ms
//omit...
//running a long long time

The program receives a signal SIGABRT and then aborts when I run sudo ifconfig wlp59s0 down on purpose(to make sure whether the program is stable or not). Here is the backstrace I got by GDB

sudo gdb -q ./modified_ping 
Reading symbols from ./modified_ping...done.
(gdb) r 192.168.1.51
Starting program: /home/jhon/icmp/build/modified_ping 192.168.1.51
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
32 bytes from 192.168.1.51: icmp_seq=1, ttl=58, time=3 ms
32 bytes from 192.168.1.51: icmp_seq=2, ttl=58, time=4 ms
32 bytes from 192.168.1.51: icmp_seq=3, ttl=58, time=3 ms
32 bytes from 192.168.1.51: icmp_seq=4, ttl=58, time=3 ms   //NOTE:manually run `ifconfig down` on purpose
send_to failed1:
send_to failed2:
*** Error in `/home/jhon/icmp/build/modified_ping': double free or corruption (!prev): 0x0000000000699f80 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777f5)[0x7ffff6dc07f5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8038a)[0x7ffff6dc938a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7ffff6dcd58c]
/home/jhon/icmp/build/modified_ping[0x44bcd6]
/home/jhon/icmp/build/modified_ping[0x44ae36]
/home/jhon/icmp/build/modified_ping[0x449b56]
/home/jhon/icmp/build/modified_ping[0x447e09]
/home/jhon/icmp/build/modified_ping[0x445403]
/home/jhon/icmp/build/modified_ping[0x44e005]
/home/jhon/icmp/build/modified_ping[0x43f347]
/home/jhon/icmp/build/modified_ping[0x44de00]
/home/jhon/icmp/build/modified_ping[0x44db4e]
/home/jhon/icmp/build/modified_ping[0x44d6eb]
/home/jhon/icmp/build/modified_ping[0x44d3b6]
/home/jhon/icmp/build/modified_ping[0x44d0d9]
/home/jhon/icmp/build/modified_ping[0x44c9c7]
/home/jhon/icmp/build/modified_ping[0x44c03f]
/home/jhon/icmp/build/modified_ping[0x44b257]
/home/jhon/icmp/build/modified_ping[0x439e94]
/home/jhon/icmp/build/modified_ping[0x43ca93]
/home/jhon/icmp/build/modified_ping[0x43c4e8]
/home/jhon/icmp/build/modified_ping[0x43cdac]
/home/jhon/icmp/build/modified_ping[0x436b34]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7ffff6d69840]
/home/jhon/icmp/build/modified_ping[0x4059a9]
======= Memory map: ========
00400000-00486000 r-xp 00000000 08:02 94506936                           /home/jhon/icmp/build/modified_ping
00685000-00686000 r--p 00085000 08:02 94506936                           /home/jhon/icmp/build/modified_ping
00686000-00687000 rw-p 00086000 08:02 94506936                           /home/jhon/icmp/build/modified_ping
00687000-006b9000 rw-p 00000000 00:00 0                                  [heap]
7ffff0000000-7ffff0021000 rw-p 00000000 00:00 0 
7ffff0021000-7ffff4000000 ---p 00000000 00:00 0 
7ffff6d49000-7ffff6f09000 r-xp 00000000 08:02 14029372                   /lib/x86_64-linux-gnu/libc-2.23.so
7ffff6f09000-7ffff7109000 ---p 001c0000 08:02 14029372                   /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7109000-7ffff710d000 r--p 001c0000 08:02 14029372                   /lib/x86_64-linux-gnu/libc-2.23.so
7ffff710d000-7ffff710f000 rw-p 001c4000 08:02 14029372                   /lib/x86_64-linux-gnu/libc-2.23.so
7ffff710f000-7ffff7113000 rw-p 00000000 00:00 0 
7ffff7113000-7ffff712a000 r-xp 00000000 08:02 14024978                   /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff712a000-7ffff7329000 ---p 00017000 08:02 14024978                   /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff7329000-7ffff732a000 r--p 00016000 08:02 14024978                   /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff732a000-7ffff732b000 rw-p 00017000 08:02 14024978                   /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff732b000-7ffff7433000 r-xp 00000000 08:02 14024798                   /lib/x86_64-linux-gnu/libm-2.23.so
7ffff7433000-7ffff7632000 ---p 00108000 08:02 14024798                   /lib/x86_64-linux-gnu/libm-2.23.so
7ffff7632000-7ffff7633000 r--p 00107000 08:02 14024798                   /lib/x86_64-linux-gnu/libm-2.23.so
7ffff7633000-7ffff7634000 rw-p 00108000 08:02 14024798                   /lib/x86_64-linux-gnu/libm-2.23.so
7ffff7634000-7ffff77a6000 r-xp 00000000 08:02 44435598                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7ffff77a6000-7ffff79a6000 ---p 00172000 08:02 44435598                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7ffff79a6000-7ffff79b0000 r--p 00172000 08:02 44435598                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7ffff79b0000-7ffff79b2000 rw-p 0017c000 08:02 44435598                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7ffff79b2000-7ffff79b6000 rw-p 00000000 00:00 0 
7ffff79b6000-7ffff79b9000 r-xp 00000000 08:02 44441454                   /usr/lib/x86_64-linux-gnu/libboost_system.so.1.58.0
7ffff79b9000-7ffff7bb8000 ---p 00003000 08:02 44441454                   /usr/lib/x86_64-linux-gnu/libboost_system.so.1.58.0
7ffff7bb8000-7ffff7bb9000 r--p 00002000 08:02 44441454                   /usr/lib/x86_64-linux-gnu/libboost_system.so.1.58.0
7ffff7bb9000-7ffff7bba000 rw-p 00003000 08:02 44441454                   /usr/lib/x86_64-linux-gnu/libboost_system.so.1.58.0
7ffff7bba000-7ffff7bd2000 r-xp 00000000 08:02 14024729                   /lib/x86_64-linux-gnu/libpthread-2.23.so
7ffff7bd2000-7ffff7dd1000 ---p 00018000 08:02 14024729                   /lib/x86_64-linux-gnu/libpthread-2.23.so
7ffff7dd1000-7ffff7dd2000 r--p 00017000 08:02 14024729                   /lib/x86_64-linux-gnu/libpthread-2.23.so
7ffff7dd2000-7ffff7dd3000 rw-p 00018000 08:02 14024729                   /lib/x86_64-linux-gnu/libpthread-2.23.so
7ffff7dd3000-7ffff7dd7000 rw-p 00000000 00:00 0 
7ffff7dd7000-7ffff7dfd000 r-xp 00000000 08:02 14024795                   /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7fcb000-7ffff7fd2000 rw-p 00000000 00:00 0 
7ffff7ff6000-7ffff7ff7000 rw-p 00000000 00:00 0 
7ffff7ff7000-7ffff7ffa000 r--p 00000000 00:00 0                          [vvar]
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0                          [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 00025000 08:02 14024795                   /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7ffd000-7ffff7ffe000 rw-p 00026000 08:02 14024795                   /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

Program received signal SIGABRT, Aborted.
0x00007ffff6d7e438 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54      ../sysdeps/unix/sysv/linux/raise.c: no such file or folder
(gdb) bt
#0  0x00007ffff6d7e438 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007ffff6d8003a in __GI_abort () at abort.c:89
#2  0x00007ffff6dc07fa in __libc_message (do_abort=do_abort@entry=2, fmt=fmt@entry=0x7ffff6ed9fd8 "*** Error in `%s': %s: 0x%s ***n") at ../sysdeps/posix/libc_fatal.c:175
#3  0x00007ffff6dc938a in malloc_printerr (ar_ptr=<optimized out>, ptr=<optimized out>, str=0x7ffff6eda108 "double free or corruption (!prev)", action=3) at malloc.c:5020
#4  _int_free (av=<optimized out>, p=<optimized out>, have_lock=0) at malloc.c:3874
#5  0x00007ffff6dcd58c in __GI___libc_free (mem=<optimized out>) at malloc.c:2975
#6  0x000000000044bcd6 in __gnu_cxx::new_allocator<char>::deallocate (this=0x7fffffffdd98, __p=0x699f80 "b") at /usr/include/c++/4.9/ext/new_allocator.h:110
#7  0x000000000044ae36 in std::allocator_traits<std::allocator<char> >::deallocate (__a=..., __p=0x699f80 "b", __n=128) at /usr/include/c++/4.9/bits/alloc_traits.h:514
#8  0x0000000000449b56 in std::_Vector_base<char, std::allocator<char> >::_M_deallocate (this=0x7fffffffdd98, __p=0x699f80 "b", __n=128) at /usr/include/c++/4.9/bits/stl_vector.h:178
#9  0x0000000000447e09 in std::_Vector_base<char, std::allocator<char> >::~_Vector_base (this=0x7fffffffdd98, __in_chrg=<optimized out>) at /usr/include/c++/4.9/bits/stl_vector.h:160
#10 0x0000000000445403 in std::vector<char, std::allocator<char> >::~vector (this=0x7fffffffdd98, __in_chrg=<optimized out>) at /usr/include/c++/4.9/bits/stl_vector.h:425
#11 0x000000000044e005 in boost::asio::basic_streambuf<std::allocator<char> >::~basic_streambuf (this=0x7fffffffdd50, __in_chrg=<optimized out>)
    at /usr/include/boost/asio/basic_streambuf.hpp:111
#12 0x000000000043f347 in pinger::start_send (this=0x698d50) at /home/jhon/icmp/src/main.cpp:42
#13 0x000000000044de00 in std::_Mem_fn<void (pinger::*)()>::_M_call<std::shared_ptr<pinger>&> (this=0x7fffffffe140, __ptr=std::shared_ptr (count 4, weak 1) 0x698d50)
    at /usr/include/c++/4.9/functional:526
#14 0x000000000044db4e in std::_Mem_fn<void (pinger::*)()>::operator()<std::shared_ptr<pinger>&, , void>(std::shared_ptr<pinger>&) const (this=0x7fffffffe140, 
    __object=std::shared_ptr (count 4, weak 1) 0x698d50) at /usr/include/c++/4.9/functional:578
#15 0x000000000044d6eb in std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>::__call<void, boost::system::error_code const&, 0ul>(std::tuple<boost::system::error_code const&>&&, std::_Index_tuple<0ul>) (this=0x7fffffffe140, __args=<unknown type in /home/jhon/icmp/build/modified_ping, CU 0xee6ae, DIE 0x1264e2>)
    at /usr/include/c++/4.9/functional:1264
#16 0x000000000044d3b6 in std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>::operator()<boost::system::error_code const&, void>(boost::system::error_code const&) (
    this=0x7fffffffe140) at /usr/include/c++/4.9/functional:1323
#17 0x000000000044d0d9 in boost::asio::detail::binder1<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>, boost::system::error_code>::operator()() (this=0x7fffffffe140)
---Type <return> to continue, or q <return> to quit---
    at /usr/include/boost/asio/detail/bind_handler.hpp:47
#18 0x000000000044c9c7 in boost::asio::asio_handler_invoke<boost::asio::detail::binder1<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>, boost::system::error_code> >(boost::asio::detail::binder1<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>, boost::system::error_code>&, ...) (function=...)
    at /usr/include/boost/asio/handler_invoke_hook.hpp:69
#19 0x000000000044c03f in boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::binder1<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>, boost::system::error_code>, std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)> >(boost::asio::detail::binder1<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>, boost::system::error_code>&, std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>&) (function=..., context=...) at /usr/include/boost/asio/detail/handler_invoke_helpers.hpp:37
#20 0x000000000044b257 in boost::asio::detail::wait_handler<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)> >::do_complete(boost::asio::detail::task_io_service*, boost::asio::detail::task_io_service_operation*, boost::system::error_code const&, unsigned long) (owner=0x698c70, base=0x699af0) at /usr/include/boost/asio/detail/wait_handler.hpp:70
#21 0x0000000000439e94 in boost::asio::detail::task_io_service_operation::complete (this=0x699af0, owner=..., ec=..., bytes_transferred=0)
    at /usr/include/boost/asio/detail/task_io_service_operation.hpp:38
#22 0x000000000043ca93 in boost::asio::detail::task_io_service::do_run_one (this=0x698c70, lock=..., this_thread=..., ec=...) at /usr/include/boost/asio/detail/impl/task_io_service.ipp:372
#23 0x000000000043c4e8 in boost::asio::detail::task_io_service::run (this=0x698c70, ec=...) at /usr/include/boost/asio/detail/impl/task_io_service.ipp:149
#24 0x000000000043cdac in boost::asio::io_service::run (this=0x7fffffffe360) at /usr/include/boost/asio/impl/io_service.ipp:59
#25 0x0000000000436b34 in main (argc=2, argv=0x7fffffffe498) at /home/jhon/icmp/src/main.cpp:155

Valgrind:

Thanks to @Basile Starynkevitch.
I try to use valgrind to figure out the problem.I add add_compile_options(-Wall -Wextra) in the CMakeLists.txt(set (CMAKE_BUILD_TYPE debug) has already been there when debugging with GDB). I recompile the code sippet above and run it with sudo valgrind .... But what surprises me is that no signal is received now.

Here is the output which is seen in the terminal (with valgrind on Ubuntu 16.04):

sudo valgrind --log-file=valgrind.log --error-limit=no --leak-check=full --tool=memcheck --show-reachable=yes ./modified_ping 192.168.1.51
32 bytes from 192.168.1.51: icmp_seq=1, ttl=58, time=90 ms
32 bytes from 192.168.1.51: icmp_seq=2, ttl=58, time=7 ms
32 bytes from 192.168.1.51: icmp_seq=3, ttl=58, time=11 ms
32 bytes from 192.168.1.51: icmp_seq=4, ttl=58, time=4 ms
32 bytes from 192.168.1.51: icmp_seq=5, ttl=58, time=3 ms
32 bytes from 192.168.1.51: icmp_seq=6, ttl=58, time=2 ms
send_to failed1:
send_to failed2:
send_to failed1:
send_to failed2:
send_to failed1:
send_to failed2:
//omit...

Here is what the valgrind.log contains:
NOTE:1.since the program runs infinitely with valgrind, the program is stopped by CTRL+C. It’s strange that the same binary program still encounters SIGABRT with sudo gdb ... when ifconfig down is called in purpose.

2.The log is huge, full log is seen at here.

sudo valgrind --log-file=valgrind.log --error-limit=no --leak-check=full --tool=memcheck --show-reachable=yes ./modified_ping 192.168.1.51


==9362== Memcheck, a memory error detector
==9362== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==9362== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==9362== Command: ./modified_ping 192.168.1.51
//...full log is seen at https://coliru.stacked-
==9362== 1,265 bytes in 55 blocks are definitely lost in loss record 18 of 20
==9362==    at 0x4C2E0EF: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9362==    by 0x505823E: ??? (in /usr/lib/x86_64-linux-gnu/libboost_system.so.1.58.0)
==9362==    by 0x5058BFD: ??? (in /usr/lib/x86_64-linux-gnu/libboost_system.so.1.58.0)
==9362==    by 0x43731A: boost::system::error_code::message() const (error_code.hpp:357)
==9362==    by 0x43F0E9: pinger::start_send() (main.cpp:53)
==9362==    by 0x44DDFF: void std::_Mem_fn<void (pinger::*)()>::_M_call<std::shared_ptr<pinger>&>(std::shared_ptr<pinger>&, void const volatile*) const (in /home/jhon/icmp/build/modified_ping)
==9362==    by 0x44DB4D: void std::_Mem_fn<void (pinger::*)()>::operator()<std::shared_ptr<pinger>&, , void>(std::shared_ptr<pinger>&) const (functional:578)
==9362==    by 0x44D6EA: void std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>::__call<void, boost::system::error_code const&, 0ul>(std::tuple<boost::system::error_code const&>&&, std::_Index_tuple<0ul>) (functional:1264)
==9362==    by 0x44D3B5: void std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>::operator()<boost::system::error_code const&, void>(boost::system::error_code const&) (functional:1323)
==9362==    by 0x44D0D8: boost::asio::detail::binder1<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>, boost::system::error_code>::operator()() (bind_handler.hpp:47)
==9362==    by 0x44C9C6: void boost::asio::asio_handler_invoke<boost::asio::detail::binder1<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>, boost::system::error_code> >(boost::asio::detail::binder1<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>, boost::system::error_code>&, ...) (handler_invoke_hook.hpp:69)
==9362==    by 0x44C03E: void boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::binder1<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>, boost::system::error_code>, std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)> >(boost::asio::detail::binder1<std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>, boost::system::error_code>&, std::_Bind<std::_Mem_fn<void (pinger::*)()> (std::shared_ptr<pinger>)>&) (handler_invoke_helpers.hpp:37)
==9362== 
==9362== 65,536 bytes in 1 blocks are still reachable in loss record 19 of 20
==9362==    at 0x4C2E0EF: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9362==    by 0x44BD8B: __gnu_cxx::new_allocator<char>::allocate(unsigned long, void const*) (new_allocator.h:104)
==9362==    by 0x44AEB9: std::allocator_traits<std::allocator<char> >::allocate(std::allocator<char>&, unsigned long) (alloc_traits.h:488)
==9362==    by 0x449CBB: std::_Vector_base<char, std::allocator<char> >::_M_allocate(unsigned long) (stl_vector.h:170)
==9362==    by 0x447F2D: std::vector<char, std::allocator<char> >::_M_default_append(unsigned long) (vector.tcc:557)
==9362==    by 0x44547E: std::vector<char, std::allocator<char> >::resize(unsigned long) (stl_vector.h:676)
==9362==    by 0x445C4F: boost::asio::basic_streambuf<std::allocator<char> >::reserve(unsigned long) (basic_streambuf.hpp:326)
==9362==    by 0x442D04: boost::asio::basic_streambuf<std::allocator<char> >::prepare(unsigned long) (basic_streambuf.hpp:207)
==9362==    by 0x43F6B5: pinger::start_receive() (main.cpp:82)
==9362==    by 0x43EDE1: pinger::init() (main.cpp:26)
==9362==    by 0x436B27: main (main.cpp:154)
==9362== 
==9362== 72,704 bytes in 1 blocks are still reachable in loss record 20 of 20
==9362==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9362==    by 0x52E4EFF: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==9362==    by 0x40106F9: call_init.part.0 (dl-init.c:72)
==9362==    by 0x401080A: call_init (dl-init.c:30)
==9362==    by 0x401080A: _dl_init (dl-init.c:120)
==9362==    by 0x4000C69: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==9362==    by 0x1: ???
==9362==    by 0xFFF000652: ???
==9362==    by 0xFFF000662: ???
==9362== 
==9362== LEAK SUMMARY:
==9362==    definitely lost: 1,265 bytes in 55 blocks
==9362==    indirectly lost: 0 bytes in 0 blocks
==9362==      possibly lost: 0 bytes in 0 blocks
==9362==    still reachable: 139,978 bytes in 19 blocks
==9362==         suppressed: 0 bytes in 0 blocks
==9362== 
==9362== For counts of detected and suppressed errors, rerun with: -v
==9362== Use --track-origins=yes to see where uninitialised values come from
==9362== ERROR SUMMARY: 337 errors from 5 contexts (suppressed: 0 from 0)

What’s more, it’s strange that no signal would be recieved anymore if std::cout << "send_to failed1:" << error.message() << std::endl; is commented out when sudo ifconfig wlp59s0 down has been called. I don’t think the problem is with this expression.But I really can’t figure out where goes wrong. Could somebody shed some light on this matter?

(gdb) make  //after commenting out `std::cout << "send_to failed1:" << error.message() << std::endl;`
[100%] Built target modified_ping
(gdb) r 192.168.1.51
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/jhon/icmp/build/modified_ping 192.168.1.51
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
32 bytes from 192.168.1.51: icmp_seq=1, ttl=58, time=4 ms
32 bytes from 192.168.1.51: icmp_seq=2, ttl=58, time=7 ms
32 bytes from 192.168.1.51: icmp_seq=3, ttl=58, time=2 ms
32 bytes from 192.168.1.51: icmp_seq=4, ttl=58, time=3 ms
32 bytes from 192.168.1.51: icmp_seq=5, ttl=58, time=3 ms
32 bytes from 192.168.1.51: icmp_seq=6, ttl=58, time=4 ms
32 bytes from 192.168.1.51: icmp_seq=7, ttl=58, time=4 ms
32 bytes from 192.168.1.51: icmp_seq=8, ttl=58, time=5 ms
32 bytes from 192.168.1.51: icmp_seq=9, ttl=58, time=3 ms
32 bytes from 192.168.1.51: icmp_seq=10, ttl=58, time=2 ms
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:
send_to failed2:

Extra Information:

I am using boost-1.58 which is installed by sudo apt-get install libboost-all-dev on ubuntu16.04.

UPDATED:

Thanks to @273K’s generous help.

But the double free or corruption seems not caused by the wrong declaration of handle_receive() because even if the program which does not invoke handle_receive() anymore still faces a similar problem. Here is the backtrace.

2

Answers


  1. I tried your code. The first error faced: error C4700: uninitialized local variable 'flags' used. I have corrected the definition:

    boost::asio::socket_base::message_flags flags{};
    

    You have extended void handle_receive(std::size_t length) from the example to
    void handle_receive(boost::system::error_code error, std::size_t length). This is incorrect. According to the manual basic_raw_socket::async_receive, it must receive error by const reference.

    void handle_receive(const boost::system::error_code &error,
                        std::size_t length)
    

    After fixing those errors, the SIGABRT went away.

    error.message() is supposed to access error by reference, but your definition directs the compiler to access error on the stack, that results in stack memory corruption, because the caller of handle_receive places sizeof(void*) bytes on the stack or in a CPU register, but error.message() uses sizeof(boost::system::error_code) bytes on the stack.

    Login or Signup to reply.
  2. The line expression

     boost::system::system_error(error)
    

    could be

     error.message()
    

    Now, the fact that it crashes gives me the idea that you might be finding conflicting/incompatible versions of Boost System at runtime vs. at compile time. I know that boost::error_code received some layout-incompatible changes over time.

    Another source of such compatibility problem is when the linked libraries are built with very different compiler flags.

    Use tools like ldd to check what libraries are actually found at runtime, and whether there are surprises. Also, check the preprocessed source (or e.g. BOOST_VERSION) to see that no surprise header versions are picked up where you didn’t expect them.


    Inverting the burden of proof, I have created a docker environment to test your code and see that the problem is not reproduced, perhaps you can compare: ubu1604.zip containing:

    • File Dockerfile

       FROM ubuntu:16.04
      
       ENV DEBIAN_FRONTEND noninteractive
       RUN apt-get -qyy update
       RUN apt-get -qyy install build-essential screen cmake
       RUN apt-get -qyy install libboost-all-dev
      
       COPY so /so
      
       # build
       WORKDIR /so
       RUN cmake . -Wno-dev && make;
      
       ENTRYPOINT [ "/so/test" ]
      
    • File so/CMakeLists.txt

       SET(CMAKE_CXX_STANDARD 11)
      
       ADD_EXECUTABLE(test test.cpp)
      
       find_package(Boost COMPONENTS system REQUIRED)
      
       include_directories(${Boost_INCLUDE_DIRS})
       set(CMAKE_CXX_FLAGS -pthread)
       target_link_libraries(test ${Boost_LIBRARIES})
      
    • File so/icmp_header.hpp

       //
       // icmp_header.hpp
       // ~~~~~~~~~~~~~~~
       //
       // Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com)
       //
       // Distributed under the Boost Software License, Version 1.0. (See accompanying
       // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       //
      
       #ifndef ICMP_HEADER_HPP
       #define ICMP_HEADER_HPP
      
       #include <istream>
       #include <ostream>
       #include <algorithm>
      
       // ICMP header for both IPv4 and IPv6.
       //
       // The wire format of an ICMP header is:
       // 
       // 0               8               16                             31
       // +---------------+---------------+------------------------------+      ---
       // |               |               |                              |       ^
       // |     type      |     code      |          checksum            |       |
       // |               |               |                              |       |
       // +---------------+---------------+------------------------------+    8 bytes
       // |                               |                              |       |
       // |          identifier           |       sequence number        |       |
       // |                               |                              |       v
       // +-------------------------------+------------------------------+      ---
      
       class icmp_header
       {
       public:
         enum { echo_reply = 0, destination_unreachable = 3, source_quench = 4,
           redirect = 5, echo_request = 8, time_exceeded = 11, parameter_problem = 12,
           timestamp_request = 13, timestamp_reply = 14, info_request = 15,
           info_reply = 16, address_request = 17, address_reply = 18 };
      
         icmp_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }
      
         unsigned char type() const { return rep_[0]; }
         unsigned char code() const { return rep_[1]; }
         unsigned short checksum() const { return decode(2, 3); }
         unsigned short identifier() const { return decode(4, 5); }
         unsigned short sequence_number() const { return decode(6, 7); }
      
         void type(unsigned char n) { rep_[0] = n; }
         void code(unsigned char n) { rep_[1] = n; }
         void checksum(unsigned short n) { encode(2, 3, n); }
         void identifier(unsigned short n) { encode(4, 5, n); }
         void sequence_number(unsigned short n) { encode(6, 7, n); }
      
         friend std::istream& operator>>(std::istream& is, icmp_header& header)
           { return is.read(reinterpret_cast<char*>(header.rep_), 8); }
      
         friend std::ostream& operator<<(std::ostream& os, const icmp_header& header)
           { return os.write(reinterpret_cast<const char*>(header.rep_), 8); }
      
       private:
         unsigned short decode(int a, int b) const
           { return (rep_[a] << 8) + rep_[b]; }
      
         void encode(int a, int b, unsigned short n)
         {
           rep_[a] = static_cast<unsigned char>(n >> 8);
           rep_[b] = static_cast<unsigned char>(n & 0xFF);
         }
      
         unsigned char rep_[8];
       };
      
       template <typename Iterator>
       void compute_checksum(icmp_header& header,
           Iterator body_begin, Iterator body_end)
       {
         unsigned int sum = (header.type() << 8) + header.code()
           + header.identifier() + header.sequence_number();
      
         Iterator body_iter = body_begin;
         while (body_iter != body_end)
         {
           sum += (static_cast<unsigned char>(*body_iter++) << 8);
           if (body_iter != body_end)
             sum += static_cast<unsigned char>(*body_iter++);
         }
      
         sum = (sum >> 16) + (sum & 0xFFFF);
         sum += (sum >> 16);
         header.checksum(static_cast<unsigned short>(~sum));
       }
      
       #endif // ICMP_HEADER_HPP
      
    • File so/ipv4_header.hpp

       //
       // ipv4_header.hpp
       // ~~~~~~~~~~~~~~~
       //
       // Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com)
       //
       // Distributed under the Boost Software License, Version 1.0. (See accompanying
       // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       //
      
       #ifndef IPV4_HEADER_HPP
       #define IPV4_HEADER_HPP
      
       #include <algorithm>
       #include <boost/asio/ip/address_v4.hpp>
      
       // Packet header for IPv4.
       //
       // The wire format of an IPv4 header is:
       // 
       // 0               8               16                             31
       // +-------+-------+---------------+------------------------------+      ---
       // |       |       |               |                              |       ^
       // |version|header |    type of    |    total length in bytes     |       |
       // |  (4)  | length|    service    |                              |       |
       // +-------+-------+---------------+-+-+-+------------------------+       |
       // |                               | | | |                        |       |
       // |        identification         |0|D|M|    fragment offset     |       |
       // |                               | |F|F|                        |       |
       // +---------------+---------------+-+-+-+------------------------+       |
       // |               |               |                              |       |
       // | time to live  |   protocol    |       header checksum        |   20 bytes
       // |               |               |                              |       |
       // +---------------+---------------+------------------------------+       |
       // |                                                              |       |
       // |                      source IPv4 address                     |       |
       // |                                                              |       |
       // +--------------------------------------------------------------+       |
       // |                                                              |       |
       // |                   destination IPv4 address                   |       |
       // |                                                              |       v
       // +--------------------------------------------------------------+      ---
       // |                                                              |       ^
       // |                                                              |       |
       // /                        options (if any)                      /    0 - 40
       // /                                                              /     bytes
       // |                                                              |       |
       // |                                                              |       v
       // +--------------------------------------------------------------+      ---
      
       class ipv4_header
       {
       public:
         ipv4_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }
      
         unsigned char version() const { return (rep_[0] >> 4) & 0xF; }
         unsigned short header_length() const { return (rep_[0] & 0xF) * 4; }
         unsigned char type_of_service() const { return rep_[1]; }
         unsigned short total_length() const { return decode(2, 3); }
         unsigned short identification() const { return decode(4, 5); }
         bool dont_fragment() const { return (rep_[6] & 0x40) != 0; }
         bool more_fragments() const { return (rep_[6] & 0x20) != 0; }
         unsigned short fragment_offset() const { return decode(6, 7) & 0x1FFF; }
         unsigned int time_to_live() const { return rep_[8]; }
         unsigned char protocol() const { return rep_[9]; }
         unsigned short header_checksum() const { return decode(10, 11); }
      
         boost::asio::ip::address_v4 source_address() const
         {
           boost::asio::ip::address_v4::bytes_type bytes
             = { { rep_[12], rep_[13], rep_[14], rep_[15] } };
           return boost::asio::ip::address_v4(bytes);
         }
      
         boost::asio::ip::address_v4 destination_address() const
         {
           boost::asio::ip::address_v4::bytes_type bytes
             = { { rep_[16], rep_[17], rep_[18], rep_[19] } };
           return boost::asio::ip::address_v4(bytes);
         }
      
         friend std::istream& operator>>(std::istream& is, ipv4_header& header)
         {
           is.read(reinterpret_cast<char*>(header.rep_), 20);
           if (header.version() != 4)
             is.setstate(std::ios::failbit);
           std::streamsize options_length = header.header_length() - 20;
           if (options_length < 0 || options_length > 40)
             is.setstate(std::ios::failbit);
           else
             is.read(reinterpret_cast<char*>(header.rep_) + 20, options_length);
           return is;
         }
      
       private:
         unsigned short decode(int a, int b) const
           { return (rep_[a] << 8) + rep_[b]; }
      
         unsigned char rep_[60];
       };
      
       #endif // IPV4_HEADER_HPP
      
    • File so/test.cpp

       #include "icmp_header.hpp"
       #include "ipv4_header.hpp"
       #include <boost/asio.hpp>
       #include <functional>
       #include <iostream>
       #include <istream>
       #include <memory>
       #include <ostream>
      
       using boost::asio::deadline_timer;
       using boost::asio::ip::icmp;
       namespace posix_time = boost::posix_time;
      
       class pinger : public std::enable_shared_from_this<pinger> {
         public:
           pinger(boost::asio::io_service& io_service, const char* destination)
               : resolver_(io_service)
               , socket_(io_service, icmp::v4())
               , timer_(io_service)
               , sequence_number_(0)
               , num_replies_(0) {
               icmp::resolver::query query(icmp::v4(), destination, "");
               destination_ = *resolver_.resolve(query);
           }
      
           void init() {
               start_send();
               start_receive();
           }
      
         private:
           void start_send() {
               std::string body(""Hello!" from Asio ping.");
      
               // Create an ICMP header for an echo request.
               icmp_header echo_request;
               echo_request.type(icmp_header::echo_request);
               echo_request.code(0);
               echo_request.identifier(get_identifier());
               echo_request.sequence_number(++sequence_number_);
               compute_checksum(echo_request, body.begin(), body.end());
      
               // Encode the request packet.
               boost::asio::streambuf request_buffer;
               std::ostream           os(&request_buffer);
               os << echo_request << body;
      
               // Send the request.
               time_sent_ = posix_time::microsec_clock::universal_time();
      
               boost::system::error_code               error;
               boost::asio::socket_base::message_flags flags{};
               socket_.send_to(request_buffer.data(), destination_, flags, error);
               if (error) {
                   std::cout << "send_to failed1:" << error.message() << std::endl;
                   std::cout << "send_to failed2:" << std::endl;
                   timer_.expires_at(time_sent_ + posix_time::seconds(1));
                   timer_.async_wait(
                       std::bind(&pinger::start_send, shared_from_this()));
               } else {
                   // Wait up to five seconds for a reply.
                   num_replies_ = 0;
                   timer_.expires_at(time_sent_ + posix_time::seconds(5));
                   timer_.async_wait(
                       std::bind(&pinger::handle_timeout, shared_from_this()));
               }
           }
      
           void handle_timeout() {
               if (num_replies_ == 0)
                   std::cout << "Request timed out" << std::endl;
      
               // Requests must be sent no less than one second apart.
               timer_.expires_at(time_sent_ + posix_time::seconds(1));
               timer_.async_wait(std::bind(&pinger::start_send, shared_from_this()));
           }
      
           void start_receive() {
               // Discard any data already in the buffer.
               reply_buffer_.consume(reply_buffer_.size());
      
               // Wait for a reply. We prepare the buffer to receive up to 64KB.
               socket_.async_receive(
                   reply_buffer_.prepare(65536),
                   std::bind(&pinger::handle_receive, shared_from_this(),
                             std::placeholders::_1, std::placeholders::_2));
           }
      
           void handle_receive(boost::system::error_code error, std::size_t length) {
               // The actual number of bytes received is committed to the buffer so
               // that we can extract it using a std::istream object.
               if (error) {
                   std::cout << "error in handle_receive:"
                             << boost::system::system_error(error).what() << std::endl;
               }
      
               reply_buffer_.commit(length);
      
               // Decode the reply packet.
               std::istream is(&reply_buffer_);
               ipv4_header  ipv4_hdr;
               icmp_header  icmp_hdr;
               is >> ipv4_hdr >> icmp_hdr;
      
               // We can receive all ICMP packets received by the host, so we need to
               // filter out only the echo replies that match the our identifier and
               // expected sequence number.
               if (is && icmp_hdr.type() == icmp_header::echo_reply &&
                   icmp_hdr.identifier() == get_identifier() &&
                   icmp_hdr.sequence_number() == sequence_number_) {
                   // If this is the first reply, interrupt the five second timeout.
                   if (num_replies_++ == 0)
                       timer_.cancel();
      
                   // Print out some information about the reply packet.
                   posix_time::ptime now =
                       posix_time::microsec_clock::universal_time();
                   std::cout << length - ipv4_hdr.header_length() << " bytes from "
                             << ipv4_hdr.source_address()
                             << ": icmp_seq=" << icmp_hdr.sequence_number()
                             << ", ttl=" << ipv4_hdr.time_to_live()
                             << ", time=" << (now - time_sent_).total_milliseconds()
                             << " ms" << std::endl;
               }
      
               start_receive();
           }
      
           static unsigned short get_identifier() {
       #if defined(BOOST_WINDOWS)
           return static_cast<unsigned short>(::GetCurrentProcessId());
       #else
           return static_cast<unsigned short>(::getpid());
       #endif
         }
      
         icmp::resolver         resolver_;
         icmp::endpoint         destination_;
         icmp::socket           socket_;
         deadline_timer         timer_;
         unsigned short         sequence_number_;
         posix_time::ptime      time_sent_;
         boost::asio::streambuf reply_buffer_;
         std::size_t            num_replies_;
       };
      
       int main(int argc, char *argv[]) {
           try {
               if (argc != 2) {
                   std::cerr << "Usage: ping <host>" << std::endl;
                   return 1;
               }
      
               boost::asio::io_service io_service;
      
               auto sp = std::make_shared<pinger>(io_service, argv[1]);
               sp->init();
               io_service.run();
           } catch (std::exception &e) {
               std::cerr << "Exception: " << e.what() << std::endl;
           }
       }
      

    Build:

    docker build -t sopingtest
    

    Run:

    docker run --rm -it sopingtest
    

    Demo

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search