Fix critical security vulnerability and state-machine wedge in BwuManager.

PiperOrigin-RevId: 923367474
This commit is contained in:
Edwin Wu
2026-05-29 06:04:06 -07:00
committed by Copybara-Service
parent 7c31996db2
commit 54c155389c
2 changed files with 110 additions and 3 deletions
+25 -3
View File
@@ -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)
@@ -1089,6 +1089,91 @@ TEST_F(BwuManagerTest, BlockBwuFrameFromAdvertiser) {
UnRegisterChannelForEndpoint(kEndpointId2);
}
TEST_F(BwuManagerTest, ReceiveUnexpectedSafeToClose_NoCrash) {
ExceptionOr<OfflineFrame> 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<OfflineFrame> 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<EndpointChannel> 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<OfflineFrame> 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<OfflineFrame> 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<FakeEndpointChannel*>(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<OfflineFrame> 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<EndpointChannel> 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<OfflineFrame> 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<FakeEndpointChannel*>(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());