Firefox · Security/NSS
CVE-2026-12296
Sandbox Escape in Security/NSS
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsecurity/sandbox/linux/SandboxFilter.cpp |
modified | |
ifsecurity/sandbox/linux/broker/SandboxBroker.cpp |
modified |
Files Changed
security/sandbox/common/test/SandboxTestingChildTests.hsecurity/sandbox/linux/SandboxBrokerClient.cppsecurity/sandbox/linux/SandboxBrokerClient.hsecurity/sandbox/linux/SandboxFilter.cppsecurity/sandbox/linux/broker/SandboxBroker.cppsecurity/sandbox/linux/broker/SandboxBrokerCommon.cppsecurity/sandbox/linux/broker/SandboxBrokerCommon.hsecurity/sandbox/linux/gtest/TestBroker.cppsecurity/sandbox/test/browser_content_sandbox_fs_tests.js
Patch
diff --git a/security/sandbox/common/test/SandboxTestingChildTests.h b/security/sandbox/common/test/SandboxTestingChildTests.h
index a42eefea587..9dae8d40761 100644
--- a/security/sandbox/common/test/SandboxTestingChildTests.h
+++ b/security/sandbox/common/test/SandboxTestingChildTests.h
@@ -627,6 +627,12 @@ void RunTestsContent(SandboxTestingChild* child) {
return fd;
});
+ child->ErrnoValueTest("symlink"_ns, EPERM,
+ [] { return symlink("something", "/tmp/testlink"); });
+ child->ErrnoValueTest("symlinkat"_ns, EPERM, [] {
+ return symlinkat("something", AT_FDCWD, "/tmp/testlink");
+ });
+
# endif // XP_LINUX
# ifdef XP_MACOSX
diff --git a/security/sandbox/linux/SandboxBrokerClient.cpp b/security/sandbox/linux/SandboxBrokerClient.cpp
index d8091602974..44672eff5b8 100644
--- a/security/sandbox/linux/SandboxBrokerClient.cpp
+++ b/security/sandbox/linux/SandboxBrokerClient.cpp
@@ -205,11 +205,6 @@ int SandboxBrokerClient::Link(const char* aOldPath, const char* aNewPath) {
return DoCall(&req, aOldPath, aNewPath, nullptr, false);
}
-int SandboxBrokerClient::Symlink(const char* aOldPath, const char* aNewPath) {
- Request req = MakeRequest(SANDBOX_FILE_SYMLINK, 0, 0);
- return DoCall(&req, aOldPath, aNewPath, nullptr, false);
-}
-
int SandboxBrokerClient::Rename(const char* aOldPath, const char* aNewPath) {
Request req = MakeRequest(SANDBOX_FILE_RENAME, 0, 0);
return DoCall(&req, aOldPath, aNewPath, nullptr, false);
diff --git a/security/sandbox/linux/SandboxBrokerClient.h b/security/sandbox/linux/SandboxBrokerClient.h
index cc5e96437dd..2f5c0952618 100644
--- a/security/sandbox/linux/SandboxBrokerClient.h
+++ b/security/sandbox/linux/SandboxBrokerClient.h
@@ -34,7 +34,6 @@ class SandboxBrokerClient final : private SandboxBrokerCommon {
int Chmod(const char* aPath, int aMode);
int Link(const char* aPath, const char* aPath2);
int Mkdir(const char* aPath, int aMode);
- int Symlink(const char* aOldPath, const char* aNewPath);
int Rename(const char* aOldPath, const char* aNewPath);
int Unlink(const char* aPath);
int Rmdir(const char* aPath);
diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp
index 04f7cf4507e..004d2314786 100644
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -299,13 +299,6 @@ class SandboxPolicyCommon : public SandboxPolicyBase {
return broker->Link(path, path2);
}
- static intptr_t SymlinkTrap(ArgsRef aArgs, void* aux) {
- auto broker = static_cast<SandboxBrokerClient*>(aux);
- auto path = reinterpret_cast<const char*>(aArgs.args[0]);
- auto path2 = reinterpret_cast<const char*>(aArgs.args[1]);
- return broker->Symlink(path, path2);
- }
-
static intptr_t RenameTrap(ArgsRef aArgs, void* aux) {
auto broker = static_cast<SandboxBrokerClient*>(aux);
auto path = reinterpret_cast<const char*>(aArgs.args[0]);
@@ -472,19 +465,6 @@ class SandboxPolicyCommon : public SandboxPolicyBase {
return broker->Link(path, path2);
}
- static intptr_t SymlinkAtTrap(ArgsRef aArgs, void* aux) {
- auto broker = static_cast<SandboxBrokerClient*>(aux);
- auto path = reinterpret_cast<const char*>(aArgs.args[0]);
- auto fd2 = static_cast<int>(aArgs.args[1]);
- auto path2 = reinterpret_cast<const char*>(aArgs.args[2]);
- if (fd2 != AT_FDCWD && path2[0] != '/') {
- SANDBOX_LOG("unsupported fd-relative symlinkat(\"%s\", %d, \"%s\")", path,
- fd2, path2);
- return BlockedSyscallTrap(aArgs, nullptr);
- }
- return broker->Symlink(path, path2);
- }
-
static intptr_t RenameAtTrap(ArgsRef aArgs, void* aux) {
auto broker = static_cast<SandboxBrokerClient*>(aux);
auto fd = static_cast<int>(aArgs.args[0]);
@@ -966,7 +946,7 @@ class SandboxPolicyCommon : public SandboxPolicyBase {
case __NR_mkdir:
return Trap(MkdirTrap, mBroker);
case __NR_symlink:
- return Trap(SymlinkTrap, mBroker);
+ return Error(EPERM);
case __NR_rename:
return Trap(RenameTrap, mBroker);
case __NR_rmdir:
@@ -995,7 +975,7 @@ class SandboxPolicyCommon : public SandboxPolicyBase {
case __NR_mkdirat:
return Trap(MkdirAtTrap, mBroker);
case __NR_symlinkat:
- return Trap(SymlinkAtTrap, mBroker);
+ return Error(EPERM);
case __NR_renameat:
return Trap(RenameAtTrap, mBroker);
case __NR_unlinkat:
diff --git a/security/sandbox/linux/broker/SandboxBroker.cpp b/security/sandbox/linux/broker/SandboxBroker.cpp
index 261ee725152..886a815b507 100644
--- a/security/sandbox/linux/broker/SandboxBroker.cpp
+++ b/security/sandbox/linux/broker/SandboxBroker.cpp
@@ -529,17 +529,6 @@ static int DoStat(const char* aPath, statstruct* aBuff, int aFlags) {
return statsyscall(aPath, aBuff);
}
-static int DoLink(const char* aPath, const char* aPath2,
- SandboxBrokerCommon::Operation aOper) {
- if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_LINK) {
- return link(aPath, aPath2);
- }
- if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_SYMLINK) {
- return symlink(aPath, aPath2);
- }
- MOZ_CRASH("SandboxBroker: Unknown link operation");
-}
-
static int DoConnect(const char* aPath, size_t aLen, int aType,
bool aIsAbstract) {
// Deny SOCK_DGRAM for the same reason it's denied for socketpair.
@@ -910,9 +899,8 @@ void SandboxBroker::ThreadMain(void) {
break;
case SANDBOX_FILE_LINK:
- case SANDBOX_FILE_SYMLINK:
if (permissive || AllowOperation(W_OK | X_OK, perms)) {
- if (DoLink(pathBuf, pathBuf2, req.mOp) == 0) {
+ if (link(pathBuf, pathBuf2) == 0) {
resp.mError = 0;
} else {
resp.mError = -errno;
diff --git a/security/sandbox/linux/broker/SandboxBrokerCommon.cpp b/security/sandbox/linux/broker/SandboxBrokerCommon.cpp
index 4762be3e19a..396a1c53284 100644
--- a/security/sandbox/linux/broker/SandboxBrokerCommon.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.cpp
@@ -46,19 +46,8 @@ unsigned SandboxBrokerCommon::OperationToInt(Operation aOp) {
// static
const char* SandboxBrokerCommon::OperationDescription(Operation aOp) {
static constexpr const char* kNames[] = {
- "open",
- "access",
- "stat",
- "chmod",
- "link",
- "symlink",
- "mkdir",
- "rename",
- "rmdir",
- "unlink",
- "readlink",
- "connect",
- "connect-abstract",
+ "open", "access", "stat", "chmod", "link", "mkdir",
+ "rename", "rmdir", "unlink", "readlink", "connect", "connect-abstract",
};
static_assert(
diff --git a/security/sandbox/linux/broker/SandboxBrokerCommon.h b/security/sandbox/linux/broker/SandboxBrokerCommon.h
index 599431f0ce4..1bdae7a3c38 100644
--- a/security/sandbox/linux/broker/SandboxBrokerCommon.h
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.h
@@ -32,7 +32,6 @@ class SandboxBrokerCommon {
SANDBOX_FILE_STAT,
SANDBOX_FILE_CHMOD,
SANDBOX_FILE_LINK,
- SANDBOX_FILE_SYMLINK,
SANDBOX_FILE_MKDIR,
SANDBOX_FILE_RENAME,
SANDBOX_FILE_RMDIR,
@@ -44,11 +43,11 @@ class SandboxBrokerCommon {
};
MOZ_USING_ENUM_STATIC(Operation, SANDBOX_FILE_OPEN, SANDBOX_FILE_ACCESS,
SANDBOX_FILE_STAT, SANDBOX_FILE_CHMOD,
- SANDBOX_FILE_LINK, SANDBOX_FILE_SYMLINK,
- SANDBOX_FILE_MKDIR, SANDBOX_FILE_RENAME,
- SANDBOX_FILE_RMDIR, SANDBOX_FILE_UNLINK,
- SANDBOX_FILE_READLINK, SANDBOX_SOCKET_CONNECT,
- SANDBOX_SOCKET_CONNECT_ABSTRACT, SANDBOX_OP_MAX_VALUE);
+ SANDBOX_FILE_LINK, SANDBOX_FILE_MKDIR,
+ SANDBOX_FILE_RENAME, SANDBOX_FILE_RMDIR,
+ SANDBOX_FILE_UNLINK, SANDBOX_FILE_READLINK,
+ SANDBOX_SOCKET_CONNECT, SANDBOX_SOCKET_CONNECT_ABSTRACT,
+ SANDBOX_OP_MAX_VALUE);
static bool OperationIsValid(Operation aOp) {
return static_cast<unsigned>(aOp) <=
diff --git a/security/sandbox/linux/gtest/TestBroker.cpp b/security/sandbox/linux/gtest/TestBroker.cpp
index 1b7b84178d2..ea5f22e14da 100644
--- a/security/sandbox/linux/gtest/TestBroker.cpp
+++ b/security/sandbox/linux/gtest/TestBroker.cpp
@@ -68,9 +68,6 @@ class SandboxBrokerTest : public ::testing::Test {
int Mkdir(const char* aPath, int aMode) {
return mClient->Mkdir(aPath, aMode);
}
- int Symlink(const char* aPath, const char* bPath) {
- return mClient->Symlink(aPath, bPath);
- }
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/security/sandbox/common/test/SandboxTestingChildTests.h b/security/sandbox/common/test/SandboxTestingChildTests.h
index a42eefea587..9dae8d40761 100644
--- a/security/sandbox/common/test/SandboxTestingChildTests.h
+++ b/security/sandbox/common/test/SandboxTestingChildTests.h
@@ -627,6 +627,12 @@ void RunTestsContent(SandboxTestingChild* child) {
return fd;
});
+ child->ErrnoValueTest("symlink"_ns, EPERM,
+ [] { return symlink("something", "/tmp/testlink"); });
+ child->ErrnoValueTest("symlinkat"_ns, EPERM, [] {
+ return symlinkat("something", AT_FDCWD, "/tmp/testlink");
+ });
+
# endif // XP_LINUX
# ifdef XP_MACOSX
diff --git a/security/sandbox/test/browser_content_sandbox_fs_tests.js b/security/sandbox/test/browser_content_sandbox_fs_tests.js
index 9e2232f40ce..cc56dc62ca6 100644
--- a/security/sandbox/test/browser_content_sandbox_fs_tests.js
+++ b/security/sandbox/test/browser_content_sandbox_fs_tests.js
@@ -75,10 +75,9 @@ async function createTempFile() {
createSymlink
);
ok(!symlinkCreated.ok, "created a symlink in temp failed");
- const expectedError = isLinux() ? lazy.LIBC.EACCES : lazy.LIBC.EPERM;
is(
symlinkCreated.code,
- expectedError,
+ lazy.LIBC.EPERM,
"created a symlink in temp failed with access denied"
);
}
Loading diff…
References
On This Page