diff --git a/pc/webrtc_sdp.cc b/pc/webrtc_sdp.cc index ea433583ab..1069a18391 100644 --- a/pc/webrtc_sdp.cc +++ b/pc/webrtc_sdp.cc @@ -2341,6 +2341,7 @@ static bool ParseMsidAttribute(absl::string_view line, // Note that JSEP stipulates not sending msid-appdata so // a=msid: // is supported for backward compability reasons only. + // RFC 8830 section 2 states that duplicate a=msid:stream track is illegal. std::vector fields; size_t num_fields = rtc::tokenize(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields); @@ -2670,6 +2671,21 @@ static std::unique_ptr ParseContentDescription( return media_desc; } +bool HasDuplicateMsidLines(cricket::SessionDescription* desc) { + std::set> seen_msids; + for (const cricket::ContentInfo& content : desc->contents()) { + for (const cricket::StreamParams& stream : + content.media_description()->streams()) { + auto msid = std::pair(stream.first_stream_id(), stream.id); + if (seen_msids.find(msid) != seen_msids.end()) { + return true; + } + seen_msids.insert(std::move(msid)); + } + } + return false; +} + bool ParseMediaDescription( absl::string_view message, const TransportDescription& session_td, @@ -2852,6 +2868,11 @@ bool ParseMediaDescription( // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag". desc->AddTransportInfo(TransportInfo(content_name, transport)); } + // Apply whole-description sanity checks + if (HasDuplicateMsidLines(desc)) { + ParseFailed(message, *pos, "Duplicate a=msid lines detected", error); + return false; + } desc->set_msid_signaling(msid_signaling); diff --git a/pc/webrtc_sdp_unittest.cc b/pc/webrtc_sdp_unittest.cc index a31aa2adef..999f0b6b26 100644 --- a/pc/webrtc_sdp_unittest.cc +++ b/pc/webrtc_sdp_unittest.cc @@ -4271,6 +4271,27 @@ TEST_F(WebRtcSdpTest, DeserializeMsidAttributeWithMissingStreamId) { EXPECT_FALSE(SdpDeserialize(sdp, &jdesc_output)); } +TEST_F(WebRtcSdpTest, DeserializeMsidAttributeWithDuplicateStreamIdAndTrackId) { + std::string sdp = + "v=0\r\n" + "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "m=audio 9 RTP/SAVPF 111\r\n" + "a=mid:0\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtpmap:111 opus/48000/2\r\n" + "a=msid:stream_id track_id\r\n" + "m=audio 9 RTP/SAVPF 111\r\n" + "a=mid:1\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtpmap:111 opus/48000/2\r\n" + "a=msid:stream_id track_id\r\n"; + + JsepSessionDescription jdesc_output(kDummyType); + EXPECT_FALSE(SdpDeserialize(sdp, &jdesc_output)); +} + // Tests that if both session-level address and media-level address exist, use // the media-level address. TEST_F(WebRtcSdpTest, ParseConnectionData) {