Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  SandboxTestingChildTests.h   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */


#include "SandboxTestingChild.h"

#include "mozilla/StaticPrefs_security.h"
#include "mozilla/ipc/UtilityProcessSandboxing.h"
#include "nsXULAppAPI.h"

#ifdef XP_UNIX
#  include <fcntl.h>
#  include <netdb.h>
#  ifdef XP_LINUX
#    include <arpa/inet.h>
#    include <linux/mempolicy.h>
#    include <sched.h>
#    include <sys/ioctl.h>
#    include <sys/mman.h>
#    include <sys/prctl.h>
#    include <sys/resource.h>
#    include <sys/socket.h>
#    include <sys/statfs.h>
#    include <sys/syscall.h>
#    include <sys/sysmacros.h>
#    include <sys/time.h>
#    include <sys/un.h>
#    include <sys/utsname.h>
#    include <termios.h>
#    include "mozilla/ProcInfo_linux.h"
#    include "mozilla/UniquePtrExtensions.h"
#    ifdef MOZ_X11
#      include "X11/Xlib.h"
#      include "X11UndefineNone.h"
#    endif  // MOZ_X11
#  endif    // XP_LINUX
#  include <sys/socket.h>
#  include <sys/stat.h>
#  include <sys/types.h>
#  include <time.h>
#  include <unistd.h>
#endif

