Disallow SDP munging if it modifies the ICE ufrag.
Gated behind default-disabled field trial WebRTC-NoSdpMangleUfrag
Bug: b/409713509
Change-Id: I48e307ef3ca65b463dde8bcb24ae5252cd6fdf58
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/385721
Reviewed-by: Guido Urdaneta <guidou@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#44360}
diff --git a/experiments/field_trials.py b/experiments/field_trials.py
index 3ca7029..1c9f8c0 100755
--- a/experiments/field_trials.py
+++ b/experiments/field_trials.py
@@ -116,6 +116,9 @@
FieldTrial('WebRTC-MixedCodecSimulcast',
362277533,
date(2025, 9, 1)),
+ FieldTrial('WebRTC-NoSdpMangleUfrag',
+ 375571816,
+ date(2025, 10, 11)),
FieldTrial('WebRTC-Pacer-FastRetransmissions',
40235589,
date(2024, 4, 1)),
diff --git a/pc/peer_connection.h b/pc/peer_connection.h
index e2f6bf9..c5254e0 100644
--- a/pc/peer_connection.h
+++ b/pc/peer_connection.h
@@ -458,6 +458,15 @@
PayloadTypePicker& payload_type_picker() override {
return payload_type_picker_;
}
+ void DisableSdpMungingChecksForTesting() {
+ if (!signaling_thread()->IsCurrent()) {
+ signaling_thread()->BlockingCall(
+ [&]() { DisableSdpMungingChecksForTesting(); });
+ return;
+ }
+ RTC_DCHECK_RUN_ON(signaling_thread());
+ sdp_handler_->DisableSdpMungingChecksForTesting();
+ }
protected:
// Available for rtc::scoped_refptr creation
diff --git a/pc/peer_connection_ice_unittest.cc b/pc/peer_connection_ice_unittest.cc
index 4ba1709..20ee339 100644
--- a/pc/peer_connection_ice_unittest.cc
+++ b/pc/peer_connection_ice_unittest.cc
@@ -654,6 +654,9 @@
auto set_local_description_with_ufrag_pwd_length = [this](int ufrag_len,
int pwd_len) {
auto pc = CreatePeerConnectionWithAudioVideo();
+ // Because local munging is forbidden by spec, we have to disable the
+ // check for it.
+ pc->GetInternalPeerConnection()->DisableSdpMungingChecksForTesting();
auto offer = pc->CreateOffer();
SetIceUfragPwd(offer.get(), std::string(ufrag_len, 'x'),
std::string(pwd_len, 'x'));
diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc
index 95b396d5..59604bc 100644
--- a/pc/sdp_offer_answer.cc
+++ b/pc/sdp_offer_answer.cc
@@ -2370,6 +2370,33 @@
UpdateEndedRemoteMediaStreams();
}
+void SdpOfferAnswerHandler::ReportInitialSdpMunging(bool had_local_description,
+ SdpType type) {
+ // Report SDP munging of the initial call to setLocalDescription separately.
+ if (!had_local_description) {
+ switch (type) {
+ case SdpType::kOffer:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.PeerConnection.SdpMunging.Offer.Initial",
+ last_sdp_munging_type_, SdpMungingType::kMaxValue);
+ break;
+ case SdpType::kAnswer:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.PeerConnection.SdpMunging.Answer.Initial",
+ last_sdp_munging_type_, SdpMungingType::kMaxValue);
+ break;
+ case SdpType::kPrAnswer:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.PeerConnection.SdpMunging.PrAnswer.Initial",
+ last_sdp_munging_type_, SdpMungingType::kMaxValue);
+ break;
+ case SdpType::kRollback:
+ // Rollback does not have SDP so can not be munged.
+ break;
+ }
+ }
+}
+
void SdpOfferAnswerHandler::DoSetLocalDescription(
std::unique_ptr<SessionDescriptionInterface> desc,
rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
@@ -2429,6 +2456,19 @@
? last_created_offer_.get()
: last_created_answer_.get());
+ if (!disable_sdp_munging_checks_ &&
+ pc_->trials().IsEnabled("WebRTC-NoSdpMangleUfrag")) {
+ if (sdp_munging_type == kIceUfrag || sdp_munging_type == kIcePwd) {
+ RTC_LOG(LS_ERROR) << "Rejecting SDP because of ufrag modification";
+ observer->OnSetLocalDescriptionComplete(
+ RTCError(RTCErrorType::INVALID_PARAMETER,
+ "SDP is modified in a non-acceptable way"));
+ last_sdp_munging_type_ = sdp_munging_type;
+ ReportInitialSdpMunging(had_local_description, desc->GetType());
+ return;
+ }
+ }
+
// Grab the description type before moving ownership to
// ApplyLocalDescription, which may destroy it before returning.
const SdpType type = desc->GetType();
@@ -2463,29 +2503,10 @@
last_created_offer_.reset(nullptr);
last_created_answer_.reset(nullptr);
last_sdp_munging_type_ = sdp_munging_type;
+
// Report SDP munging of the initial call to setLocalDescription separately.
- if (!had_local_description) {
- switch (local_description()->GetType()) {
- case SdpType::kOffer:
- RTC_HISTOGRAM_ENUMERATION(
- "WebRTC.PeerConnection.SdpMunging.Offer.Initial",
- last_sdp_munging_type_, SdpMungingType::kMaxValue);
- break;
- case SdpType::kAnswer:
- RTC_HISTOGRAM_ENUMERATION(
- "WebRTC.PeerConnection.SdpMunging.Answer.Initial",
- last_sdp_munging_type_, SdpMungingType::kMaxValue);
- break;
- case SdpType::kPrAnswer:
- RTC_HISTOGRAM_ENUMERATION(
- "WebRTC.PeerConnection.SdpMunging.PrAnswer.Initial",
- last_sdp_munging_type_, SdpMungingType::kMaxValue);
- break;
- case SdpType::kRollback:
- // Rollback does not have SDP so can not be munged.
- break;
- }
- }
+ ReportInitialSdpMunging(had_local_description,
+ local_description()->GetType());
observer->OnSetLocalDescriptionComplete(RTCError::OK());
pc_->NoteUsageEvent(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED);
diff --git a/pc/sdp_offer_answer.h b/pc/sdp_offer_answer.h
index f3eff28..453e4bc 100644
--- a/pc/sdp_offer_answer.h
+++ b/pc/sdp_offer_answer.h
@@ -39,6 +39,7 @@
#include "api/uma_metrics.h"
#include "api/video/video_bitrate_allocator_factory.h"
#include "media/base/media_channel.h"
+#include "media/base/media_engine.h"
#include "media/base/stream_params.h"
#include "p2p/base/port_allocator.h"
#include "pc/codec_vendor.h"
@@ -183,6 +184,9 @@
}
SdpMungingType sdp_munging_type() const { return last_sdp_munging_type_; }
+ void DisableSdpMungingChecksForTesting() {
+ disable_sdp_munging_checks_ = true;
+ }
private:
class RemoteDescriptionOperation;
@@ -559,6 +563,8 @@
AddIceCandidateResult AddIceCandidateInternal(
const IceCandidateInterface* candidate);
+ void ReportInitialSdpMunging(bool had_local_description, SdpType type);
+
// ==================================================================
// Access to pc_ variables
MediaEngineInterface* media_engine() const;
@@ -682,6 +688,11 @@
// determines the SSL role.
std::optional<bool> initial_offerer_ RTC_GUARDED_BY(signaling_thread());
+ // Whether SDP munging checks are enabled or not.
+ // Some tests will be detected as SDP munging, so offer the option
+ // to disable.
+ bool disable_sdp_munging_checks_ = false;
+
WeakPtrFactory<SdpOfferAnswerHandler> weak_ptr_factory_
RTC_GUARDED_BY(signaling_thread());
};
diff --git a/pc/sdp_offer_answer_unittest.cc b/pc/sdp_offer_answer_unittest.cc
index a765355..8038556 100644
--- a/pc/sdp_offer_answer_unittest.cc
+++ b/pc/sdp_offer_answer_unittest.cc
@@ -1867,7 +1867,44 @@
}
TEST_F(SdpOfferAnswerMungingTest, IceUfrag) {
+ auto pc = CreatePeerConnection(
+ FieldTrials::CreateNoGlobal("WebRTC-NoSdpMangleUfrag/Enabled/"));
+ pc->AddAudioTrack("audio_track", {});
+
+ auto offer = pc->CreateOffer();
+ auto& transport_infos = offer->description()->transport_infos();
+ ASSERT_EQ(transport_infos.size(), 1u);
+ transport_infos[0].description.ice_ufrag =
+ "amungediceufragthisshouldberejected";
+ RTCError error;
+ // Ufrag is rejected.
+ EXPECT_FALSE(pc->SetLocalDescription(std::move(offer), &error));
+ EXPECT_THAT(
+ metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"),
+ ElementsAre(Pair(SdpMungingType::kIceUfrag, 1)));
+}
+
+TEST_F(SdpOfferAnswerMungingTest, IceUfragCheckDisabledByFieldTrial) {
+ auto pc = CreatePeerConnection(
+ FieldTrials::CreateNoGlobal("WebRTC-NoSdpMangleUfrag/Disabled/"));
+ pc->AddAudioTrack("audio_track", {});
+
+ auto offer = pc->CreateOffer();
+ auto& transport_infos = offer->description()->transport_infos();
+ ASSERT_EQ(transport_infos.size(), 1u);
+ transport_infos[0].description.ice_ufrag =
+ "amungediceufragthisshouldberejected";
+ RTCError error;
+ // Ufrag is not rejected.
+ EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error));
+ EXPECT_THAT(
+ metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"),
+ ElementsAre(Pair(SdpMungingType::kIceUfrag, 1)));
+}
+
+TEST_F(SdpOfferAnswerMungingTest, IceUfragWithCheckDisabledForTesting) {
auto pc = CreatePeerConnection();
+ pc->GetInternalPeerConnection()->DisableSdpMungingChecksForTesting();
pc->AddAudioTrack("audio_track", {});
auto offer = pc->CreateOffer();