Firefox · DOM
CVE-2026-4704
Logic Error in DOM
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifdom/media/webrtc/tests/crashtests/2014868.html |
modified | |
fordom/media/webrtc/tests/crashtests/2014868.html |
modified |
Files Changed
dom/media/webrtc/jsep/JsepSessionImpl.cppdom/media/webrtc/tests/crashtests/2014868.htmldom/media/webrtc/tests/crashtests/crashtests.list
Patch
diff --git a/dom/media/webrtc/jsep/JsepSessionImpl.cpp b/dom/media/webrtc/jsep/JsepSessionImpl.cpp
index 709cc2f7808..c20ee9afb20 100644
--- a/dom/media/webrtc/jsep/JsepSessionImpl.cpp
+++ b/dom/media/webrtc/jsep/JsepSessionImpl.cpp
@@ -1919,17 +1919,17 @@ nsresult JsepSessionImpl::ValidateRemoteDescription(const Sdp& description) {
const SdpMediaSection& oldMsection =
mCurrentRemoteDescription->GetMediaSection(i);
- if (mSdpHelper.MsectionIsDisabled(newMsection) ||
- mSdpHelper.MsectionIsDisabled(oldMsection)) {
- continue;
- }
-
if (oldMsection.GetMediaType() != newMsection.GetMediaType()) {
JSEP_SET_ERROR("Remote description changes the media type of m-line "
<< i);
return NS_ERROR_INVALID_ARG;
}
+ if (mSdpHelper.MsectionIsDisabled(newMsection) ||
+ mSdpHelper.MsectionIsDisabled(oldMsection)) {
+ continue;
+ }
+
bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
if (mIsPendingOfferer.isSome() && *mIsPendingOfferer && differ &&
diff --git a/dom/media/webrtc/tests/crashtests/2014868.html b/dom/media/webrtc/tests/crashtests/2014868.html
new file mode 100644
index 00000000000..6ccf59568a0
--- /dev/null
+++ b/dom/media/webrtc/tests/crashtests/2014868.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <meta charset="utf-8">
+ <title>Bug 2014868 - Reject SDP when MID changes at existing m-line index</title>
+</head>
+<body>
+<script>
+async function run() {
+ try {
+ const pc1 = new RTCPeerConnection();
+ const pc2 = new RTCPeerConnection();
+
+ pc1.addTransceiver("audio", { direction: "sendrecv" });
+ pc1.addTransceiver("video", { direction: "recvonly" });
+ pc1.createDataChannel("test");
+
+ let offer = await pc1.createOffer();
+ await pc1.setLocalDescription(offer);
+ await pc2.setRemoteDescription(offer);
+ let answer = await pc2.createAnswer();
+ await pc2.setLocalDescription(answer);
+ await pc1.setRemoteDescription(answer);
+
+ // Stop video transceiver to trigger recycling logic
+ for (const t of pc1.getTransceivers()) {
+ if (t.receiver.track.kind === "video") {
+ t.stop();
+ }
+ }
+
+ offer = await pc1.createOffer();
+ await pc1.setLocalDescription(offer);
+ await pc2.setRemoteDescription(offer);
+ answer = await pc2.createAnswer();
+ await pc2.setLocalDescription(answer);
+ await pc1.setRemoteDescription(answer);
+
+ // Munge the offer: insert a duplicate video m-line at the application m-line
+ // position. This displaces the application section and creates a media type
+ // mismatch at that index, testing that we reject invalid type changes.
+ const sdpLines = offer.sdp.split("\r\n");
+ let mLineCount = 0;
+ let insertIndex = -1;
+ for (let i = 0; i < sdpLines.length; i++) {
+ if (sdpLines[i].startsWith("m=")) {
+ mLineCount++;
+ if (mLineCount === 2) {
+ insertIndex = i;
+ }
+ }
+ }
+
+ // Duplicate the second m-line section with a changed MID
+ if (insertIndex !== -1) {
+ let endIndex = insertIndex + 1;
+ for (let i = insertIndex + 1; i < sdpLines.length; i++) {
+ if (sdpLines[i].startsWith("m=")) {
+ endIndex = i;
+ break;
+ }
+ }
+ if (endIndex === sdpLines.length) endIndex = sdpLines.length;
+
+ const mlineSection = sdpLines.slice(insertIndex, endIndex);
+ // Change the MID in the copied section
+ for (let i = 0; i < mlineSection.length; i++) {
+ if (mlineSection[i].startsWith("a=mid:")) {
+ mlineSection[i] = "a=mid:changed-mid";
+ break;
+ }
+ }
+ sdpLines.splice(endIndex, 0, ...mlineSection);
+ }
+
+ const modifiedAnswer = { type: "offer", sdp: sdpLines.join("\r\n") };
+ await pc1.setRemoteDescription(modifiedAnswer);
+
+ pc1.close();
+ pc2.close();
+ } catch (e) {
+ }
+
+ document.documentElement.removeAttribute("class");
+}
+
+run();
+</script>
+</body>
+</html>
diff --git a/dom/media/webrtc/tests/crashtests/crashtests.list b/dom/media/webrtc/tests/crashtests/crashtests.list
index f2c3444137e..26774133e8d 100644
--- a/dom/media/webrtc/tests/crashtests/crashtests.list
+++ b/dom/media/webrtc/tests/crashtests/crashtests.list
@@ -16,4 +16,5 @@ skip-if(Android) skip-if(cocoaWidget&&/^15\.30$/.test(os_version)) load 1991492_
skip-if(Android) skip-if(cocoaWidget&&/^15\.30$/.test(os_version)) load 1991494.html # No screenshare on Android, macOS: bug 1984994
pref(media.navigator.streams.fake,false) pref(media.getusermedia.camera.fake.force,true) load 2009260.html
pref(media.navigator.permission.device,true) pref(media.audio_loopback_dev,"real-device-request-with-clear-cache") load getUserMedia-audio.html # bug 1767893
+load 2014868.html
load pc-video-size1.html
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/dom/media/webrtc/tests/crashtests/2014868.html b/dom/media/webrtc/tests/crashtests/2014868.html
new file mode 100644
index 00000000000..6ccf59568a0
--- /dev/null
+++ b/dom/media/webrtc/tests/crashtests/2014868.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <meta charset="utf-8">
+ <title>Bug 2014868 - Reject SDP when MID changes at existing m-line index</title>
+</head>
+<body>
+<script>
+async function run() {
+ try {
+ const pc1 = new RTCPeerConnection();
+ const pc2 = new RTCPeerConnection();
+
+ pc1.addTransceiver("audio", { direction: "sendrecv" });
+ pc1.addTransceiver("video", { direction: "recvonly" });
+ pc1.createDataChannel("test");
+
+ let offer = await pc1.createOffer();
+ await pc1.setLocalDescription(offer);
+ await pc2.setRemoteDescription(offer);
+ let answer = await pc2.createAnswer();
+ await pc2.setLocalDescription(answer);
+ await pc1.setRemoteDescription(answer);
+
+ // Stop video transceiver to trigger recycling logic
+ for (const t of pc1.getTransceivers()) {
+ if (t.receiver.track.kind === "video") {
+ t.stop();
+ }
+ }
+
+ offer = await pc1.createOffer();
+ await pc1.setLocalDescription(offer);
+ await pc2.setRemoteDescription(offer);
+ answer = await pc2.createAnswer();
+ await pc2.setLocalDescription(answer);
+ await pc1.setRemoteDescription(answer);
+
+ // Munge the offer: insert a duplicate video m-line at the application m-line
+ // position. This displaces the application section and creates a media type
+ // mismatch at that index, testing that we reject invalid type changes.
+ const sdpLines = offer.sdp.split("\r\n");
+ let mLineCount = 0;
+ let insertIndex = -1;
+ for (let i = 0; i < sdpLines.length; i++) {
+ if (sdpLines[i].startsWith("m=")) {
+ mLineCount++;
+ if (mLineCount === 2) {
+ insertIndex = i;
+ }
+ }
+ }
+
+ // Duplicate the second m-line section with a changed MID
+ if (insertIndex !== -1) {
+ let endIndex = insertIndex + 1;
+ for (let i = insertIndex + 1; i < sdpLines.length; i++) {
+ if (sdpLines[i].startsWith("m=")) {
+ endIndex = i;
+ break;
+ }
+ }
+ if (endIndex === sdpLines.length) endIndex = sdpLines.length;
+
+ const mlineSection = sdpLines.slice(insertIndex, endIndex);
+ // Change the MID in the copied section
+ for (let i = 0; i < mlineSection.length; i++) {
+ if (mlineSection[i].startsWith("a=mid:")) {
+ mlineSection[i] = "a=mid:changed-mid";
+ break;
+ }
+ }
+ sdpLines.splice(endIndex, 0, ...mlineSection);
+ }
+
+ const modifiedAnswer = { type: "offer", sdp: sdpLines.join("\r\n") };
+ await pc1.setRemoteDescription(modifiedAnswer);
+
+ pc1.close();
+ pc2.close();
+ } catch (e) {
+ }
+
+ document.documentElement.removeAttribute("class");
+}
+
+run();
+</script>
+</body>
+</html>
diff --git a/dom/media/webrtc/tests/crashtests/crashtests.list b/dom/media/webrtc/tests/crashtests/crashtests.list
index f2c3444137e..26774133e8d 100644
--- a/dom/media/webrtc/tests/crashtests/crashtests.list
+++ b/dom/media/webrtc/tests/crashtests/crashtests.list
@@ -16,4 +16,5 @@ skip-if(Android) skip-if(cocoaWidget&&/^15\.30$/.test(os_version)) load 1991492_
skip-if(Android) skip-if(cocoaWidget&&/^15\.30$/.test(os_version)) load 1991494.html # No screenshare on Android, macOS: bug 1984994
pref(media.navigator.streams.fake,false) pref(media.getusermedia.camera.fake.force,true) load 2009260.html
pref(media.navigator.permission.device,true) pref(media.audio_loopback_dev,"real-device-request-with-clear-cache") load getUserMedia-audio.html # bug 1767893
+load 2014868.html
load pc-video-size1.html
Loading diff…
References
On This Page