#ifdef XP_MACOSX
#  if defined(__SSE2__) || defined(_M_X64) || \
      (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
#    include "emmintrin.h"
#  endif
#  include <spawn.h>
#  include <CoreFoundation/CoreFoundation.h>
#  include <CoreGraphics/CoreGraphics.h>
#  include <AudioToolbox/AudioToolbox.h>
namespace ApplicationServices {
#  include <ApplicationServices/ApplicationServices.h>
}
#endif

#ifdef XP_WIN
#  include <stdio.h>
#  include <winternl.h>

#  include "mozilla/DynamicallyLinkedFunctionPtr.h"
#  include "nsAppDirectoryServiceDefs.h"
#  include "mozilla/WindowsProcessMitigations.h"
#endif

#ifdef XP_LINUX
// Defined in <linux/watch_queue.h> which was added in 5.8
#  ifndef O_NOTIFICATION_PIPE
#    define O_NOTIFICATION_PIPE O_EXCL
#  endif
// Added in 5.7.
#  ifndef MREMAP_DONTUNMAP
#    define MREMAP_DONTUNMAP 4
#  endif
#endif

constexpr bool kIsDebug =
#ifdef DEBUG
    true;
#else
    false;
#endif

namespace mozilla {

#ifdef XP_LINUX
static void RunTestsSched(SandboxTestingChild* child) {
  struct sched_param param_pid_0 = {};
  child->ErrnoTest("sched_getparam(0)"_ns, true,
                   [&] { return sched_getparam(0, ¶m_pid_0); });

  struct sched_param param_pid_tid = {};
  child->ErrnoTest("sched_getparam(tid)"_ns, true, [&] {
    return sched_getparam((pid_t)syscall(__NR_gettid), ¶m_pid_tid);
  });

  struct sched_param param_pid_Ntid = {};
  child->ErrnoValueTest("sched_getparam(Ntid)"_ns, EPERM, [&] {
    return sched_getparam((pid_t)(syscall(__NR_gettid) - 1), ¶m_pid_Ntid);
  });
}
#endif

// Tests that apply to every process type (more or less)
static void RunGenericTests(SandboxTestingChild* child, bool aIsGMP = false) {
#ifdef XP_LINUX
  // Check ABI issues with 32-bit arguments on 64-bit platforms.
  if (sizeof(void*) == 8) {
    static constexpr uint64_t kHighBits = 0xDEADBEEF00000000;

    struct timespec ts0, ts1;
    child->ErrnoTest("high_bits_gettime"_ns, true, [&] {
      return syscall(__NR_clock_gettime, kHighBits | CLOCK_MONOTONIC, &ts0);
    });
    // Try to make sure we got the correct clock by reading it again and
    // comparing to see if the times are vaguely similar.
    int rv = clock_gettime(CLOCK_MONOTONIC, &ts1);
    MOZ_RELEASE_ASSERT(rv == 0);
    MOZ_RELEASE_ASSERT(ts0.tv_sec <= ts1.tv_sec + 1);
    MOZ_RELEASE_ASSERT(ts1.tv_sec <= ts0.tv_sec + 60);

    // Check some non-zeroth arguments.  (fcntl is convenient for
    // this, but GMP has a stricter policy, so skip it there.)
    if (!aIsGMP) {
      int flags;
      child->ErrnoTest("high_bits_fcntl_getfl"_ns, true, [&] {
        flags = syscall(__NR_fcntl, 0, kHighBits | F_GETFL);
        return flags;
      });
      MOZ_RELEASE_ASSERT(flags == fcntl(0, F_GETFL));

      int fds[2];
      rv = pipe(fds);
      MOZ_RELEASE_ASSERT(rv >= 0);
      child->ErrnoTest("high_bits_fcntl_setfl"_ns, true, [&] {
        return syscall(__NR_fcntl, fds[0], kHighBits | F_SETFL,
                       kHighBits | O_NONBLOCK);
      });
      flags = fcntl(fds[0], F_GETFL);
      MOZ_RELEASE_ASSERT(flags >= 0);
      MOZ_RELEASE_ASSERT(flags & O_NONBLOCK);
    }
  }
#endif  // XP_LINUX
}

#ifdef XP_WIN
/**
 * Uses NtCreateFile directly to test file system brokering.
 *
 */

static void FileTest(const nsCString& aName, const char* aSpecialDirName,
                     const nsString& aRelativeFilePath, ACCESS_MASK aAccess,
                     bool aExpectSuccess, SandboxTestingChild* aChild) {
  static const StaticDynamicallyLinkedFunctionPtr<decltype(&NtCreateFile)>
      pNtCreateFile(L"ntdll.dll""NtCreateFile");
  static const StaticDynamicallyLinkedFunctionPtr<decltype(&NtClose)> pNtClose(
      L"ntdll.dll""NtClose");

  // Start the filename with the NT namespace
  nsString testFilename(u"\\??\\"_ns);
  nsString dirPath;
  aChild->SendGetSpecialDirectory(nsDependentCString(aSpecialDirName),
                                  &dirPath);
  testFilename.Append(dirPath);
  testFilename.AppendLiteral("\\");
  testFilename.Append(aRelativeFilePath);

  UNICODE_STRING uniFileName;
  ::RtlInitUnicodeString(&uniFileName, testFilename.get());

  OBJECT_ATTRIBUTES objectAttributes;
  InitializeObjectAttributes(&objectAttributes, &uniFileName,
                             OBJ_CASE_INSENSITIVE, nullptr, nullptr);

  HANDLE fileHandle = INVALID_HANDLE_VALUE;
  IO_STATUS_BLOCK ioStatusBlock = {};

  ULONG createOptions = StringEndsWith(testFilename, u"\\"_ns) ||
                                StringEndsWith(testFilename, u"/"_ns)
                            ? FILE_DIRECTORY_FILE
                            : FILE_NON_DIRECTORY_FILE;
  NTSTATUS status = pNtCreateFile(
      &fileHandle, aAccess, &objectAttributes, &ioStatusBlock, nullptr, 0, 0,
      FILE_OPEN_IF, createOptions | FILE_SYNCHRONOUS_IO_NONALERT, nullptr, 0);

  if (fileHandle != INVALID_HANDLE_VALUE) {
    pNtClose(fileHandle);
  }

  nsCString accessString;
  if ((aAccess & FILE_GENERIC_READ) == FILE_GENERIC_READ) {
    accessString.AppendLiteral("r");
  }
  if ((aAccess & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE) {
    accessString.AppendLiteral("w");
  }
  if ((aAccess & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE) {
    accessString.AppendLiteral("e");
  }

  nsCString msgRelPath = NS_ConvertUTF16toUTF8(aRelativeFilePath);
  for (size_t i = 0, j = 0; i < aRelativeFilePath.Length(); ++i, ++j) {
    if (aRelativeFilePath[i] == u'\\') {
      msgRelPath.Insert('\\', j++);
    }
  }

  nsCString message;
  message.AppendPrintf(
      "Special dir: %s, file: %s, access: %s , returned status: %lx",
      aSpecialDirName, msgRelPath.get(), accessString.get(), status);

  aChild->SendReportTestResults(aName, aExpectSuccess == NT_SUCCESS(status),
                                message);
}
#endif

#ifdef XP_MACOSX
/*
 * Test if this process can launch another process with posix_spawnp,
 * exec, and LSOpenCFURLRef. All launches are expected to fail. In processes
 * where the sandbox permits reading of file metadata (content processes at
 * this time), we expect the posix_spawnp error to be EPERM. In processes
 * without that permission, we expect ENOENT. Changing the sandbox policy
 * may break this assumption, but the important aspect to test for is that the
 * launch is not permitted.
 */

void RunMacTestLaunchProcess(SandboxTestingChild* child,
                             int aPosixSpawnExpectedError = ENOENT) {
  // Test that posix_spawnp fails
  char* argv[2];
  argv[0] = const_cast<char*>("bash");
  argv[1] = NULL;
  int rv = posix_spawnp(NULL, "/bin/bash", NULL, NULL, argv, NULL);
  nsPrintfCString posixSpawnMessage("posix_spawnp returned %d, expected %d", rv,
                                    aPosixSpawnExpectedError);
  child->SendReportTestResults("posix_spawnp test"_ns,
                               rv == aPosixSpawnExpectedError,
                               posixSpawnMessage);

  // Test that exec fails
  child->ErrnoTest("execv /bin/bash test"_ns, false, [&] {
    char* argvp = NULL;
    return execv("/bin/bash", &argvp);
  });

  // Test that launching an application using LSOpenCFURLRef fails
  char* uri = const_cast<char*>("/System/Applications/Utilities/Console.app");
  CFStringRef filePath = ::CFStringCreateWithCString(kCFAllocatorDefault, uri,
                                                     kCFStringEncodingUTF8);
  CFURLRef urlRef = ::CFURLCreateWithFileSystemPath(
      kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false);
  if (!urlRef) {
    child->SendReportTestResults("LSOpenCFURLRef"_ns, false,
                                 "CFURLCreateWithFileSystemPath failed"_ns);
    return;
  }

  OSStatus status = ApplicationServices::LSOpenCFURLRef(urlRef, NULL);
  ::CFRelease(urlRef);
  nsPrintfCString lsMessage(
      "LSOpenCFURLRef returned %d, "
      "expected kLSServerCommunicationErr (%d)",
      status, ApplicationServices::kLSServerCommunicationErr);
  child->SendReportTestResults(
      "LSOpenCFURLRef"_ns,
      status == ApplicationServices::kLSServerCommunicationErr, lsMessage);
}

/*
 * Test if this process can connect to the macOS window server.
 * When |aShouldHaveAccess| is true, the test passes if access is __permitted__.
 * When |aShouldHaveAccess| is false, the test passes if access is __blocked__.
 */

void RunMacTestWindowServer(SandboxTestingChild* child,
                            bool aShouldHaveAccess = false) {
  // CGSessionCopyCurrentDictionary() returns NULL when a
  // connection to the window server is not available.
  CFDictionaryRef windowServerDict = CGSessionCopyCurrentDictionary();
  bool gotWindowServerDetails = (windowServerDict != nullptr);
  bool testPassed = (gotWindowServerDetails == aShouldHaveAccess);
  child->SendReportTestResults(
      "CGSessionCopyCurrentDictionary"_ns, testPassed,
      gotWindowServerDetails
          ? "dictionary returned, access is permitted"_ns
          : "no dictionary returned, access appears blocked"_ns);
  if (windowServerDict != nullptr) {
    CFRelease(windowServerDict);
  }
}

/*
 * Test if this process can get access to audio components on macOS.
 * When |aShouldHaveAccess| is true, the test passes if access is __permitted__.
 * When |aShouldHaveAccess| is false, the test passes if access is __blocked__.
 */

void RunMacTestAudioAPI(SandboxTestingChild* child,
                        bool aShouldHaveAccess = false) {
  AudioStreamBasicDescription inputFormat;
  inputFormat.mFormatID = kAudioFormatMPEG4AAC;
  inputFormat.mSampleRate = 48000.0;
  inputFormat.mChannelsPerFrame = 2;
  inputFormat.mBitsPerChannel = 0;
  inputFormat.mFormatFlags = 0;
  inputFormat.mFramesPerPacket = 1024;
  inputFormat.mBytesPerPacket = 0;

  UInt32 inputFormatSize = sizeof(inputFormat);
  OSStatus status = AudioFormatGetProperty(
      kAudioFormatProperty_FormatInfo, 0, NULL, &inputFormatSize, &inputFormat);

  bool gotAudioFormat = (status == 0);
  bool testPassed = (gotAudioFormat == aShouldHaveAccess);
  child->SendReportTestResults(
      "AudioFormatGetProperty"_ns, testPassed,
      gotAudioFormat ? "got audio format, access is permitted"_ns
                     : "no audio format, access appears blocked"_ns);
}
#endif /* XP_MACOSX */

#ifdef XP_WIN
void RunWinTestWin32k(SandboxTestingChild* child,
                      bool aShouldHaveAccess = true) {
  bool isLockedDown = (IsWin32kLockedDown() == true);
  bool testPassed = (isLockedDown == aShouldHaveAccess);
  child->SendReportTestResults(
      "Win32kLockdown"_ns, testPassed,
      isLockedDown ? "got lockdown, access is blocked"_ns
                   : "no lockdown, access appears permitted"_ns);
}
#endif  // XP_WIN

void RunTestsContent(SandboxTestingChild* child) {
  MOZ_ASSERT(child, "No SandboxTestingChild*?");

  RunGenericTests(child);

#ifdef XP_UNIX
  struct stat st;
  static const char kAllowedPath[] = "/usr/lib";

  child->ErrnoTest("fstatat_as_stat"_ns, true,
                   [&] { return fstatat(AT_FDCWD, kAllowedPath, &st, 0); });
  child->ErrnoTest("fstatat_as_lstat"_ns, true, [&] {
    return fstatat(AT_FDCWD, kAllowedPath, &st, AT_SYMLINK_NOFOLLOW);
  });

#  ifdef XP_LINUX
  child->ErrnoTest("fstatat_as_fstat"_ns, true,
                   [&] { return fstatat(0, "", &st, AT_EMPTY_PATH); });

  const struct timespec usec = {0, 1000};
  child->ErrnoTest("nanosleep"_ns, true,
                   [&] { return nanosleep(&usec, nullptr); });

  struct timespec res = {0, 0};
  child->ErrnoTest("clock_getres"_ns, true,
                   [&] { return clock_getres(CLOCK_REALTIME, &res); });

  // same process is allowed
  struct timespec tproc = {0, 0};
  clockid_t same_process = MAKE_PROCESS_CPUCLOCK(getpid(), CPUCLOCK_SCHED);
  child->ErrnoTest("clock_gettime_same_process"_ns, true,
                   [&] { return clock_gettime(same_process, &tproc); });

  // different process is blocked by sandbox (SIGSYS, kernel would return
  // EINVAL)
  struct timespec tprocd = {0, 0};
  clockid_t diff_process = MAKE_PROCESS_CPUCLOCK(1, CPUCLOCK_SCHED);
  child->ErrnoValueTest("clock_gettime_diff_process"_ns, ENOSYS,
                        [&] { return clock_gettime(diff_process, &tprocd); });

  // thread is allowed
  struct timespec tthread = {0, 0};
  clockid_t thread =
      MAKE_THREAD_CPUCLOCK((pid_t)syscall(__NR_gettid), CPUCLOCK_SCHED);
  child->ErrnoTest("clock_gettime_thread"_ns, true,
                   [&] { return clock_gettime(thread, &tthread); });

  // getcpu is allowed
  // We're using syscall directly because:
  // - sched_getcpu uses vdso and as a result doesn't go through the sandbox.
  // - getcpu isn't defined in the header files we're using yet.
  int c;
  child->ErrnoTest("getcpu"_ns, true,
                   [&] { return syscall(SYS_getcpu, &c, NULL, NULL); });

  // An abstract socket that does not starts with '/', so we don't want it to
  // work.
  // Checking ENETUNREACH should be thrown by SandboxBrokerClient::Connect()
  // when it detects it does not starts with a '/'
  child->ErrnoValueTest("connect_abstract_blocked"_ns, ENETUNREACH, [&] {
    int sockfd;
    struct sockaddr_un addr;
    char str[] = "\0xyz";  // Abstract socket requires first byte to be NULL
    size_t str_size = 4;

    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    memcpy(&addr.sun_path, str, str_size);

    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
      return -1;
    }

    int con_st = connect(sockfd, (struct sockaddr*)&addr,
                         sizeof(sa_family_t) + str_size);
    return con_st;
  });

  // An abstract socket that does starts with /, so we do want it to work.
  // Checking ECONNREFUSED because this is what the broker should get
  // when trying to establish the connect call for us if it's allowed;
  // otherwise we get EACCES, meaning that it was passed to the broker
  // (unlike the previous test) but rejected.
  const int errorForX =
      StaticPrefs::security_sandbox_content_headless_AtStartup() ? EACCES
                                                                 : ECONNREFUSED;
  child->ErrnoValueTest("connect_abstract_permit"_ns, errorForX, [&] {
    int sockfd;
    struct sockaddr_un addr;
    // we re-use actual X path, because this is what is allowed within
    // SandboxBrokerPolicyFactory::InitContentPolicy()
    // We can't just use any random path allowed, but one with CONNECT allowed.

    // (Note that the real X11 sockets have names like `X0` for
    // display `:0`; there shouldn't be anything named just `X`.)

    // Abstract socket requires first byte to be NULL
    char str[] = "\0/tmp/.X11-unix/X";
    size_t str_size = 17;

    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    memcpy(&addr.sun_path, str, str_size);

    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
      return -1;
    }

    int con_st = connect(sockfd, (struct sockaddr*)&addr,
                         sizeof(sa_family_t) + str_size);
    return con_st;
  });

  // Testing FIPS-relevant files, which need to be accessible
  std::vector<std::pair<const char*, bool>> open_tests = {
      {"/dev/random"true}};
  // Not all systems have that file, so we only test access, if it exists
  // in the first place
  if (stat("/proc/sys/crypto/fips_enabled", &st) == 0) {
    open_tests.push_back({"/proc/sys/crypto/fips_enabled"true});
  }

  for (const std::pair<const char*, bool>& to_open : open_tests) {
    child->ErrnoTest("open("_ns + nsCString(to_open.first) + ")"_ns,
                     to_open.second, [&] {
                       int fd = open(to_open.first, O_RDONLY);
                       if (to_open.second && fd > 0) {
                         close(fd);
                       }
                       return fd;
                     });
  }

  child->ErrnoTest("statfs"_ns, true, [] {
    struct statfs sf;
    return statfs("/usr/share", &sf);
  });

  child->ErrnoTest("pipe2"_ns, true, [] {
    int fds[2];
    int rv = pipe2(fds, O_CLOEXEC);
    int savedErrno = errno;
    if (rv == 0) {
      close(fds[0]);
      close(fds[1]);
    }
    errno = savedErrno;
    return rv;
  });

  child->ErrnoValueTest("chroot"_ns, ENOSYS, [] { return chroot("/"); });

  child->ErrnoValueTest("pipe2_notif"_ns, ENOSYS, [] {
    int fds[2];
    return pipe2(fds, O_NOTIFICATION_PIPE);
  });

#    ifdef MOZ_X11
  // Check that X11 access is blocked (bug 1129492).
  // This will fail if security.sandbox.content.headless is turned off.
  if (PR_GetEnv("DISPLAY")) {
    Display* disp = XOpenDisplay(nullptr);

    child->SendReportTestResults(
        "x11_access"_ns, !disp,
        disp ? "XOpenDisplay succeeded"_ns : "XOpenDisplay failed"_ns);
    if (disp) {
      XCloseDisplay(disp);
    }
  }
#    endif  // MOZ_X11

  child->ErrnoTest("realpath localtime"_ns, true, [] {
    char buf[PATH_MAX];
    return realpath("/etc/localtime", buf) ? 0 : -1;
  });

  // Check that readlink truncates results longer than the buffer
  // (rather than failing) and returns the total number of bytes
  // actually written (not the size of the link or anything else).
  {
    char buf;
    ssize_t rv = readlink("/etc/localtime", &buf, 1);
    int err = errno;
    if (rv == 1) {
      child->SendReportTestResults("readlink truncate"_ns, true,
                                   "expected 1, got 1"_ns);
    } else if (rv < 0) {
      nsPrintfCString msg("expected 1, got error: %s", strerror(err));
      child->SendReportTestResults("readlink truncate"_ns, false, msg);
    } else {
      nsPrintfCString msg("expected 1, got %zd", rv);
      child->SendReportTestResults("readlink truncate"_ns, false, msg);
    }
  }

  {
    static constexpr size_t kMapSize = 65536;
    void* mapping = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE,
                         MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    MOZ_ASSERT(mapping != MAP_FAILED);
    child->ErrnoTest("mremap-zero"_ns, true, [&] {
      void* rv = mremap(mapping, kMapSize, kMapSize, 0);
      if (rv == MAP_FAILED) {
        return -1;
      }
      MOZ_ASSERT(rv == mapping);
      return 0;
    });

    child->ErrnoValueTest("mremap-forbidden"_ns, ENOSYS, [&] {
      void* rv = mremap(mapping, kMapSize, kMapSize, MREMAP_DONTUNMAP);
      // This is an invalid flag combination (DONTUNMAP requires
      // MAYMOVE) so it will always fail with *something*.
      MOZ_ASSERT(rv == MAP_FAILED);
      return -1;
    });

    munmap(mapping, kMapSize);
  }

#  endif  // XP_LINUX

#  ifdef XP_MACOSX
  RunMacTestLaunchProcess(child, EPERM);
  RunMacTestWindowServer(child);
  RunMacTestAudioAPI(child, true);
#  endif

#elif XP_WIN
  FileTest("read from chrome"_ns, NS_APP_USER_CHROME_DIR, u"sandboxTest.txt"_ns,
           FILE_GENERIC_READ, true, child);
  FileTest("read from profile via relative path"_ns, NS_APP_USER_CHROME_DIR,
           u"..\\sandboxTest.txt"_ns, FILE_GENERIC_READ, false, child);
  // The profile dir is the parent of the chrome dir.
  FileTest("read from chrome using forward slash"_ns,
           NS_APP_USER_PROFILE_50_DIR, u"chrome/sandboxTest.txt"_ns,
           FILE_GENERIC_READ, false, child);

  // Note: these only pass in DEBUG builds because we allow write access to the
  // temp dir for certain test logs and that is where the profile is created.
  FileTest("read from profile"_ns, NS_APP_USER_PROFILE_50_DIR,
           u"sandboxTest.txt"_ns, FILE_GENERIC_READ, kIsDebug, child);
  FileTest("read/write from chrome"_ns, NS_APP_USER_CHROME_DIR,
           u"sandboxTest.txt"_ns, FILE_GENERIC_READ | FILE_GENERIC_WRITE,
           kIsDebug, child);
#else
    child->ReportNoTests();
#endif
}

void RunTestsSocket(SandboxTestingChild* child) {
  MOZ_ASSERT(child, "No SandboxTestingChild*?");

  RunGenericTests(child);

#ifdef XP_UNIX
  child->ErrnoTest("getaddrinfo"_ns, true, [&] {
    struct addrinfo* res;
    int rv = getaddrinfo("localhost", nullptr, nullptr, &res);
    if (res != nullptr) {
      freeaddrinfo(res);
    }
    return rv;
  });

#  ifdef XP_LINUX
  child->ErrnoTest("prctl_allowed"_ns, true, [&] {
    int rv = prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
    return rv;
  });

  child->ErrnoTest("prctl_blocked"_ns, false, [&] {
    int rv = prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
    return rv;
  });

  // Testing FIPS-relevant files, which need to be accessible
  std::vector<std::pair<const char*, bool>> open_tests = {
      {"/dev/random"true}};
  // Not all systems have that file, so we only test access, if it exists
  // in the first place
  struct stat st;
  if (stat("/proc/sys/crypto/fips_enabled", &st) == 0) {
    open_tests.push_back({"/proc/sys/crypto/fips_enabled"true});
  }

  for (const std::pair<const char*, bool>& to_open : open_tests) {
    child->ErrnoTest("open("_ns + nsCString(to_open.first) + ")"_ns,
                     to_open.second, [&] {
                       int fd = open(to_open.first, O_RDONLY);
                       if (to_open.second && fd > 0) {
                         close(fd);
                       }
                       return fd;
                     });
  }

  // getcpu is allowed
  // We're using syscall directly because:
  // - sched_getcpu uses vdso and as a result doesn't go through the sandbox.
  // - getcpu isn't defined in the header files we're using yet.
  int c;
  child->ErrnoTest("getcpu"_ns, true,
                   [&] { return syscall(SYS_getcpu, &c, NULL, NULL); });

  child->ErrnoTest("sendmsg"_ns, true, [&] {
    int fd = socket(AF_INET6, SOCK_DGRAM, 0);
    if (fd < 0) {
      return fd;
    }

    struct sockaddr_in6 addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin6_family = AF_INET6;
    // Address within 100::/64, i.e. IPv6 discard prefix.
    inet_pton(AF_INET6, "100::1", &addr.sin6_addr);
    addr.sin6_port = htons(12345);

    struct msghdr msg = {0};
    struct iovec iov[1];
    char buf[] = "test";
    iov[0].iov_base = buf;
    iov[0].iov_len = sizeof(buf);
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;
    msg.msg_name = &addr;
    msg.msg_namelen = sizeof(addr);

    int rv = sendmsg(fd, &msg, 0);
    close(fd);
    MOZ_ASSERT(rv == sizeof(buf),
               "Expected sendmsg to return the number of bytes sent");
    return rv;
  });

  child->ErrnoTest("recvmmsg"_ns, true, [&] {
    int fd = socket(AF_INET6, SOCK_DGRAM, 0);
    if (fd < 0) {
      return fd;
    }

    // Set the socket to non-blocking mode
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0) {
      close(fd);
      return -1;
    }
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
      close(fd);
      return -1;
    }

    struct sockaddr_in6 addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin6_family = AF_INET6;
    addr.sin6_addr = in6addr_any;
    addr.sin6_port = htons(0);

    if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
      close(fd);
      return -1;
    }

    struct mmsghdr msgs[1];
    memset(msgs, 0, sizeof(msgs));
    struct iovec iov[1];
    char buf[64];
    iov[0].iov_base = buf;
    iov[0].iov_len = sizeof(buf);
    msgs[0].msg_hdr.msg_iov = iov;
    msgs[0].msg_hdr.msg_iovlen = 1;

    int rv = recvmmsg(fd, msgs, 1, 0, nullptr);
    close(fd);
    MOZ_ASSERT(rv == -1 && errno == EAGAIN,
               "recvmmsg should return -1 with EAGAIN given that no datagrams "
               "are available");
    return 0;
  });
