High firefox Memory Corruption 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionInternally found bugs present in Firefox ESR 140.13, Firefox ESR 153.0 and Firefox 153. Some of these bugs showed evidence of memory corruption or another security-relevant defect and we presume that with enough effort some of these could have been exploited.
ComponentSecurity/NSS
Bug ClassMemory Corruption
Tracker2048797
Fix commitf2be9cfab86e (firefox) +33/-1
CISA KEVNot listed
CreditedNicolas Silva, Tom Ritter and the Mozilla Fuzzing Team
Disclosed2026-08-18

Background

`SandboxBroker`
A trusted broker process that mediates filesystem syscalls on behalf of sandboxed content processes on Linux.
`realpath`/`SymlinkPath`
The broker’s path-canonicalization routine that resolves ., .., and symlinks so a requested path can be checked against the sandbox policy.
`readlink`
A syscall that reads a symlink’s target string, which the broker calls during path resolution before enforcing permissions.
`PROC_SUPER_MAGIC`
The statfs filesystem-type identifier for procfs, whose “magic” symlinks resolve to arbitrary kernel-managed targets.

Root Cause Analysis

During policy enforcement, SandboxBroker::SymlinkPath called readlink on a caller-controlled path to canonicalize it before deciding whether access was permitted. If that path pointed into /proc, the resolved entry could be a procfs “magic” symlink (for example /proc/self/fd/N or /proc/self/root), which the kernel resolves dynamically to a target the sandbox policy never intended to expose, letting the broker’s canonicalization be steered toward out-of-policy paths. The invariant violated is that path resolution inside the trusted broker must operate on ordinary filesystem symlinks whose textual target is stable and policy-checkable, not on kernel-synthesized procfs links.

The fix introduces SafeReadlink, which rejects any path beginning with /proc/, then opens the node with O_PATH | O_NOFOLLOW and calls fstatfs to confirm the object is not on a filesystem whose f_type equals PROC_SUPER_MAGIC before reading the link via readlinkat on the file descriptor itself. Operating on an O_PATH descriptor and re-reading through readlinkat(fd, "", ...) closes the resolution against a fixed object rather than re-walking the attacker-controlled path string. This works because both a fast string prefix check and an authoritative filesystem-type check must pass before any link target is trusted, denying procfs-backed symlinks entirely.

Key insight
The core mistake was resolving symlinks in the trusted broker with a plain readlink on an attacker-supplied path without excluding procfs, whose magic symlinks resolve to arbitrary kernel targets; the fix gates resolution behind an explicit /proc/ prefix rejection plus an fstatfs/PROC_SUPER_MAGIC filesystem-type check on a stable O_PATH descriptor.

Attack Path

  1. Compromise a content process An attacker gains code execution inside a sandboxed content/renderer process that can send filesystem broker requests.
  2. Craft a procfs-backed path The attacker submits a path that resolves through a procfs “magic” symlink such as /proc/self/fd/N or /proc/self/root.
  3. Subvert canonicalization The broker’s SymlinkPath calls readlink on that entry, and the kernel returns a dynamically resolved target outside the intended policy scope.
  4. Bypass policy checks Because the canonicalized path no longer reflects the caller’s textual request, the broker evaluates permissions against the wrong or attacker-influenced target.
  5. Reach out-of-policy resources The attacker leverages the mis-resolved path to access files the sandbox policy was meant to deny.

Impact Assessment

An attacker who already controls a sandboxed content process can drive the trusted broker’s path resolution through procfs magic symlinks to escape the intended filesystem policy, reaching files outside the sandbox’s allowed set. The precondition is prior code execution in a content process capable of issuing broker filesystem requests on Linux. The metadata classifies this as a high-severity memory-corruption-class defect with presumed exploitability, so the practical gain is a sandbox filesystem-policy escape from the constrained process context.

Changed Functions

FunctionChangeNotes
if
security/sandbox/linux/broker/SandboxBrokerRealpath.cpp
modified

Files Changed

  • security/sandbox/linux/broker/SandboxBrokerRealpath.cpp

Audit Directions

  • procfs magic symlinks
    Audit every trusted-process path resolver for readlink/realpath on caller-controlled paths that could traverse /proc, and require both a prefix check and an fstatfs f_type check before trusting a link target.
  • Path re-resolution TOCTOU
    Prefer resolving against a stable O_PATH descriptor with readlinkat(fd, "", ...) over re-walking a path string, so the object checked is the object read.
  • Broker policy boundaries
    Review other SandboxBroker operations that canonicalize or stat paths to confirm they enforce filesystem-type restrictions consistently, not just this one call site.
diff --git a/security/sandbox/linux/broker/SandboxBrokerRealpath.cpp b/security/sandbox/linux/broker/SandboxBrokerRealpath.cpp
index b910264a70f..27656540844 100644
--- a/security/sandbox/linux/broker/SandboxBrokerRealpath.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerRealpath.cpp
@@ -36,15 +36,19 @@
 static char sccsid[] = "@(#)realpath.c	8.1 (Berkeley) 2/16/94";
 #endif /* LIBC_SCCS and not lint */
 #include <errno.h>
+#include <fcntl.h>
+#include <linux/magic.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/param.h>
 #include <sys/stat.h>
+#include <sys/vfs.h>
 #include <unistd.h>
 
 #include "SandboxBroker.h"
 #include "SandboxLogging.h"
 #include "base/strings/string_util.h"
+#include "mozilla/UniquePtrExtensions.h"
 
 // Original copy in, but not usable from here:
 // toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc
@@ -60,6 +64,34 @@ static size_t my_strlcat(char* s1, const char* s2, size_t len) {
 
 namespace mozilla {
 
+static ssize_t SafeReadlink(const char* __restrict path, char* __restrict buf,
+                            size_t bufsiz) {
+  // In theory this is subsumed by the f_type check, but just in case
+  if (strncmp(path, "/proc/", 6) == 0) {
+    errno = EPERM;
+    return -1;
+  }
+
+  // Linux extension: open any filesystem node, including a symlink
+  UniqueFileHandle fd{open(path, O_PATH | O_NOFOLLOW)};
+  if (!fd) {
+    return -1;
+  }
+
+  // Check the filesystem info for the symlink (not its target)
+  struct statfs sf;
+  if (fstatfs(fd.get(), &sf) != 0) {
+    return -1;
+  }
+  if (sf.f_type == PROC_SUPER_MAGIC) {
+    errno = EPERM;
+    return -1;
+  }
+
+  // Linux extension: readlink the object referenced by the fd itself
+  return readlinkat(fd.get(), "", buf, bufsiz);
+}
+
 /*
  * Original: realpath
  * Find the real name of path, by removing all ".", ".." and symlink
@@ -223,7 +255,7 @@ char* SandboxBroker::SymlinkPath(const Policy* policy,
         *perms |= link_path_perms;
       }
       /* Original symlink lookup code */
-      slen = readlink(resolved, symlink, sizeof(symlink) - 1);
+      slen = SafeReadlink(resolved, symlink, sizeof(symlink) - 1);
       if (slen < 0) {
         if (m) free(resolved);
         return (nullptr);
Loading diff…