Medium firefox Sandbox Escape 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionInformation disclosure, sandbox escape in the Security: Process Sandboxing component
ComponentSecurity/NSS
Bug ClassSandbox Escape
Tracker2040177
Fix commit6bd940d2c486 (firefox) +20/-2
CISA KEVNot listed
CreditedYaqoub Aldurayhim
Disclosed2026-06-16

Background

`SandboxBroker`
a trusted parent-process broker that services filesystem requests from sandboxed Linux child processes over a socket, applying a per-child access policy.
`Operation`
the enum tag on a broker request identifying the syscall being brokered (e.g. SANDBOX_FILE_LINK, SANDBOX_FILE_RENAME), some of which involve two paths and most only one.
`FORCE_DENY` / `CRASH_INSTEAD`
negative permission bits in the policy that cause a request to be refused (or the child to be crashed) rather than granting any access.
`mPolicy->Lookup`
the call that maps a resolved path string to its permission bitmask under the child’s sandbox policy.

Root Cause Analysis

In SandboxBroker::ThreadMain, a request carrying a non-empty second path had that path’s permissions computed via mPolicy->Lookup and combined with the first path’s permissions using a plain intersection, perms &= perms2. That intersection is correct for positive access bits (both paths must be permitted) but wrong for the negative bits FORCE_DENY and CRASH_INSTEAD, because AND-ing clears a deny bit whenever it is set on only one of the two paths, effectively cancelling a denial that should have stood. Additionally, the code processed a second path for any operation, even single-path operations that legitimately supply only one path, so a malicious child could attach an unexpected second path to influence the permission result.

The fix rejects extra paths for operations whose OperationPaths(req.mOp) is less than 2 by logging, shutting down the read side, and breaking, and it changes the combination to perms = (perms & perms2) | ((perms | perms2) & kNegPerms) so deny bits are unioned (kept if present on either path) while positive bits are still intersected. This restores the invariant that a denial on either path denies the whole operation and that only two-path operations may carry two paths.

Key insight
The single mistake was treating deny bits like positive access bits and intersecting them, so pairing a denied path with an allowed one erased the denial; the fix unions the negative bits (kNegPerms) separately and refuses second paths on single-path operations.

Attack Path

  1. Craft a two-path request A compromised or malicious content process sends a brokered file request (e.g. rename/link, or a single-path op with an injected extra path) to the parent SandboxBroker.
  2. Pair a denied path with an allowed path One path resolves to a policy entry carrying FORCE_DENY/CRASH_INSTEAD while the other resolves to an allowed entry.
  3. Cancel the denial via intersection The old perms &= perms2 AND-s the two masks, clearing the deny bit because it is set on only one path, yielding a net-permissive result.
  4. Perform the brokered operation The broker executes the filesystem action on the path that policy intended to deny, disclosing or manipulating files outside the sandbox’s allowed set.

Impact Assessment

An attacker who already controls a sandboxed child process can bypass filesystem denials enforced by the parent broker, gaining read/write access to paths the sandbox policy marks FORCE_DENY, which amounts to information disclosure and a sandbox escape from the child’s confinement. The action runs in the trusted parent broker context on behalf of the child, so it reaches files outside the child’s permitted set. The precondition is prior compromise (or malicious control) of a sandboxed process capable of issuing broker requests.

Changed Functions

FunctionChangeNotes
if
security/sandbox/linux/broker/SandboxBroker.cpp
modified
switch
security/sandbox/linux/broker/SandboxBrokerCommon.h
modified

Files Changed

  • security/sandbox/linux/broker/SandboxBroker.cpp
  • security/sandbox/linux/broker/SandboxBrokerCommon.h

Audit Directions

  • Negative permission bits under set-combination
    Audit every place that merges permission masks (AND/OR) and confirm deny/crash bits are unioned, not intersected, so a denial on any input is never silently cleared.
  • Per-operation input arity
    Verify broker request handlers validate the number and shape of attacker-controlled fields (extra paths, extra descriptors) against what each Operation legitimately expects, rejecting surplus data.
  • Multi-path privilege decisions
    Review link/rename and other multi-argument brokered operations to ensure the access decision reflects the least-privileged of all involved paths.
diff --git a/security/sandbox/linux/broker/SandboxBroker.cpp b/security/sandbox/linux/broker/SandboxBroker.cpp
index 886a815b507..d9843e3c9ea 100644
--- a/security/sandbox/linux/broker/SandboxBroker.cpp
+++ b/security/sandbox/linux/broker/SandboxBroker.cpp
@@ -820,13 +820,21 @@ void SandboxBroker::ThreadMain(void) {
       // Same for the second path.
       pathLen2 = strnlen(pathBuf2, kMaxPathLen);
       if (pathLen2 > 0) {
+        if (OperationPaths(req.mOp) < 2) {
+          SANDBOX_LOG("extra path for op %s from pid %d",
+                      OperationDescription(req.mOp), mChildPid);
+          shutdown(mFileDesc, SHUT_RD);
+          break;
+        }
         // Force 0 termination.
         pathBuf2[pathLen2] = '\0';
         pathLen2 = ConvertRelativePath(pathBuf2, sizeof(pathBuf2), pathLen2);
         int perms2 = mPolicy->Lookup(nsDependentCString(pathBuf2, pathLen2));
 
-        // Take the intersection of the permissions for both paths.
-        perms &= perms2;
+        // Take the intersection of the permissions for both paths
+        // (the bits which cause denials need to be handled specially).
+        constexpr int kNegPerms = FORCE_DENY | CRASH_INSTEAD;
+        perms = (perms & perms2) | ((perms | perms2) & kNegPerms);
       }
     } else {
       // Failed to receive intelligible paths.
diff --git a/security/sandbox/linux/broker/SandboxBrokerCommon.h b/security/sandbox/linux/broker/SandboxBrokerCommon.h
index 1bdae7a3c38..f9ab26a2578 100644
--- a/security/sandbox/linux/broker/SandboxBrokerCommon.h
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.h
@@ -54,6 +54,16 @@ class SandboxBrokerCommon {
            static_cast<unsigned>(SANDBOX_OP_MAX_VALUE);
   }
 
+  static int OperationPaths(Operation aOp) {
+    switch (aOp) {
+      case SANDBOX_FILE_LINK:
+      case SANDBOX_FILE_RENAME:
+        return 2;
+      default:
+        return 1;
+    }
+  }
+
   static unsigned OperationToInt(Operation);
   static const char* OperationDescription(Operation);
 
Loading diff…