Guard against redefining a PT within a single codec list.

Bug: webrtc:360058654
Change-Id: I433031a11f40a70cedc3862edb3eee4e94ddbdc9
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/366563
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43318}
This commit is contained in:
Harald Alvestrand 2024-10-25 14:17:02 +00:00 committed by WebRTC LUCI CQ
parent c4f61fbde3
commit 75a106672e
5 changed files with 76 additions and 6 deletions

View File

@ -394,6 +394,7 @@ rtc_library("payload_type_picker") {
"../api/audio_codecs:audio_codecs_api", "../api/audio_codecs:audio_codecs_api",
"../media:codec", "../media:codec",
"../media:media_constants", "../media:media_constants",
"../rtc_base:checks",
"../rtc_base:logging", "../rtc_base:logging",
"../rtc_base:strong_alias", "../rtc_base:strong_alias",
"//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings",

View File

@ -22,6 +22,7 @@
#include "media/base/codec.h" #include "media/base/codec.h"
#include "media/base/codec_comparators.h" #include "media/base/codec_comparators.h"
#include "media/base/media_constants.h" #include "media/base/media_constants.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h" #include "rtc_base/logging.h"
namespace webrtc { namespace webrtc {
@ -178,6 +179,17 @@ RTCError PayloadTypeRecorder::AddMapping(PayloadType payload_type,
auto existing_codec_it = payload_type_to_codec_.find(payload_type); auto existing_codec_it = payload_type_to_codec_.find(payload_type);
if (existing_codec_it != payload_type_to_codec_.end() && if (existing_codec_it != payload_type_to_codec_.end() &&
!MatchesWithCodecRules(codec, existing_codec_it->second)) { !MatchesWithCodecRules(codec, existing_codec_it->second)) {
// Redefinition attempted.
if (disallow_redefinition_level_ > 0) {
if (accepted_definitions_.count(payload_type) > 0) {
// We have already defined this PT in this scope.
RTC_LOG(LS_WARNING)
<< "Rejected attempt to redefine mapping for PT " << payload_type
<< " from " << existing_codec_it->second << " to " << codec;
return RTCError(RTCErrorType::INVALID_MODIFICATION,
"Attempt to redefine a codec mapping");
}
}
if (absl::EqualsIgnoreCase(codec.name, existing_codec_it->second.name)) { if (absl::EqualsIgnoreCase(codec.name, existing_codec_it->second.name)) {
// The difference is in clock rate, channels or FMTP parameters. // The difference is in clock rate, channels or FMTP parameters.
RTC_LOG(LS_INFO) << "Warning: Attempt to change a codec's parameters"; RTC_LOG(LS_INFO) << "Warning: Attempt to change a codec's parameters";
@ -185,15 +197,17 @@ RTCError PayloadTypeRecorder::AddMapping(PayloadType payload_type,
// This is done in production today, so we can't return an error. // This is done in production today, so we can't return an error.
} else { } else {
RTC_LOG(LS_WARNING) << "Warning: You attempted to redefine a codec from " RTC_LOG(LS_WARNING) << "Warning: You attempted to redefine a codec from "
<< existing_codec_it->second.ToString() << " to " << existing_codec_it->second << " to "
<< " new codec " << codec.ToString(); << " new codec " << codec;
// This is a spec violation. // This is a spec violation.
// TODO: https://issues.webrtc.org/41480892 - return an error. // TODO: https://issues.webrtc.org/41480892 - return an error.
} }
// Accept redefinition. // Accept redefinition.
accepted_definitions_.emplace(payload_type);
payload_type_to_codec_.insert_or_assign(payload_type, codec); payload_type_to_codec_.insert_or_assign(payload_type, codec);
return RTCError::OK(); return RTCError::OK();
} }
accepted_definitions_.emplace(payload_type);
payload_type_to_codec_.emplace(payload_type, codec); payload_type_to_codec_.emplace(payload_type, codec);
suggester_.AddMapping(payload_type, codec); suggester_.AddMapping(payload_type, codec);
return RTCError::OK(); return RTCError::OK();
@ -229,6 +243,18 @@ RTCErrorOr<cricket::Codec> PayloadTypeRecorder::LookupCodec(
return result->second; return result->second;
} }
void PayloadTypeRecorder::DisallowRedefinition() {
if (disallow_redefinition_level_ == 0) {
accepted_definitions_.clear();
}
++disallow_redefinition_level_;
}
void PayloadTypeRecorder::ReallowRedefinition() {
RTC_CHECK(disallow_redefinition_level_ > 0);
--disallow_redefinition_level_;
}
void PayloadTypeRecorder::Commit() { void PayloadTypeRecorder::Commit() {
checkpoint_payload_type_to_codec_ = payload_type_to_codec_; checkpoint_payload_type_to_codec_ = payload_type_to_codec_;
} }

View File

@ -17,8 +17,9 @@
#include <vector> #include <vector>
#include "api/rtc_error.h" #include "api/rtc_error.h"
#include "media/base/codec.h"
#include "call/payload_type.h" #include "call/payload_type.h"
#include "media/base/codec.h"
#include "rtc_base/checks.h"
namespace webrtc { namespace webrtc {
@ -57,11 +58,23 @@ class PayloadTypeRecorder {
public: public:
explicit PayloadTypeRecorder(PayloadTypePicker& suggester) explicit PayloadTypeRecorder(PayloadTypePicker& suggester)
: suggester_(suggester) {} : suggester_(suggester) {}
~PayloadTypeRecorder() {
// Ensure consistent use of paired Disallow/ReallowRedefintion calls.
RTC_DCHECK(disallow_redefinition_level_ == 0);
}
RTCError AddMapping(PayloadType payload_type, cricket::Codec codec); RTCError AddMapping(PayloadType payload_type, cricket::Codec codec);
std::vector<std::pair<PayloadType, cricket::Codec>> GetMappings() const; std::vector<std::pair<PayloadType, cricket::Codec>> GetMappings() const;
RTCErrorOr<PayloadType> LookupPayloadType(cricket::Codec codec) const; RTCErrorOr<PayloadType> LookupPayloadType(cricket::Codec codec) const;
RTCErrorOr<cricket::Codec> LookupCodec(PayloadType payload_type) const; RTCErrorOr<cricket::Codec> LookupCodec(PayloadType payload_type) const;
// Redefinition guard.
// In some scenarios, redefinition must be allowed between one offer/answer
// set and the next offer/answer set, but within the processing of one
// SDP, it should never be allowed.
// Implemented as a stack push/pop for convenience; if Disallow has
// been called more times than Reallow, redefinition is prohibited.
void DisallowRedefinition();
void ReallowRedefinition();
// Transaction support. // Transaction support.
// Commit() commits previous changes. // Commit() commits previous changes.
void Commit(); void Commit();
@ -72,6 +85,8 @@ class PayloadTypeRecorder {
PayloadTypePicker& suggester_; PayloadTypePicker& suggester_;
std::map<PayloadType, cricket::Codec> payload_type_to_codec_; std::map<PayloadType, cricket::Codec> payload_type_to_codec_;
std::map<PayloadType, cricket::Codec> checkpoint_payload_type_to_codec_; std::map<PayloadType, cricket::Codec> checkpoint_payload_type_to_codec_;
int disallow_redefinition_level_ = 0;
std::set<PayloadType> accepted_definitions_;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -67,6 +67,24 @@ TEST(PayloadTypePicker, ModifyingPtIsIgnored) {
EXPECT_EQ(result.value(), b_codec); EXPECT_EQ(result.value(), b_codec);
} }
TEST(PayloadTypePicker, ModifyingPtIsAnErrorIfDisallowed) {
PayloadTypePicker picker;
PayloadTypeRecorder recorder(picker);
const PayloadType a_payload_type(123);
cricket::Codec a_codec =
cricket::CreateVideoCodec(cricket::Codec::kIdNotSet, "vp8");
cricket::Codec b_codec =
cricket::CreateVideoCodec(cricket::Codec::kIdNotSet, "vp9");
recorder.DisallowRedefinition();
recorder.AddMapping(a_payload_type, a_codec);
auto error = recorder.AddMapping(a_payload_type, b_codec);
EXPECT_FALSE(error.ok());
auto result = recorder.LookupCodec(a_payload_type);
// Attempted redefinition should be ignored.
EXPECT_EQ(result.value(), a_codec);
recorder.ReallowRedefinition();
}
TEST(PayloadTypePicker, RollbackAndCommit) { TEST(PayloadTypePicker, RollbackAndCommit) {
PayloadTypePicker picker; PayloadTypePicker picker;
PayloadTypeRecorder recorder(picker); PayloadTypeRecorder recorder(picker);

View File

@ -377,19 +377,29 @@ webrtc::RTCError JsepTransport::RecordPayloadTypes(bool local,
webrtc::SdpType type, webrtc::SdpType type,
const ContentInfo& content) { const ContentInfo& content) {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
if (local) {
local_payload_types_.DisallowRedefinition();
} else {
remote_payload_types_.DisallowRedefinition();
}
webrtc::RTCError result = webrtc::RTCError::OK();
for (auto codec : content.media_description()->codecs()) { for (auto codec : content.media_description()->codecs()) {
webrtc::RTCError result;
if (local) { if (local) {
result = local_payload_types_.AddMapping(codec.id, codec); result = local_payload_types_.AddMapping(codec.id, codec);
} else { } else {
result = remote_payload_types_.AddMapping(codec.id, codec); result = remote_payload_types_.AddMapping(codec.id, codec);
} }
if (!result.ok()) { if (!result.ok()) {
break;
}
}
if (local) {
local_payload_types_.ReallowRedefinition();
} else {
remote_payload_types_.ReallowRedefinition();
}
return result; return result;
} }
}
return webrtc::RTCError::OK();
}
void JsepTransport::SetRemoteIceParameters( void JsepTransport::SetRemoteIceParameters(
const IceParameters& ice_parameters, const IceParameters& ice_parameters,