tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 8bae5f49b3cae9c9de197268c60ea61da4a625f4
parent e0722384b722bd5d13103eb93ccd0ccfbad6e49b
Author: Nishu Sheth <nsheth@mozilla.com>
Date:   Tue, 28 Oct 2025 19:14:56 +0000

Bug 1990941 - Adding additional macOS GPU process sandbox tests r=haik

Differential Revision: https://phabricator.services.mozilla.com/D269211

Diffstat:
Mipc/app/Makefile.in | 2+-
Msecurity/sandbox/common/moz.build | 2++
Msecurity/sandbox/common/test/SandboxTestingChildTests.h | 209+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msecurity/sandbox/test/browser_sandbox_test.js | 19+++++++++++++++++++
4 files changed, 231 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,209 @@ 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); + } + 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); }