arvidn / libtorrent

an efficient feature complete C++ bittorrent implementation
http://libtorrent.org
Other
5.21k stars 993 forks source link

100% cpu when blocked by firewall #3311

Closed sally431 closed 6 years ago

sally431 commented 6 years ago

I have this in my own code (it is the only libtorrent related code, and I haven't altered any of the code that was installed by boost or libtorrent):

static libtorrent::session s;

If I block my program using my firewall, it gets infinitely stuck in the following loop from boost\asio\detail\impl\win_iocp_io_service.ipp:

  size_t win_iocp_io_service::run(boost::system::error_code& ec)
  {
    <snip>

    size_t n = 0;
    while (do_one(true, ec))
      if (n != (std::numeric_limits<size_t>::max)())
        ++n;
    return n;
  }

What is happening is the call to do_one causes the Windows connect function to be called, which fails and returns -1. Calling WSAGetLastError per the documentation for connect gets the following error code:

  WSAECONNREFUSED 10061 Connection refused.
  No connection could be made because the target computer actively refused it. This usually results
  from trying to connect to a service that is inactive on the foreign host—that is, one with no server
  application running.

This is understandable as I blocked the program in my firewall. The problem is, the program continues to try to connect, over and over, hammering the firewall with connection attempts, each one port number higher than the last, many times per second. This runs the CPU at 100% and brings the computer to its knees.

From the debugger I can tell that the following function from boost\asio\detail\impl\socket_select_interrupter.ipp is being called:

  void socket_select_interrupter::open_descriptors()
  {
    if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr,
          addr_len, ec) == socket_error_retval)
      boost::asio::detail::throw_error(ec, "socket_select_interrupter");
    <snip>
  }

and it is throwing that "socket_select_interrupter" exception, which is caught in libtorrent's udp_socket.cpp:

  void udp_socket::setup_read(udp::socket* s)
  {
        <snip>
    TORRENT_CATCH(boost::system::system_error& e)
    {
  #ifdef BOOST_NO_EXCEPTIONS
        // dummy
        error_code ec;
        boost::system::system_error e(ec);
  #endif
        get_io_service().post(boost::bind(&udp_socket::on_read
            , this, e.code(), s));
    }
  }

