Browse Source

backport from upstream: fixed uTP vulnerability

677e642754
miguelfreitas v0.9.31
Miguel Freitas 9 years ago
parent
commit
187e052240
  1. 2
      libtorrent/src/session.cpp
  2. 103
      libtorrent/src/utp_stream.cpp

2
libtorrent/src/session.cpp

@ -1342,7 +1342,7 @@ namespace libtorrent @@ -1342,7 +1342,7 @@ namespace libtorrent
, utp_min_timeout(500) // milliseconds
, utp_syn_resends(2)
, utp_fin_resends(2)
, utp_num_resends(6)
, utp_num_resends(3)
, utp_connect_timeout(3000) // milliseconds
#ifndef TORRENT_NO_DEPRECATE
, utp_delayed_ack(0) // milliseconds

103
libtorrent/src/utp_stream.cpp

@ -111,7 +111,12 @@ enum @@ -111,7 +111,12 @@ enum
// the max number of packets to fast-resend per
// selective ack message
sack_resend_limit = 3,
// only re-sending a single packet per sack
// appears to improve performance by making it
// less likely to loose the re-sent packet. Because
// when that happens, we must time-out in order
// to continue, which takes a long time.
sack_resend_limit = 1
};
// compare if lhs is less than rhs, taking wrapping
@ -237,7 +242,7 @@ struct utp_socket_impl @@ -237,7 +242,7 @@ struct utp_socket_impl
, m_written(0)
, m_receive_buffer_size(0)
, m_read_buffer_size(0)
, m_in_buf_size(100 * 1024 * 1024)
, m_in_buf_size(1024 * 1024)
, m_in_packets(0)
, m_out_packets(0)
, m_send_delay(0)
@ -267,6 +272,7 @@ struct utp_socket_impl @@ -267,6 +272,7 @@ struct utp_socket_impl
, m_deferred_ack(false)
, m_subscribe_drained(false)
, m_stalled(false)
, m_confirmed(false)
{
TORRENT_ASSERT(m_userdata);
for (int i = 0; i != num_delay_hist; ++i)
@ -637,6 +643,11 @@ struct utp_socket_impl @@ -637,6 +643,11 @@ struct utp_socket_impl
// of sockets in the utp_socket_manager to be notified of
// the socket being writable again
bool m_stalled:1;
// this is false by default and set to true once we've received a non-SYN
// packet for this connection with a correct ack_nr, confirming that the
// other end is not spoofing its source IP
bool m_confirmed:1;
};
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
@ -1227,7 +1238,7 @@ void utp_socket_impl::send_syn() @@ -1227,7 +1238,7 @@ void utp_socket_impl::send_syn()
{
INVARIANT_CHECK;
m_seq_nr = random();
m_seq_nr = random() & 0xffff;
m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK;
m_loss_seq_nr = m_acked_seq_nr;
m_ack_nr = 0;
@ -1447,9 +1458,10 @@ void utp_socket_impl::parse_sack(boost::uint16_t packet_ack, boost::uint8_t cons @@ -1447,9 +1458,10 @@ void utp_socket_impl::parse_sack(boost::uint16_t packet_ack, boost::uint8_t cons
{
experienced_loss(m_fast_resend_seq_nr);
int num_resent = 0;
for (; m_fast_resend_seq_nr != last_ack; m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK)
while (m_fast_resend_seq_nr != last_ack)
{
packet* p = (packet*)m_outbuf.at(m_fast_resend_seq_nr);
m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK;
if (!p) continue;
++num_resent;
if (!resend_packet(p, true)) break;
@ -1813,7 +1825,8 @@ bool utp_socket_impl::send_pkt(int flags) @@ -1813,7 +1825,8 @@ bool utp_socket_impl::send_pkt(int flags)
}
h->timestamp_difference_microseconds = m_reply_micro;
h->wnd_size = m_in_buf_size - m_buffered_incoming_bytes - m_receive_buffer_size;
h->wnd_size = (std::max)(m_in_buf_size - m_buffered_incoming_bytes
- m_receive_buffer_size, boost::int32_t(0));
h->ack_nr = m_ack_nr;
// if this is a FIN packet, override the type
@ -2079,8 +2092,10 @@ void utp_socket_impl::experienced_loss(int seq_nr) @@ -2079,8 +2092,10 @@ void utp_socket_impl::experienced_loss(int seq_nr)
// window size. The first packet that's lost will
// update the limit to the last sequence number we sent.
// i.e. only packet sent after this loss can cause another
// window size cut
if (compare_less_wrap(seq_nr, m_loss_seq_nr, ACK_MASK)) return;
// window size cut. The +1 is to turn the comparison into
// less than or equal to. If we experience loss of the
// same packet again, ignore it.
if (compare_less_wrap(seq_nr, m_loss_seq_nr + 1, ACK_MASK)) return;
// cut window size in 2
m_cwnd = (std::max)(m_cwnd * m_sm->loss_multiplier() / 100, boost::int64_t(m_mtu << 16));
@ -2261,10 +2276,30 @@ bool utp_socket_impl::consume_incoming_data( @@ -2261,10 +2276,30 @@ bool utp_socket_impl::consume_incoming_data(
return true;
}
if (m_read_buffer_size == 0
&& m_receive_buffer_size >= m_in_buf_size - m_buffered_incoming_bytes)
{
// if we don't have a buffer from the upper layer, and the
// number of queued up bytes, waiting for the upper layer,
// exceeds the advertized receive window, start ignoring
// more data packets
UTP_LOG("%8p: ERROR: our advertized window is not honored. "
"recv_buf: %d buffered_in: %d max_size: %d\n"
, static_cast<void*>(this), m_receive_buffer_size, m_buffered_incoming_bytes, m_in_buf_size);
return false;
}
if (ph->seq_nr == ((m_ack_nr + 1) & ACK_MASK))
{
TORRENT_ASSERT(m_inbuf.at(m_ack_nr) == 0);
if (m_buffered_incoming_bytes + m_receive_buffer_size + payload_size > m_in_buf_size)
{
UTP_LOGV("%8p: other end is not honoring our advertised window, dropping packet\n"
, static_cast<void*>(this));
return true;
}
// we received a packet in order
incoming(ptr, payload_size, 0, now);
m_ack_nr = (m_ack_nr + 1) & ACK_MASK;
@ -2312,7 +2347,14 @@ bool utp_socket_impl::consume_incoming_data( @@ -2312,7 +2347,14 @@ bool utp_socket_impl::consume_incoming_data(
if (m_inbuf.at(ph->seq_nr))
{
UTP_LOGV("%8p: already received seq_nr: %d\n"
, this, int(ph->seq_nr));
, static_cast<void*>(this), int(ph->seq_nr));
return true;
}
if (m_buffered_incoming_bytes + m_receive_buffer_size + payload_size > m_in_buf_size)
{
UTP_LOGV("%8p: other end is not honoring our advertised window, dropping packet %d\n"
, static_cast<void*>(this), int(ph->seq_nr));
return true;
}
@ -2502,8 +2544,10 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -2502,8 +2544,10 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
if (m_state == UTP_STATE_SYN_SENT && ph->get_type() == ST_STATE)
cmp_seq_nr = m_seq_nr;
#endif
if (m_state != UTP_STATE_NONE
&& compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK))
if ((m_state != UTP_STATE_NONE || ph->get_type() != ST_SYN)
&& (compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK)
|| compare_less_wrap(ph->ack_nr, m_acked_seq_nr
- dup_ack_limit, ACK_MASK)))
{
UTP_LOGV("%8p: incoming packet ack_nr:%d our seq_nr:%d (ignored)\n"
, this, int(ph->ack_nr), m_seq_nr);
@ -2538,7 +2582,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -2538,7 +2582,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
{
UTP_LOGV("%8p: incoming packet seq_nr:%d eof_seq_nr:%d (ignored)\n"
, this, int(ph->seq_nr), m_eof_seq_nr);
return true;
}
if (m_state != UTP_STATE_NONE
@ -2643,7 +2687,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -2643,7 +2687,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
, this, len, int(ptr - buf));
return true;
}
if (ptr - buf + len > size_t(size))
if (ptr - buf + len > ptrdiff_t(size))
{
UTP_LOGV("%8p: invalid extension header size:%d packet:%d\n"
, this, len, int(ptr - buf));
@ -2755,7 +2799,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -2755,7 +2799,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
m_local_address = m_sm->local_endpoint(m_remote_address, ec).address();
m_ack_nr = ph->seq_nr;
m_seq_nr = random();
m_seq_nr = random() & 0xffff;
m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK;
m_loss_seq_nr = m_acked_seq_nr;
m_fast_resend_seq_nr = m_seq_nr;
@ -2768,8 +2812,6 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -2768,8 +2812,6 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
TORRENT_ASSERT(m_recv_id == ((m_send_id + 1) & 0xffff));
defer_ack();
return true;
}
else
{
@ -2777,7 +2819,6 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -2777,7 +2819,6 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
UTP_LOGV("%8p: type:%s state:%s (ignored)\n"
, this, packet_type_names[ph->get_type()], socket_state_names[m_state]);
#endif
return true;
}
break;
}
@ -2790,7 +2831,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -2790,7 +2831,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
UTP_LOGV("%8p: incorrect ack_nr (%d) waiting for %d\n"
, this, int(ph->ack_nr), (m_seq_nr - 1) & ACK_MASK);
#endif
return true;
break;
}
TORRENT_ASSERT(!m_error);
@ -2845,7 +2886,11 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -2845,7 +2886,11 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
// (i.e. ST_STATE) we're not ACKing anything. If we just
// received a FIN packet, we need to ack that as well
bool has_ack = ph->get_type() == ST_DATA || ph->get_type() == ST_FIN || ph->get_type() == ST_SYN;
int prev_out_packets = m_out_packets;
boost::uint32_t prev_out_packets = m_out_packets;
// the connection is connected and this packet made it past all the
// checks. We can now assume the other end is not spoofing it's IP.
if (ph->get_type() != ST_SYN) m_confirmed = true;
// try to send more data as long as we can
// if send_pkt returns true
@ -2960,7 +3005,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -2960,7 +3005,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
}
#endif
return true;
break;
}
case UTP_STATE_FIN_SENT:
{
@ -3022,7 +3067,9 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -3022,7 +3067,9 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
// data, and we can gracefully shut down.
if (consume_incoming_data(ph, ptr, payload_size, receive_time))
return true;
{
break;
}
if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK))
{
@ -3047,18 +3094,17 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size @@ -3047,18 +3094,17 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
}
}
return true;
break;
}
case UTP_STATE_DELETE:
default:
{
// respond with a reset
send_reset(ph);
return true;
break;
}
}
return false;
return true;
}
void utp_socket_impl::do_ledbat(int acked_bytes, int delay, int in_flight, ptime const now)
@ -3165,6 +3211,9 @@ int utp_socket_impl::packet_timeout() const @@ -3165,6 +3211,9 @@ int utp_socket_impl::packet_timeout() const
int timeout = (std::max)(m_sm->min_timeout(), m_rtt.mean() + m_rtt.avg_deviation() * 2);
if (m_num_timeouts > 0) timeout += (1 << (int(m_num_timeouts) - 1)) * 1000;
// timeouts over 1 minute are capped
if (timeout > 60000) timeout = 60000;
return timeout;
}
@ -3192,7 +3241,11 @@ void utp_socket_impl::tick(ptime const& now) @@ -3192,7 +3241,11 @@ void utp_socket_impl::tick(ptime const& now)
if (m_outbuf.size()) ++m_num_timeouts;
if (m_num_timeouts > m_sm->num_resends())
// a socket that has not been confirmed to actually have a live remote end
// (the IP may have been spoofed) fail on the first timeout. If we had
// heard anything from this peer, it would have been confirmed.
if (m_num_timeouts > m_sm->num_resends()
|| (m_num_timeouts > 0 && !m_confirmed))
{
// the connection is dead
m_error = asio::error::timed_out;

Loading…
Cancel
Save