diff --git a/connections/implementation/bwu_manager.cc b/connections/implementation/bwu_manager.cc index 76b93cc6..0cbafc7a 100644 --- a/connections/implementation/bwu_manager.cc +++ b/connections/implementation/bwu_manager.cc @@ -572,9 +572,19 @@ void BwuManager::OnBwuNegotiationFrame( OperationResultCode::NEARBY_GENERIC_REMOTE_UPGRADE_FAILURE); break; case BandwidthUpgradeNegotiationFrame::LAST_WRITE_TO_PRIOR_CHANNEL: + if (!in_progress_upgrades_.contains(endpoint_id)) { + LOG(ERROR) << "Received LAST_WRITE_TO_PRIOR_CHANNEL for endpoint " + << endpoint_id << " but no upgrade is in progress."; + return; + } ProcessLastWriteToPriorChannelEvent(client, endpoint_id); break; case BandwidthUpgradeNegotiationFrame::SAFE_TO_CLOSE_PRIOR_CHANNEL: + if (!in_progress_upgrades_.contains(endpoint_id)) { + LOG(ERROR) << "Received SAFE_TO_CLOSE_PRIOR_CHANNEL for endpoint " + << endpoint_id << " but no upgrade is in progress."; + return; + } ProcessSafeToClosePriorChannelEvent(client, endpoint_id); break; default: @@ -1205,9 +1215,8 @@ void BwuManager::ProcessLastWriteToPriorChannelEvent( // loss). But now that we've received this definitive final write over that // prior EndpointChannel, we can let the remote device that they can safely // close their end of this now-dormant EndpointChannel. - EndpointChannel* previous_endpoint_channel = - previous_endpoint_channels_[endpoint_id].get(); - if (!previous_endpoint_channel) { + auto it = previous_endpoint_channels_.find(endpoint_id); + if (it == previous_endpoint_channels_.end()) { LOG(ERROR) << "BwuManager received a BWU_NEGOTIATION.LAST_WRITE_TO_PRIOR_CHANNEL " "OfflineFrame for unknown endpoint " @@ -1215,6 +1224,12 @@ void BwuManager::ProcessLastWriteToPriorChannelEvent( successfully_upgraded_endpoints_.emplace(endpoint_id); return; } + EndpointChannel* previous_endpoint_channel = it->second.get(); + if (!previous_endpoint_channel) { + LOG(ERROR) << "previous_endpoint_channel is null for endpoint " + << endpoint_id; + return; + } LOG(INFO) << "ProcessLastWriteToPriorChannelEvent: service_id=" << previous_endpoint_channel->GetServiceId() @@ -1267,6 +1282,13 @@ void BwuManager::ProcessSafeToClosePriorChannelEvent( // or not (as is the case with Android's Bluetooth sockets, where closing // instantly throws an IOException on the remote device). auto item = previous_endpoint_channels_.extract(endpoint_id); + if (item.empty()) { + LOG(ERROR) + << "BwuManager received a BWU_NEGOTIATION.SAFE_TO_CLOSE_PRIOR_CHANNEL " + "OfflineFrame for unknown endpoint " + << endpoint_id << ", can't complete the upgrade protocol."; + return; + } auto& previous_endpoint_channel = item.mapped(); if (previous_endpoint_channel == nullptr) { LOG(ERROR) diff --git a/connections/implementation/bwu_manager_test.cc b/connections/implementation/bwu_manager_test.cc index dd858901..e817ad7c 100644 --- a/connections/implementation/bwu_manager_test.cc +++ b/connections/implementation/bwu_manager_test.cc @@ -1089,6 +1089,91 @@ TEST_F(BwuManagerTest, BlockBwuFrameFromAdvertiser) { UnRegisterChannelForEndpoint(kEndpointId2); } +TEST_F(BwuManagerTest, ReceiveUnexpectedSafeToClose_NoCrash) { + ExceptionOr safe_to_close_frame = + parser::FromBytes(parser::ForBwuSafeToClose()); + bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); +} + +TEST_F(BwuManagerTest, ReceiveUnexpectedLastWrite_NoCrashOrWedge) { + ExceptionOr last_write_frame = + parser::FromBytes(parser::ForBwuLastWrite()); + bwu_manager_->OnIncomingFrame(last_write_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); +} + +TEST_F(BwuManagerTest, ReceiveEarlyLastWrite_Success) { + CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH); + std::shared_ptr shared_initial_channel = + ecm_.GetChannelForEndpoint(std::string(kEndpointId1)); + + bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1), + Medium::WEB_RTC); + ASSERT_TRUE(bwu_manager_->IsUpgradeOngoing(std::string(kEndpointId1))); + + ExceptionOr last_write_frame = + parser::FromBytes(parser::ForBwuLastWrite()); + bwu_manager_->OnIncomingFrame(last_write_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); + + FakeEndpointChannel* upgraded_channel = + fake_web_rtc_bwu_handler_->NotifyBwuManagerOfIncomingConnection( + /*initialize_call_index=*/0u, bwu_manager_.get()); + + ExceptionOr safe_to_close_frame = + parser::FromBytes(parser::ForBwuSafeToClose()); + bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); + + auto old_channel = + dynamic_cast(shared_initial_channel.get()); + EXPECT_FALSE(upgraded_channel->IsPaused()); + EXPECT_TRUE(old_channel->is_closed()); + EXPECT_EQ(location::nearby::proto::connections::DisconnectionReason::UPGRADED, + old_channel->disconnection_reason()); + UnRegisterChannelForEndpoint(kEndpointId1); +} + +TEST_F(BwuManagerTest, ReceiveUnexpectedLastWriteBeforeUpgrade_NoWedge) { + ExceptionOr last_write_frame = + parser::FromBytes(parser::ForBwuLastWrite()); + bwu_manager_->OnIncomingFrame(last_write_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); + + CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH); + std::shared_ptr shared_initial_channel = + ecm_.GetChannelForEndpoint(std::string(kEndpointId1)); + + bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1), + Medium::WEB_RTC); + + FakeEndpointChannel* upgraded_channel = + fake_web_rtc_bwu_handler_->NotifyBwuManagerOfIncomingConnection( + /*initialize_call_index=*/0u, bwu_manager_.get()); + + bwu_manager_->OnIncomingFrame(last_write_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); + + ExceptionOr safe_to_close_frame = + parser::FromBytes(parser::ForBwuSafeToClose()); + bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); + + auto old_channel = + dynamic_cast(shared_initial_channel.get()); + EXPECT_FALSE(upgraded_channel->IsPaused()); + EXPECT_TRUE(old_channel->is_closed()); + UnRegisterChannelForEndpoint(kEndpointId1); +} + INSTANTIATE_TEST_SUITE_P(BwuManagerTestParam, BwuManagerTestParam, testing::Bool());