Low firefox Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionPrivilege escalation in the WebRTC: Audio/Video component
ComponentDOM
Bug ClassLogic Error
Tracker2033275
Fix commit8eb3dbb90545 (firefox) +34/-8
CISA KEVNot listed
Creditedpakhunov.anton.n
Disclosed2026-05-19

Changed Functions

FunctionChangeNotes
for
dom/media/systemservices/CamerasParent.cpp
modified
if
dom/media/systemservices/CamerasParent.cpp
modified

Files Changed

  • dom/media/systemservices/CamerasParent.cpp
  • dom/media/systemservices/CamerasParent.h
diff --git a/dom/media/systemservices/CamerasParent.cpp b/dom/media/systemservices/CamerasParent.cpp
index c0604297ac4..8035a37ee4c 100644
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -26,6 +26,7 @@
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "mozilla/media/DesktopCaptureInterface.h"
 #include "mozilla/media/MediaUtils.h"
+#include "nsAppRunner.h"
 #include "nsIPermissionManager.h"
 #include "nsIThread.h"
 #include "nsNetUtil.h"
@@ -485,10 +486,11 @@ auto AggregateCapturer::RemoveStreamsFor(CamerasParent* aParent)
           .mNumRemainingStreamsForParent = 0};
 }
 
-Maybe<int> AggregateCapturer::CaptureIdFor(int aStreamId) {
+Maybe<int> AggregateCapturer::CaptureIdFor(int aStreamId,
+                                           CamerasParent* aParent) {
   auto streamsGuard = mStreams.Lock();
   for (auto& stream : *streamsGuard) {
-    if (stream->mId == aStreamId) {
+    if (stream->mId == aStreamId && stream->mParent == aParent) {
       return Some(mCaptureId);
     }
   }
@@ -1217,11 +1219,28 @@ ipc::IPCResult CamerasParent::RecvAllocateCapture(
 
   LOG("CamerasParent(%p)::%s: Verifying permissions", this, __func__);
 
-  using Promise1 = MozPromise<bool, bool, true>;
+  PBackgroundParent* manager = Manager();
+  MOZ_ASSERT_IF(!manager, RunningGTest());
+  RefPtr<dom::ThreadsafeContentParentHandle> contentParent =
+      manager ? ipc::BackgroundParent::GetContentParentHandle(manager)
+              : nullptr;
+
+  enum class CapturePermission { Invalid, Allowed, NotAllowed };
+  using Promise1 = MozPromise<CapturePermission, bool, true>;
   using Promise2 = MozPromise<Maybe<int>, bool, true>;
   InvokeAsync(
       GetMainThreadSerialEventTarget(), __func__,
-      [aWindowID] {
+      [aWindowID, contentParent] {
+        if (contentParent) {
+          RefPtr<dom::WindowGlobalParent> wgp =
+              dom::WindowGlobalParent::GetByInnerWindowId(aWindowID);
+          if (!wgp || wgp->ContentParentId() != contentParent->ChildID()) {
+            LOG("Window does not belong to sending process");
+            return Promise1::CreateAndResolve(
+                CapturePermission::Invalid,
+                "CamerasParent::RecvAllocateCapture");
+          }
+        }
         // Verify whether the claimed origin has received permission
         // to use the camera, either persistently or this session (one
         // shot).
@@ -1235,7 +1254,9 @@ ipc::IPCResult CamerasParent::RecvAllocateCapture(
         if (!allowed) {
           LOG("No camera permission for this origin");
         }
-        return Promise1::CreateAndResolve(allowed,
+        return Promise1::CreateAndResolve(allowed
+                                              ? CapturePermission::Allowed
+                                              : CapturePermission::NotAllowed,
                                           "CamerasParent::RecvAllocateCapture");
       })
       ->Then(mVideoCaptureThread, __func__,
@@ -1247,7 +1268,12 @@ ipc::IPCResult CamerasParent::RecvAllocateCapture(
                  return Promise2::CreateAndResolve(
                      Nothing(), "CamerasParent::RecvAllocateCapture no engine");
                }
-               bool allowed = aValue.ResolveValue();
+               CapturePermission permission = aValue.ResolveValue();
+               if (permission == CapturePermission::Invalid) {
+                 return Promise2::CreateAndResolve(
+                     Nothing(), "CamerasParent::RecvAllocateCapture");
+               }
+               bool allowed = permission == CapturePermission::Allowed;
                if (!allowed && IsWindowCapturing(aWindowID, unique_id)) {
                  allowed = true;
                  LOG("No permission but window is already capturing this "
@@ -1476,7 +1502,7 @@ AggregateCapturer* CamerasParent::GetAggregator(CaptureEngine aEngine,
     if (aggregator->mCapEngine != aEngine) {
       continue;
     }
-    Maybe captureId = aggregator->CaptureIdFor(aStreamId);
+    Maybe captureId = aggregator->CaptureIdFor(aStreamId, this);
     if (captureId) {
       return aggregator.get();
     }
diff --git a/dom/media/systemservices/CamerasParent.h b/dom/media/systemservices/CamerasParent.h
index 3aea3ba9bb0..86d447ad7c6 100644
--- a/dom/media/systemservices/CamerasParent.h
+++ b/dom/media/systemservices/CamerasParent.h
@@ -66,7 +66,7 @@ class AggregateCapturer final
   };
   RemoveStreamResult RemoveStream(int aStreamId);
   RemoveStreamResult RemoveStreamsFor(CamerasParent* aParent);
-  Maybe<int> CaptureIdFor(int aStreamId);
+  Maybe<int> CaptureIdFor(int aStreamId, CamerasParent* aParent);
   int32_t StartStream(int aStreamId,
                       const webrtc::VideoCaptureCapability& aCapability,
                       const NormalizedConstraints& aConstraints,
Loading diff…