#  endif  // XP_LINUX
#elif XP_MACOSX
  RunMacTestLaunchProcess(child);
  RunMacTestWindowServer(child);
  RunMacTestAudioAPI(child);
#else   // XP_UNIX
    child->ReportNoTests();
#endif  // XP_UNIX
}

void RunTestsRDD(SandboxTestingChild* child) {
  MOZ_ASSERT(child, "No SandboxTestingChild*?");

  RunGenericTests(child);

#ifdef XP_UNIX
#  ifdef XP_LINUX
  child->ErrnoValueTest("ioctl_tiocsti"_ns, ENOSYS, [&] {
    int rv = ioctl(1, TIOCSTI, "x");
    return rv;
  });

  struct rusage res = {};
  child->ErrnoTest("getrusage"_ns, true, [&] {
    int rv = getrusage(RUSAGE_SELF, &res);
    return rv;
  });

  child->ErrnoValueTest("unlink"_ns, ENOENT, [&] {
    int rv = unlink("");
    return rv;
  });

  child->ErrnoValueTest("unlinkat"_ns, ENOENT, [&] {
    int rv = unlinkat(AT_FDCWD, "", 0);
    return rv;
  });

  RunTestsSched(child);

  child->ErrnoValueTest("socket_inet"_ns, EACCES,
                        [] { return socket(AF_INET, SOCK_STREAM, 0); });

  child->ErrnoValueTest("socket_unix"_ns, EACCES,
                        [] { return socket(AF_UNIX, SOCK_STREAM, 0); });

  child->ErrnoTest("uname"_ns, true, [] {
    struct utsname uts;
    return uname(&uts);
  });

  child->ErrnoValueTest("ioctl_dma_buf"_ns, ENOTTY, [] {
    // Apply the ioctl to the wrong kind of fd; it should fail with
    // ENOTTY (rather than ENOSYS if it were blocked).
    return ioctl(0, _IOW('b', 0, uint64_t), nullptr);
  });

  // getcpu is allowed
  // We're using syscall directly because:
  // - sched_getcpu uses vdso and as a result doesn't go through the sandbox.
  // - getcpu isn't defined in the header files we're using yet.
  int c;
  child->ErrnoTest("getcpu"_ns, true,
                   [&] { return syscall(SYS_getcpu, &c, NULL, NULL); });

  // The nvidia proprietary drivers will, in some cases, try to
  // mknod their device files; we reject this politely.
  child->ErrnoValueTest("mknod"_ns, EPERM, [] {
    return mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3));
  });

  // Rust panics call getcwd to try to print relative paths in
  // backtraces.
  child->ErrnoValueTest("getcwd"_ns, ENOENT, [] {
    char buf[4096];
    return (getcwd(buf, sizeof(buf)) == nullptr) ? -1 : 0;
  });

  // nvidia defines some ioctls with the type 0x46 ('F', otherwise
  // used by fbdev) and numbers starting from 200 (0xc8).
  child->ErrnoValueTest("ioctl_nvidia"_ns, ENOTTY,
                        [] { return ioctl(0, 0x46c8, nullptr); });

  child->ErrnoTest("statfs"_ns, true, [] {
    struct statfs sf;
    return statfs("/usr/share", &sf);
  });

  child->ErrnoValueTest("fork"_ns, EPERM, [] {
    pid_t pid = fork();
    if (pid == 0) {
      // Success: shouldn't happen, and parent will report a test
      // failure.
      _exit(0);
    }
    return pid;
  });

