commit ffcae969c4f8f28ed884e504b6142e5c1eb7f7bf
parent 082b7d7ffae978ee4048007427fdfa0548bf413f
Author: Nishu Sheth <nsheth@mozilla.com>
Date: Fri, 31 Oct 2025 14:51:47 +0000
Bug 1990941 - Adding additional macOS GPU process sandbox tests r=haik
Differential Revision: https://phabricator.services.mozilla.com/D269211
Diffstat:
4 files changed, 232 insertions(+), 1 deletion(-)
diff --git a/ipc/app/Makefile.in b/ipc/app/Makefile.in
@@ -21,7 +21,7 @@ ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) #{
libs::
# plugin-container
$(NSINSTALL) -D $(DIST)/bin/$(PROGRAM).app
- rsync -a -C --exclude '*.in' $(srcdir)/macbuild/Contents $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app
+ rsync -a -C --exclude '*.in' $(srcdir)/macbuild/Contents $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app
$(call py_action,preprocessor $(MOZ_CHILD_PROCESS_NAME).app/Contents/Info.plist,-Fsubstitution -DEXECUTABLE='$(MOZ_CHILD_PROCESS_NAME)' -DBUNDLEID='$(MOZ_CHILD_PROCESS_BUNDLEID)' -DMOZ_DEVELOPER_REPO_PATH='$(topsrcdir)' -DMOZ_DEVELOPER_OBJ_PATH='$(topobjdir)' $(srcdir)/macbuild/Contents/Info.plist.in -o $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app/Contents/Info.plist)
$(call py_action,preprocessor $(MOZ_CHILD_PROCESS_NAME).app/Contents/Resources/English.lproj/InfoPlist.strings,-Fsubstitution --output-encoding utf-16 -DAPP_NAME='$(MOZ_CHILD_PROCESS_APPNAME)' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in -o $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app/Contents/Resources/English.lproj/InfoPlist.strings)
$(NSINSTALL) -D $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app/Contents/MacOS
diff --git a/security/sandbox/common/moz.build b/security/sandbox/common/moz.build
@@ -57,3 +57,5 @@ FINAL_LIBRARY = "xul"
EXPORTS.mozilla += [
"SandboxSettings.h",
]
+
+DEFINES["MOZ_GPU_PROCESS_BUNDLEID"] = '"%s"' % CONFIG["MOZ_GPU_PROCESS_BUNDLEID"]
diff --git a/security/sandbox/common/test/SandboxTestingChildTests.h b/security/sandbox/common/test/SandboxTestingChildTests.h
@@ -52,6 +52,12 @@
# include <CoreFoundation/CoreFoundation.h>
# include <CoreGraphics/CoreGraphics.h>
# include <AudioToolbox/AudioToolbox.h>
+# include <dirent.h>
+# include <fcntl.h>
+# include <limits.h>
+# include <sys/stat.h>
+# include <sys/sysctl.h>
+# include <unistd.h>
namespace ApplicationServices {
# include <ApplicationServices/ApplicationServices.h>
}
@@ -1101,6 +1107,210 @@ void RunTestsGPU(SandboxTestingChild* child) {
child->SendReportTestResults("sandbox_check()"_ns, isSandboxStarted,
gpuSandboxCheckMessage);
+ // Home directory tests
+ const char* home = getenv("HOME");
+ if (home) {
+ // Test write to home directory
+ nsCString testFile(home);
+ testFile.Append("/gpu_sbox_writetest.tmp");
+
+ child->ErrnoTest("write denied ($HOME)"_ns, false,
+ [p = std::string(testFile.get())] {
+ int fd = open(p.c_str(), O_CREAT | O_WRONLY, 0600);
+ if (fd >= 0) {
+ close(fd);
+ }
+ return fd;
+ });
+
+ // Test reading from home directory - file already created by parent process
+ nsCString testReadFile(home);
+ testReadFile.Append("/.mozilla_gpu_sandbox_read_test");
+ std::string path = std::string(testReadFile.get());
+ if (access(path.c_str(), F_OK) == 0) {
+ child->ErrnoTest("read denied (home test file)"_ns, false, [path] {
+ int fd = open(path.c_str(), O_RDONLY);
+ if (fd >= 0) {
+ close(fd);
+ }
+ return fd;
+ });
+ } else {
+ child->SendReportTestResults(
+ "read denied (home test file)"_ns, false,
+ "Test file does not exist, test setup failure"_ns);
+ }
+ } else {
+ child->SendReportTestResults("HOME check"_ns, false,
+ "HOME environment variable not set"_ns);
+ }
+
+ // System directory
+ child->ErrnoTest("write denied (/tmp)"_ns, false, [] {
+ int fd = open("/tmp/gpu_sandbox_test_file.txt", O_CREAT | O_WRONLY, 0600);
+ if (fd >= 0) {
+ close(fd);
+ }
+ return fd;
+ });
+
+ child->ErrnoTest("write denied (/private/tmp)"_ns, false, [] {
+ int fd = open("/private/tmp/sandboxTest.txt", O_CREAT | O_WRONLY, 0600);
+ if (fd >= 0) {
+ close(fd);
+ }
+ return fd;
+ });
+
+ // Shader cache tests
+ char buf[PATH_MAX];
+ if (confstr(_CS_DARWIN_USER_CACHE_DIR, buf, sizeof(buf)) > 0) {
+ nsCString cache(buf);
+
+ // Can't create directories at the cache root.
+ nsCString subdir = cache + "/mozilla-gpu-sbox-test"_ns;
+ child->ErrnoTest("mkdir denied (cache root subdir)"_ns, false,
+ [s = std::string(subdir.get())] {
+ int rv = mkdir(s.c_str(), 0700);
+ if (rv == 0) {
+ rmdir(s.c_str()); // cleanup if somehow created
+ }
+ return rv;
+ });
+
+ // Can't write files at the cache root.
+ nsCString file = cache + "/gpu_test_file.txt"_ns;
+ child->ErrnoTest("write denied (cache root)"_ns, false,
+ [f = std::string(file.get())] {
+ int fd = open(f.c_str(), O_CREAT | O_WRONLY, 0600);
+ if (fd >= 0) {
+ close(fd);
+ unlink(f.c_str());
+ }
+ return fd;
+ });
+
+ // Can't create a fake GPU bundle cache directory.
+ nsCString fakeGpuDir = cache + "/org.mozilla.firefox-fake-gpu"_ns;
+ child->ErrnoTest("mkdir denied (fake GPU cache dir)"_ns, false,
+ [dir = std::string(fakeGpuDir.get())] {
+ int rv = mkdir(dir.c_str(), 0700);
+ if (rv == 0) {
+ rmdir(dir.c_str()); // cleanup if somehow created
+ }
+ return rv;
+ });
+
+ // Actual GPU bundle cache directory
+ nsCString actualGpuCacheDir =
+ cache + "/"_ns + nsCString(MOZ_GPU_PROCESS_BUNDLEID);
+
+ // Allowed to write a regular file in the GPU bundle cache.
+ nsCString legitGpuFile = actualGpuCacheDir + "/gpu_test.cache"_ns;
+ child->ErrnoTest("write allowed (GPU bundle cache)"_ns, true,
+ [f = std::string(legitGpuFile.get())] {
+ int fd = open(f.c_str(), O_CREAT | O_WRONLY, 0600);
+ if (fd >= 0) {
+ close(fd);
+ unlink(f.c_str()); // cleanup
+ return 0;
+ }
+ return fd;
+ });
+
+ // Symlink test inside the GPU bundle cache.
+ nsCString symlinkInGpuCache = actualGpuCacheDir + "/bad_symlink"_ns;
+ child->ErrnoTest("symlink denied (GPU bundle cache)"_ns, false,
+ [s = std::string(symlinkInGpuCache.get())] {
+ int rv = symlink("/etc/passwd", s.c_str());
+ if (rv == 0) { // cleanup if somehow created
+ int fd = open(s.c_str(), O_RDONLY);
+ if (fd >= 0) {
+ close(fd);
+ }
+ unlink(s.c_str());
+ }
+ return rv;
+ });
+
+ } else {
+ child->SendReportTestResults("cache dir check"_ns, false,
+ "Could not get user cache directory"_ns);
+ }
+
+ // Test read permissions
+ child->ErrnoTest(
+ "read allowed (/System/.../SystemVersion.plist)"_ns, true, [] {
+ int fd =
+ open("/System/Library/CoreServices/SystemVersion.plist", O_RDONLY);
+ if (fd >= 0) {
+ close(fd);
+ return 0;
+ }
+ return fd;
+ });
+
+ // Test directory listing permissions
+ child->ErrnoTest("list allowed (/Library/ColorSync/Profiles)"_ns, true, [] {
+ DIR* d = opendir("/Library/ColorSync/Profiles");
+ if (!d) return -1;
+ struct dirent* entry = readdir(d);
+ closedir(d);
+ return entry ? 0 : -1;
+ });
+
+ child->ErrnoTest("list allowed (/private/var/db/CVMS)"_ns, true, [] {
+ DIR* d = opendir("/private/var/db/CVMS");
+ if (!d) return -1;
+ struct dirent* entry = readdir(d);
+ closedir(d);
+ return entry ? 0 : -1;
+ });
+
+ // Test sysctl permissions
+ child->ErrnoTest("sysctl allowed (kern.ostype)"_ns, true, [] {
+ char buf[256];
+ size_t sz = sizeof(buf);
+ int rv = sysctlbyname("kern.ostype", buf, &sz, nullptr, 0);
+ return rv;
+ });
+
+ child->ErrnoTest("sysctl allowed (hw.memsize)"_ns, true, [] {
+ uint64_t mem = 0;
+ size_t sz = sizeof(mem);
+ int rv = sysctlbyname("hw.memsize", &mem, &sz, nullptr, 0);
+ return rv;
+ });
+
+ // System directory write
+ child->ErrnoTest("write denied (root)"_ns, false, [] {
+ int fd = open("/gpu_root_test.txt", O_CREAT | O_WRONLY, 0600);
+ if (fd >= 0) {
+ close(fd);
+ }
+ return fd;
+ });
+
+ child->ErrnoTest("write denied (/usr/local)"_ns, false, [] {
+ int fd = open("/usr/local/gpu_bad_test.txt", O_CREAT | O_WRONLY, 0600);
+ if (fd >= 0) {
+ close(fd);
+ }
+ return fd;
+ });
+
+ child->ErrnoTest("write denied (/etc)"_ns, false, [] {
+ int fd = open("/etc/gpu_bad_test.txt", O_CREAT | O_WRONLY, 0600);
+ if (fd >= 0) {
+ close(fd);
+ }
+ return fd;
+ });
+
+ RunMacTestLaunchProcess(child, EPERM);
+ RunMacTestAudioAPI(child);
+ RunMacTestWindowServer(child, true);
+
#else // defined(XP_WIN)
child->ReportNoTests();
#endif // defined(XP_WIN)
diff --git a/security/sandbox/test/browser_sandbox_test.js b/security/sandbox/test/browser_sandbox_test.js
@@ -39,6 +39,14 @@ function test() {
let sandboxTestDone = () => {
remainingTests = remainingTests - 1;
if (remainingTests == 0) {
+ // Clean up test file
+ if (homeTestFile.exists()) {
+ ok(homeTestFile.isFile(), "homeTestFile should be a file");
+ if (homeTestFile.isFile()) {
+ homeTestFile.remove(false);
+ }
+ }
+
Services.obs.removeObserver(sandboxTestResult, "sandbox-test-result");
Services.obs.removeObserver(sandboxTestDone, "sandbox-test-done");
@@ -55,5 +63,16 @@ function test() {
Ci.mozISandboxTest
);
+ let homeTestFile;
+ try {
+ homeTestFile = Services.dirsvc.get("Home", Ci.nsIFile);
+ homeTestFile.append(".mozilla_gpu_sandbox_read_test");
+ if (!homeTestFile.exists()) {
+ homeTestFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+ }
+ } catch (e) {
+ ok(false, "Failed to create home test file: " + e);
+ }
+
comp.startTests(processTypes);
}