Firefox · Core
CVE-2026-117996
Logic Error in Core
Overview
Moderate
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
forthird_party/libwebrtc/video/video_receive_stream2.cc |
modified | |
ifthird_party/libwebrtc/video/video_receive_stream2.cc |
modified | |
TEST_Pthird_party/libwebrtc/video/video_receive_stream2_unittest.cc |
modified |
Files Changed
third_party/libwebrtc/README.mozilla.last-vendorthird_party/libwebrtc/moz-patch-stack/s0037.patchthird_party/libwebrtc/moz-patch-stack/s0041.patchthird_party/libwebrtc/moz-patch-stack/s0056.patchthird_party/libwebrtc/video/video_receive_stream2.ccthird_party/libwebrtc/video/video_receive_stream2.hthird_party/libwebrtc/video/video_receive_stream2_unittest.cc
Patch
diff --git a/third_party/libwebrtc/README.mozilla.last-vendor b/third_party/libwebrtc/README.mozilla.last-vendor
index e6be0752bc7..981fdecbb84 100644
--- a/third_party/libwebrtc/README.mozilla.last-vendor
+++ b/third_party/libwebrtc/README.mozilla.last-vendor
@@ -1,4 +1,4 @@
# ./mach python dom/media/webrtc/third_party_build/loop-ff.py
-libwebrtc updated from /home/apehrson/firefox/firefox/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2026-10-20T11:17:26.444830+00:00.
+libwebrtc updated from /home/apehrson/firefox/firefox/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2026-10-20T11:28:14.527015+00:00.
# base of lastest vendoring
-ce13eaa8e5
+2a990ce0cc
diff --git a/third_party/libwebrtc/moz-patch-stack/s0037.patch b/third_party/libwebrtc/moz-patch-stack/s0037.patch
index 8f219e2c2a3..6c34e547ac0 100644
--- a/third_party/libwebrtc/moz-patch-stack/s0037.patch
+++ b/third_party/libwebrtc/moz-patch-stack/s0037.patch
@@ -53,10 +53,10 @@ index 7b133968cf..b7fa03a961 100644
// Implements RtpVideoFrameReceiver.
void ManageFrame(std::unique_ptr<RtpFrameObject> frame) override;
diff --git a/video/video_receive_stream2.cc b/video/video_receive_stream2.cc
-index e30453cca8..b2d0e40948 100644
+index 1d4f86e090..20eb1ca073 100644
--- a/video/video_receive_stream2.cc
+++ b/video/video_receive_stream2.cc
-@@ -729,6 +729,14 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const {
+@@ -747,6 +747,14 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const {
non_sender_rtt_stats->round_trip_time_measurements;
stats.total_round_trip_time = non_sender_rtt_stats->total_round_trip_time;
}
diff --git a/third_party/libwebrtc/moz-patch-stack/s0041.patch b/third_party/libwebrtc/moz-patch-stack/s0041.patch
index bed9fbdc66f..595cb00a3e7 100644
--- a/third_party/libwebrtc/moz-patch-stack/s0041.patch
+++ b/third_party/libwebrtc/moz-patch-stack/s0041.patch
@@ -153,10 +153,10 @@ index b7fa03a961..f18cdaa55d 100644
private:
// Implements RtpVideoFrameReceiver.
diff --git a/video/video_receive_stream2.cc b/video/video_receive_stream2.cc
-index b2d0e40948..4c998da9f3 100644
+index 20eb1ca073..05fb700936 100644
--- a/video/video_receive_stream2.cc
+++ b/video/video_receive_stream2.cc
-@@ -735,7 +735,8 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const {
+@@ -753,7 +753,8 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const {
// seem to be any support for these stats right now. So, we hack this in.
rtp_video_stream_receiver_.RemoteRTCPSenderInfo(
&stats.rtcp_sender_packets_sent, &stats.rtcp_sender_octets_sent,
diff --git a/third_party/libwebrtc/moz-patch-stack/s0056.patch b/third_party/libwebrtc/moz-patch-stack/s0056.patch
index fd7280b1a68..455b10f08a3 100644
--- a/third_party/libwebrtc/moz-patch-stack/s0056.patch
+++ b/third_party/libwebrtc/moz-patch-stack/s0056.patch
@@ -272,7 +272,7 @@ index f18cdaa55d..68889ae661 100644
// h26x_packet_buffer_ is applicable to H.264 and H.265. For H.265 it is
// always used but for H.264 it is only used if WebRTC-Video-H26xPacketBuffer
diff --git a/video/video_receive_stream2.cc b/video/video_receive_stream2.cc
-index 4c998da9f3..44a22df92f 100644
+index 05fb700936..280b8cbda9 100644
--- a/video/video_receive_stream2.cc
+++ b/video/video_receive_stream2.cc
@@ -272,6 +272,7 @@ VideoReceiveStream2::VideoReceiveStream2(
diff --git a/third_party/libwebrtc/video/video_receive_stream2.cc b/third_party/libwebrtc/video/video_receive_stream2.cc
index 44a22df92f2..280b8cbda9c 100644
--- a/third_party/libwebrtc/video/video_receive_stream2.cc
+++ b/third_party/libwebrtc/video/video_receive_stream2.cc
@@ -575,23 +575,32 @@ VideoReceiveStream2::GetReceiveCodecConfig() const {
return receive_codecs;
}
-void VideoReceiveStream2::RegisterCodecsOnReceiver(
+bool VideoReceiveStream2::RegisterCodecsOnReceiver(
const std::vector<Decoder>& old_decoders,
const std::vector<Decoder>& new_decoders) {
- // Deregister all external decoders from the previous configuration, also
- // the unchanged ones. DeregisterReceiveCodecs() below causes the active
- // decoder to be released before the next frame is decoded, and a released
- // decoder must not be configured again since not all implementations
- // support that (e.g. hardware decoders may fall back to software). New
- // decoder instances get created on demand instead.
+ // Deregister decoders from the previous configuration that are removed or
+ // updated in the new configuration. Unchanged decoders are left as is, so
+ // that a decoder that is in use keeps running. Releasing and configuring it
+ // again would require a keyframe and isn't supported by all implementations
+ // (e.g. hardware decoders may fall back to software).
+ bool removed_decoder_instance = false;
for (const Decoder& old_decoder : old_decoders) {
- video_receiver_.RegisterExternalDecoder(nullptr, old_decoder.payload_type);
+ if (absl::c_linear_search(new_decoders, old_decoder)) {
+ continue;
+ }
+ if (video_receiver_.IsExternalDecoderRegistered(old_decoder.payload_type)) {
+ video_receiver_.RegisterExternalDecoder(nullptr,
+ old_decoder.payload_type);
+ removed_decoder_instance = true;
+ }
+ video_receiver_.DeregisterReceiveCodec(old_decoder.payload_type);
}
- video_receiver_.DeregisterReceiveCodecs();
-
- // Register settings for the new decoder configuration.
+ // Register settings for added or updated decoders.
for (const Decoder& decoder : new_decoders) {
+ if (absl::c_linear_search(old_decoders, decoder)) {
+ continue;
+ }
VideoDecoder::Settings settings;
settings.set_codec_type(
PayloadStringToCodecType(decoder.video_format.name));
@@ -601,6 +610,8 @@ void VideoReceiveStream2::RegisterCodecsOnReceiver(
video_receiver_.RegisterReceiveCodec(decoder.payload_type, settings);
}
+
+ return removed_decoder_instance;
}
void VideoReceiveStream2::ConfigureCodecs() {
@@ -630,19 +641,26 @@ void VideoReceiveStream2::SetDecoders(std::vector<Decoder> decoders) {
// if Stop() is called, it synchronously flushes decode_queue_ before setting
// decoder_running_ to false, avoiding a clash with ConfigureCodecs().
decode_queue_->PostTask([this, old_decoders = std::move(old_decoders),
- new_decoders = config_.decoders]() mutable {
+ new_decoders = config_.decoders,
+ task_safety_flag = task_safety_.flag()]() mutable {
RTC_DCHECK_RUN_ON(&decode_sequence_checker_);
- RegisterCodecsOnReceiver(old_decoders, new_decoders);
+ const bool removed_decoder_instance =
+ RegisterCodecsOnReceiver(old_decoders, new_decoders);
active_decoders_ = std::move(new_decoders);
+ if (!removed_decoder_instance) {
+ return;
+ }
+ // The decoder that was in use has been removed or replaced. Request a
+ // keyframe right away since delta frames currently in the jitter buffer or
+ // arriving before the keyframe will fail to decode due to missing state.
+ call_->worker_thread()->PostTask(
+ SafeTask(std::move(task_safety_flag), [this] {
+ RTC_DCHECK_RUN_ON(&worker_sequence_checker_);
+ RequestKeyFrame(env_.clock().CurrentTime());
+ }));
});
rtp_video_stream_receiver_.SetReceiveCodecs(GetReceiveCodecConfig());
-
- // Request a keyframe immediately because changing the decoder configuration
- // resets the decoder database and underlying decoders. Delta frames currently
- // in the jitter buffer or arriving before the keyframe will fail to decode
- // due to missing state.
- RequestKeyFrame(env_.clock().CurrentTime());
}
void VideoReceiveStream2::SetRawPayloadTypes(std::set<int> raw_payload_types) {
diff --git a/third_party/libwebrtc/video/video_receive_stream2.h b/third_party/libwebrtc/video/video_receive_stream2.h
index 4ff12a7fb15..a55b3488c37 100644
--- a/third_party/libwebrtc/video/video_receive_stream2.h
+++ b/third_party/libwebrtc/video/video_receive_stream2.h
@@ -255,10 +255,12 @@ class VideoReceiveStream2
void UpdateHistograms();
void ConfigureCodecs() RTC_RUN_ON(worker_sequence_checker_);
- // Registers new decoders with the internal VideoReceiver2 database and
- // deregisters all external decoder instances from `old_decoders` so that new
- // instances get created on demand.
- void RegisterCodecsOnReceiver(const std::vector<Decoder>& old_decoders,
+ // Applies the difference between `old_decoders` and `new_decoders` to the
+ // internal VideoReceiver2 database. Removed or updated decoders, including
+ // their external decoder instances, are deregistered and added or updated
+ // decoders are registered. Unchanged decoders are left as is. Returns true if
+ // an external decoder instance was deregistered.
+ bool RegisterCodecsOnReceiver(const std::vector<Decoder>& old_decoders,
const std::vector<Decoder>& new_decoders);
std::vector<RtpVideoStreamReceiver2::ReceiveCodec> GetReceiveCodecConfig()
const RTC_RUN_ON(worker_sequence_checker_);
diff --git a/third_party/libwebrtc/video/video_receive_stream2_unittest.cc b/third_party/libwebrtc/video/video_receive_stream2_unittest.cc
index 87eddc0c390..70b974e40c5 100644
--- a/third_party/libwebrtc/video/video_receive_stream2_unittest.cc
+++ b/third_party/libwebrtc/video/video_receive_stream2_unittest.cc
@@ -899,6 +899,7 @@ TEST_P(VideoReceiveStream2Test,
.AsLast()
.Build());
time_controller_.AdvanceTime(TimeDelta::Millis(100));
+ const int pli_packets = rtcp_packet_parser_.pli()->num_packets();
// Reconfigure PT kH264PayloadType with modified video_format parameters.
VideoReceiveStreamInterface::Decoder new_decoder;
@@ -908,6 +909,9 @@ TEST_P(VideoReceiveStream2Test,
video_receive_stream_->SetDecoders({new_decoder});
time_controller_.AdvanceTime(TimeDelta::Millis(100));
+ // Replacing the decoder that is in use requires a keyframe.
+ EXPECT_EQ(rtcp_packet_parser_.pli()->num_packets(), pli_packets + 1);
+
// Verify that modified format causes old decoder to be deregistered and a
// new decoder created.
EXPECT_CALL(mock_decoder_factory_,
@@ -927,11 +931,11 @@ TEST_P(VideoReceiveStream2Test,
time_controller_.AdvanceTime(TimeDelta::Millis(100));
}
-// Changing the set of decoders while running releases the active decoder.
-// Verify that a new instance is created for an unchanged decoder, instead of
-// configuring the released instance again. Not all decoders support that, e.g.
-// hardware decoders may fall back to software decoding.
-TEST_P(VideoReceiveStream2Test, SetDecodersRecreatesUnchangedDecoder) {
+// Changing the set of decoders while running should not affect an unchanged
+// decoder that is in use. Releasing and configuring it again would require a
+// keyframe and isn't supported by all decoders, e.g. hardware decoders may fall
+// back to software decoding.
+TEST_P(VideoReceiveStream2Test, SetDecodersKeepsUnchangedDecoder) {
video_receive_stream_->Start();
time_controller_.AdvanceTime(TimeDelta::Zero());
@@ -949,6 +953,14 @@ TEST_P(VideoReceiveStream2Test, SetDecodersRecreatesUnchangedDecoder) {
.AsLast()
.Build());
time_controller_.AdvanceTime(TimeDelta::Millis(100));
+ const int pli_packets = rtcp_packet_parser_.pli()->num_packets();
+
+ // Verify that the AV1 decoder keeps decoding without being released,
+ // configured again or recreated.
+ EXPECT_CALL(mock_decoder_factory_, Create).Times(0);
+ EXPECT_CALL(mock_decoder_, Release).Times(0);
+ EXPECT_CALL(mock_decoder_, Configure).Times(0);
+ EXPECT_CALL(mock_decoder_, Decode(_, _));
// Add a decoder while keeping the AV1 decoder unchanged, as happens when the
// receiving side creates an offer listing all of its receive codecs.
@@ -961,21 +973,19 @@ TEST_P(VideoReceiveStream2Test, SetDecodersRecreatesUnchangedDecoder) {
video_receive_stream_->SetDecoders(std::move(decoders));
time_controller_.AdvanceTime(TimeDelta::Millis(100));
- // Verify that a new AV1 decoder is created.
- EXPECT_CALL(mock_decoder_factory_,
- Create(_, Field(&SdpVideoFormat::name, Eq("AV1"))));
- EXPECT_CALL(mock_decoder_, Configure);
- EXPECT_CALL(mock_decoder_, RegisterDecodeCompleteCallback);
- EXPECT_CALL(mock_decoder_, Decode(_, _));
-
video_receive_stream_->OnCompleteFrame(test::FakeFrameBuilder()
.Id(1)
.PayloadType(kAv1PayloadType)
.Time(12345)
.AsLast()
.Build());
-
time_controller_.AdvanceTime(TimeDelta::Millis(100));
+
+ // No keyframe is needed since the decoder state is intact.
+ EXPECT_EQ(rtcp_packet_parser_.pli()->num_packets(), pli_packets);
+
+ // Allow the decoder to be released when the stream is stopped.
+ EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_decoder_));
}
TEST_P(VideoReceiveStream2Test, SetDecodersWhileStoppedUpdatesCodecs) {
Loading diff…
References
On This Page