#  elif XP_MACOSX
  RunMacTestLaunchProcess(child);
  RunMacTestWindowServer(child);
  RunMacTestAudioAPI(child, true);
#  endif
#else  // XP_UNIX
#  ifdef XP_WIN
  RunWinTestWin32k(child, false);
#  endif  // XP_WIN
  child->ReportNoTests();
#endif
}

void RunTestsGMPlugin(SandboxTestingChild* child) {
  MOZ_ASSERT(child, "No SandboxTestingChild*?");

  RunGenericTests(child, /* aIsGMP = */ true);

#ifdef XP_UNIX
#  ifdef XP_LINUX
  struct utsname utsname_res = {};
  child->ErrnoTest("uname_restricted"_ns, true, [&] {
    int rv = uname(&utsname_res);

    nsCString expectedSysname("Linux"_ns);
    nsCString sysname(utsname_res.sysname);
    nsCString expectedVersion("3"_ns);
    nsCString version(utsname_res.version);
    if ((sysname != expectedSysname) || (version != expectedVersion)) {
      return -1;
    }

    return rv;
  });

  child->ErrnoTest("getuid"_ns, true, [&] { return getuid(); });
  child->ErrnoTest("getgid"_ns, true, [&] { return getgid(); });
  child->ErrnoTest("geteuid"_ns, true, [&] { return geteuid(); });
  child->ErrnoTest("getegid"_ns, true, [&] { return getegid(); });

  RunTestsSched(child);

  std::vector<std::pair<const char*, bool>> open_tests = {
      {"/etc/ld.so.cache"true},
      {"/proc/cpuinfo"true},
      {"/etc/hostname"false}};

  for (const std::pair<const char*, bool>& to_open : open_tests) {
    child->ErrnoTest("open("_ns + nsCString(to_open.first) + ")"_ns,
                     to_open.second, [&] {
                       int fd = open(to_open.first, O_RDONLY);
                       if (to_open.second && fd > 0) {
                         close(fd);
                       }
                       return fd;
                     });
  }

  child->ErrnoValueTest("readlink_exe"_ns, EINVAL, [] {
    char pathBuf[PATH_MAX];
    return readlink("/proc/self/exe", pathBuf, sizeof(pathBuf));
  });

  child->ErrnoTest("memfd_sizing"_ns, true, [] {
    int fd = syscall(__NR_memfd_create, "sandbox-test", 0);
    if (fd < 0) {
      if (errno == ENOSYS) {
        // Don't fail the test if the kernel is old.
        return 0;
      }
      return -1;
    }

    int rv = ftruncate(fd, 4096);
    int savedErrno = errno;
    close(fd);
    errno = savedErrno;
    return rv;
  });

  {
    static constexpr size_t kMapSize = 65536;
    void* mapping = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE,
                         MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    MOZ_ASSERT(mapping != MAP_FAILED);

#    ifndef MOZ_MEMORY
    child->ErrnoTest("mremap-move"_ns, true, [&] {
      void* rv = mremap(mapping, kMapSize, kMapSize, MREMAP_MAYMOVE);
      if (rv == MAP_FAILED) {
        return -1;
      }
      // It *may* move the mapping, but when the size doesn't change
      // it's not expected to:
      MOZ_ASSERT(rv == mapping);
      return 0;
    });
#    endif

    child->ErrnoValueTest("mremap-forbidden"_ns, ENOSYS, [&] {
      void* rv = mremap(mapping, kMapSize, kMapSize, MREMAP_DONTUNMAP);
      // This is an invalid flag combination (DONTUNMAP requires
      // MAYMOVE) so it will always fail with *something*.
      MOZ_ASSERT(rv == MAP_FAILED);
      return -1;
    });

    munmap(mapping, kMapSize);
  }

#  elif XP_MACOSX  // XP_LINUX
  RunMacTestLaunchProcess(child);
  /* The Mac GMP process requires access to the window server */
  RunMacTestWindowServer(child, true /* aShouldHaveAccess */);
  RunMacTestAudioAPI(child);
#  endif           // XP_MACOSX
#else              // XP_UNIX
  child->ReportNoTests();
#endif
}