Of course, exceptions are an expensive resource used by developers to "return" normal and expected errors (even though they aren't supposed to be used that way), so I'd expect nothing less from the horriblty misnamed "boost", but I cannot understand why libtorrent is doing this. Shouldn't it back off somehow? I looked through the libtorrent documentation and all I could find that seemed like it might work was:

  // this is the maximum number of connections we will
  // attempt this tick
  int max_connections = m_settings.get_int(settings_pack::connection_speed);

but that had no effect at all, even when set to zero. So how do I prevent libtorrent using 100% CPU when it is blocked from connecting by a firewall?

libtorrent v1.1.9 windows 10 visual studio

arvidn commented 6 years ago

I would expect outgoing connections to spin in trying to connect to new peers (as long as there are new peers to try). However, there are limits to how many times peers are tried before giving up. In this case it sounds like it's a listen socket that fails with WSAECONNREFUSED. At least that seems a lot more likely to be spinning like this.

It would be really helpful if you could find out which boost.asio call is failing. You should be able to set a breakpoint on the exception being thrown for instance. Although, fundamentally it doesn't sound like it's the exception itself that's the problem, but rather that this error is not expected or not treated as fatal.

arvidn commented 6 years ago

are you using a socks5 proxy by any chance?

sally431 commented 6 years ago

I'm not using any proxy.

Regarding which boost.asio call is failing, here is a stack trace at the point the exception is thrown (from line 89 of boost::asio::detail::socket_select_interrupter::open_descriptors() in boost\asio\detail\impl\socket_select_interrupter.ipp):

>   Foo.exe!boost::asio::detail::socket_select_interrupter::open_descriptors()  Line 89 C++
    Foo.exe!boost::asio::detail::socket_select_interrupter::socket_select_interrupter()  Line 42    C++
    Foo.exe!boost::asio::detail::select_reactor::select_reactor(boost::asio::io_service & io_service={...})  Line 48 + 0x5f bytes   C++
    Foo.exe!boost::asio::detail::service_registry::create<boost::asio::detail::select_reactor>()  Line 81 + 0x23 bytes  C++
    Foo.exe!boost::asio::detail::service_registry::do_use_service(const boost::asio::io_service::service::key & key={...}, boost::asio::io_service::service * (boost::asio::io_service &)* factory=0x006bb365)  Line 123 + 0x9 bytes    C++
    Foo.exe!boost::asio::detail::service_registry::use_service<boost::asio::detail::select_reactor>()  Line 49  C++
    Foo.exe!boost::asio::use_service<boost::asio::detail::select_reactor>(boost::asio::io_service & ios={...})  Line 34 C++
    Foo.exe!boost::asio::detail::win_iocp_socket_service_base::get_reactor()  Line 675 + 0xa bytes  C++
    Foo.exe!boost::asio::detail::win_iocp_socket_service_base::start_reactor_op(boost::asio::detail::win_iocp_socket_service_base::base_implementation_type & impl={...}, int op_type=0, boost::asio::detail::reactor_op * op=0x00396c08)  Line 540 + 0x8 bytes C++
    Foo.exe!boost::asio::detail::win_iocp_socket_service_base::start_null_buffers_receive_op(boost::asio::detail::win_iocp_socket_service_base::base_implementation_type & impl={...}, int flags=0, boost::asio::detail::reactor_op * op=0x00396c08)  Line 451  C++
    Foo.exe!boost::asio::detail::win_iocp_socket_service<boost::asio::ip::udp>::async_receive_from<libtorrent::aux::allocating_handler<boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::arg<1>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > >,336> >(boost::asio::detail::win_iocp_socket_service<boost::asio::ip::udp>::implementation_type & impl={...}, const boost::asio::null_buffers & __formal={...}, boost::asio::ip::basic_endpoint<boost::asio::ip::udp> & sender_endpoint={...}, int flags=0, libtorrent::aux::allocating_handler<boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::arg<1>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > >,336> & handler={...})  Line 434 C++
    Foo.exe!boost::asio::datagram_socket_service<boost::asio::ip::udp>::async_receive_from<boost::asio::null_buffers,libtorrent::aux::allocating_handler<boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::arg<1>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > >,336> >(boost::asio::detail::win_iocp_socket_service<boost::asio::ip::udp>::implementation_type & impl={...}, const boost::asio::null_buffers & buffers={...}, boost::asio::ip::basic_endpoint<boost::asio::ip::udp> & sender_endpoint={...}, int flags=0, const libtorrent::aux::allocating_handler<boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::arg<1>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > >,336> & handler={...})  Line 419  C++
    Foo.exe!boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> >::async_receive_from<boost::asio::null_buffers,libtorrent::aux::allocating_handler<boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::arg<1>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > >,336> >(const boost::asio::null_buffers & buffers={...}, boost::asio::ip::basic_endpoint<boost::asio::ip::udp> & sender_endpoint={...}, const libtorrent::aux::allocating_handler<boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::arg<1>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > >,336> & handler={...})  Line 898   C++
    Foo.exe!libtorrent::udp_socket::setup_read(boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > * s=0x00396bbc)  Line 579 + 0x80 bytes  C++
    Foo.exe!libtorrent::udp_socket::on_read(const boost::system::error_code & ec={...}, boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > * s=0x00396bbc)  Line 359  C++
    Foo.exe!boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>::operator()(libtorrent::udp_socket * p=0x00396b90, const boost::system::error_code & a1={...}, boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > * a2=0x00396bbc)  Line 281   C++
    Foo.exe!boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::_bi::value<boost::system::error_code>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> >::operator()<boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list0>(boost::_bi::type<void> __formal={...}, boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> & f={...}, boost::_bi::list0 & a={...}, boost::_bi::type<void> __formal={...})  Line 399 C++
    Foo.exe!boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::_bi::value<boost::system::error_code>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > >::operator()()  Line 21  C++
    Foo.exe!boost::asio::asio_handler_invoke<boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::_bi::value<boost::system::error_code>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > > >(boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::_bi::value<boost::system::error_code>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > > & function={...}, ...)  Line 70  C++
    Foo.exe!boost_asio_handler_invoke_helpers::invoke<boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::_bi::value<boost::system::error_code>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > >,boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::_bi::value<boost::system::error_code>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > > >(boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::_bi::value<boost::system::error_code>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > > & function={...}, boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::_bi::value<boost::system::error_code>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > > & context={...})  Line 37 + 0x12 bytes    C++
    Foo.exe!boost::asio::detail::completion_handler<boost::_bi::bind_t<void,boost::_mfi::mf2<void,libtorrent::udp_socket,boost::system::error_code const &,boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *>,boost::_bi::list3<boost::_bi::value<libtorrent::udp_socket *>,boost::_bi::value<boost::system::error_code>,boost::_bi::value<boost::asio::basic_datagram_socket<boost::asio::ip::udp,boost::asio::datagram_socket_service<boost::asio::ip::udp> > *> > > >::do_complete(boost::asio::detail::win_iocp_io_service * owner=0x00395030, boost::asio::detail::win_iocp_operation * base=0x02d48950, const boost::system::error_code & __formal={...}, const boost::system::error_code & __formal={...})  Line 68 + 0xd bytes C++
    Foo.exe!boost::asio::detail::win_iocp_operation::complete(boost::asio::detail::win_iocp_io_service & owner={...}, const boost::system::error_code & ec={...}, unsigned int bytes_transferred=0)  Line 46 + 0x12 bytes   C++
    Foo.exe!boost::asio::detail::win_iocp_io_service::do_one(bool block=true, boost::system::error_code & ec={...})  Line 406   C++
    Foo.exe!boost::asio::detail::win_iocp_io_service::run(boost::system::error_code & ec={...})  Line 164 + 0xd bytes   C++
    Foo.exe!boost::asio::io_service::run()  Line 59 + 0xf bytes C++
    Foo.exe!boost::_mfi::mf0<unsigned int,boost::asio::io_service>::operator()(boost::asio::io_service * p=0x00394f84)  Line 50 C++
    Foo.exe!boost::_bi::list1<boost::_bi::value<boost::asio::io_service *> >::operator()<unsigned int,boost::_mfi::mf0<unsigned int,boost::asio::io_service>,boost::_bi::list0>(boost::_bi::type<unsigned int> __formal={...}, boost::_mfi::mf0<unsigned int,boost::asio::io_service> & f={...}, boost::_bi::list0 & a={...}, boost::_bi::type<unsigned int> __formal={...})  Line 250  C++
    Foo.exe!boost::_bi::bind_t<unsigned int,boost::_mfi::mf0<unsigned int,boost::asio::io_service>,boost::_bi::list1<boost::_bi::value<boost::asio::io_service *> > >::operator()()  Line 21    C++
    Foo.exe!boost::asio::detail::win_thread::func<boost::_bi::bind_t<unsigned int,boost::_mfi::mf0<unsigned int,boost::asio::io_service>,boost::_bi::list1<boost::_bi::value<boost::asio::io_service *> > > >::run()  Line 119  C++
    Foo.exe!boost::asio::detail::win_thread_function(void * arg=0x003995c0)  Line 120   C++
    Foo.exe!_callthreadstartex()  Line 348 + 0xf bytes  C
    Foo.exe!_threadstartex(void * ptd=0x00399610)  Line 331 C
    kernel32.dll!7c80b729()     
    [Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]
arvidn commented 6 years ago

would you mind testing this patch? https://github.com/arvidn/libtorrent/pull/3312

sally431 commented 6 years ago

Yes, that patch fixes it for me. My thanks.