From 1f928d33161daa8df4b8a85a72764d79d9cef0e4 Mon Sep 17 00:00:00 2001 From: Harald Alvestrand Date: Thu, 28 Mar 2019 11:29:38 +0100 Subject: [PATCH] Close data channels when ID assignment fails. This prevents crashes due to unassigned IDs. Bug: chromium:945256 Change-Id: I63f3a17cc7dff07dab58a6bc59fe3606b23e8e18 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/129902 Reviewed-by: Seth Hampson Commit-Queue: Harald Alvestrand Cr-Commit-Position: refs/heads/master@{#27349} --- pc/data_channel.h | 12 +++++++----- pc/peer_connection.cc | 9 ++++++++- pc/peer_connection_end_to_end_unittest.cc | 24 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/pc/data_channel.h b/pc/data_channel.h index fa5c0f5f43..eef127928f 100644 --- a/pc/data_channel.h +++ b/pc/data_channel.h @@ -147,6 +147,13 @@ class DataChannel : public DataChannelInterface, public sigslot::has_slots<> { virtual uint64_t bytes_received() const { return bytes_received_; } virtual bool Send(const DataBuffer& buffer); + // Close immediately, ignoring any queued data or closing procedure. + // This is called for RTP data channels when SDP indicates a channel should + // be removed, or SCTP data channels when the underlying SctpTransport is + // being destroyed. + // It is also called by the PeerConnection if SCTP ID assignment fails. + void CloseAbruptly(); + // Called when the channel's ready to use. That can happen when the // underlying DataMediaChannel becomes ready, or when this channel is a new // stream on an existing DataMediaChannel, and we've finished negotiation. @@ -242,11 +249,6 @@ class DataChannel : public DataChannelInterface, public sigslot::has_slots<> { }; bool Init(const InternalDataChannelInit& config); - // Close immediately, ignoring any queued data or closing procedure. - // This is called for RTP data channels when SDP indicates a channel should - // be removed, or SCTP data channels when the underlying SctpTransport is - // being destroyed. - void CloseAbruptly(); void UpdateState(); void SetState(DataState state); void DisconnectFromProvider(); diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 211c5a7073..5b702e0c7a 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -5102,16 +5102,23 @@ bool PeerConnection::HasDataChannels() const { } void PeerConnection::AllocateSctpSids(rtc::SSLRole role) { + std::vector> channels_to_close; for (const auto& channel : sctp_data_channels_) { if (channel->id() < 0) { int sid; if (!sid_allocator_.AllocateSid(role, &sid)) { - RTC_LOG(LS_ERROR) << "Failed to allocate SCTP sid."; + RTC_LOG(LS_ERROR) << "Failed to allocate SCTP sid, closing channel."; + channels_to_close.push_back(channel); continue; } channel->SetSctpSid(sid); } } + // Since closing modifies the list of channels, we have to do the actual + // closing outside the loop. + for (const auto& channel : channels_to_close) { + channel->CloseAbruptly(); + } } void PeerConnection::OnSctpDataChannelClosed(DataChannel* channel) { diff --git a/pc/peer_connection_end_to_end_unittest.cc b/pc/peer_connection_end_to_end_unittest.cc index ae8366fc5f..b37d00a6e8 100644 --- a/pc/peer_connection_end_to_end_unittest.cc +++ b/pc/peer_connection_end_to_end_unittest.cc @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_encoder_factory_template.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "media/sctp/sctp_transport_internal.h" #include "rtc_base/gunit.h" #include "rtc_base/logging.h" @@ -721,6 +722,29 @@ TEST_P(PeerConnectionEndToEndTest, CloseDataChannelRemotelyWhileNotReferenced) { // close message and be destroyed. rtc::Thread::Current()->ProcessMessages(100); } + +// Test behavior of creating too many datachannels. +TEST_P(PeerConnectionEndToEndTest, TooManyDataChannelsOpenedBeforeConnecting) { + CreatePcs(webrtc::MockAudioEncoderFactory::CreateEmptyFactory(), + webrtc::MockAudioDecoderFactory::CreateEmptyFactory()); + + webrtc::DataChannelInit init; + std::vector> channels; + for (int i = 0; i <= cricket::kMaxSctpStreams / 2; i++) { + rtc::scoped_refptr caller_dc( + caller_->CreateDataChannel("data", init)); + channels.push_back(std::move(caller_dc)); + } + Negotiate(); + WaitForConnection(); + EXPECT_EQ_WAIT(callee_signaled_data_channels_.size(), + static_cast(cricket::kMaxSctpStreams / 2), kMaxWait); + EXPECT_EQ(DataChannelInterface::kOpen, + channels[(cricket::kMaxSctpStreams / 2) - 1]->state()); + EXPECT_EQ(DataChannelInterface::kClosed, + channels[cricket::kMaxSctpStreams / 2]->state()); +} + #endif // HAVE_SCTP INSTANTIATE_TEST_SUITE_P(PeerConnectionEndToEndTest,