void RunTestsGenericUtility(SandboxTestingChild* child) {
  MOZ_ASSERT(child, "No SandboxTestingChild*?");

  RunGenericTests(child);

#ifdef XP_UNIX
#  ifdef XP_LINUX
  child->ErrnoValueTest("ioctl_tiocsti"_ns, ENOSYS, [&] {
    int rv = ioctl(1, TIOCSTI, "x");
    return rv;
  });

  struct rusage res;
  child->ErrnoTest("getrusage"_ns, true, [&] {
    int rv = getrusage(RUSAGE_SELF, &res);
    return rv;
  });
#  elif XP_MACOSX  // XP_LINUX
  RunMacTestLaunchProcess(child);
  RunMacTestWindowServer(child);
  RunMacTestAudioAPI(child);
#  endif           // XP_MACOSX
#elif XP_WIN       // XP_UNIX
  child->ErrnoValueTest("write_only"_ns, EACCES, [&] {
    FILE* rv = fopen("test_sandbox.txt""w");
    if (rv != nullptr) {
      fclose(rv);
      return 0;
    }
    return -1;
  });
  RunWinTestWin32k(child);
#else              // XP_UNIX
    child->ReportNoTests();
#endif             // XP_MACOSX
}

void RunTestsUtilityAudioDecoder(SandboxTestingChild* child,
                                 ipc::SandboxingKind aSandbox) {
  MOZ_ASSERT(child, "No SandboxTestingChild*?");

  RunGenericTests(child);

#ifdef XP_UNIX
#  ifdef XP_LINUX
  // getrusage is allowed in Generic Utility and on AudioDecoder
  struct rusage res;
  child->ErrnoTest("getrusage"_ns, true, [&] {
    int rv = getrusage(RUSAGE_SELF, &res);
    return rv;
  });

  // get_mempolicy is not allowed in Generic Utility but is on AudioDecoder
  child->ErrnoTest("get_mempolicy"_ns, true, [&] {
    int numa_node;
    int test_val = 0;
    // <numaif.h> not installed by default, let's call directly the syscall
    long rv = syscall(SYS_get_mempolicy, &numa_node, NULL, 0, (void*)&test_val,
                      MPOL_F_NODE | MPOL_F_ADDR);
    return rv;
  });
  // set_mempolicy is not allowed in Generic Utility but is on AudioDecoder
  child->ErrnoValueTest("set_mempolicy"_ns, ENOSYS, [&] {
    // <numaif.h> not installed by default, let's call directly the syscall
    long rv = syscall(SYS_set_mempolicy, 0, NULL, 0);
    return rv;
  });

  child->ErrnoValueTest("prctl_capbtest_read_blocked"_ns, EINVAL, [] {
    int rv = prctl(PR_CAPBSET_READ, 0, 0, 0, 0);
    return rv;
  });

#  elif XP_MACOSX  // XP_LINUX
  RunMacTestLaunchProcess(child);
  RunMacTestWindowServer(child);
  RunMacTestAudioAPI(
      child,
      aSandbox == ipc::SandboxingKind::UTILITY_AUDIO_DECODING_APPLE_MEDIA);
#  endif           // XP_MACOSX
#else              // XP_UNIX
#  ifdef XP_WIN
  RunWinTestWin32k(child);
#  endif  // XP_WIN
  child->ReportNoTests();
#endif    // XP_UNIX
}

void RunTestsGPU(SandboxTestingChild* child) {
  MOZ_ASSERT(child, "No SandboxTestingChild*?");

  RunGenericTests(child);

#if defined(XP_WIN)

  FileTest("R/W access to shader-cache dir"_ns, NS_APP_USER_PROFILE_50_DIR,
           u"shader-cache\\"_ns, FILE_GENERIC_READ | FILE_GENERIC_WRITE, true,
           child);

  FileTest("R/W access to shader-cache files"_ns, NS_APP_USER_PROFILE_50_DIR,
           u"shader-cache\\sandboxTest.txt"_ns,
           FILE_GENERIC_READ | FILE_GENERIC_WRITE, true, child);

#else   // defined(XP_WIN)
  child->ReportNoTests();
#endif  // defined(XP_WIN)
}

}  // namespace mozilla

Messung V0.5
C=89 H=98 G=93

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge