Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/security/sandbox/linux/gtest/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 20 kB image not shown  

Quelle  TestBroker.cpp   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 http://mozilla.org/MPL/2.0/. */


#include "gtest/gtest.h"

#include "broker/SandboxBroker.h"
#include "broker/SandboxBrokerUtils.h"
#include "SandboxBrokerClient.h"

#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <sched.h>
#include <semaphore.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include "mozilla/Atomics.h"
#include "mozilla/PodOperations.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/ipc/FileDescriptor.h"

namespace mozilla {

class SandboxBrokerTest : public ::testing::Test {
  static const int MAY_ACCESS = SandboxBroker::MAY_ACCESS;
  static const int MAY_READ = SandboxBroker::MAY_READ;
  static const int MAY_WRITE = SandboxBroker::MAY_WRITE;
  static const int MAY_CREATE = SandboxBroker::MAY_CREATE;
  static const auto AddAlways = SandboxBroker::Policy::AddAlways;

  UniquePtr<SandboxBroker> mServer;
  UniquePtr<SandboxBrokerClient> mClient;

  UniquePtr<const SandboxBroker::Policy> GetPolicy() const;

  template <class C, void (C::*Main)()>
  static void* ThreadMain(void* arg) {
    (static_cast<C*>(arg)->*Main)();
    return nullptr;
  }

 protected:
  int Open(const char* aPath, int aFlags) {
    return mClient->Open(aPath, aFlags);
  }
  int Access(const char* aPath, int aMode) {
    return mClient->Access(aPath, aMode);
  }
  int Stat(const char* aPath, statstruct* aStat) {
    return mClient->Stat(aPath, aStat);
  }
  int LStat(const char* aPath, statstruct* aStat) {
    return mClient->LStat(aPath, aStat);
  }
  int Chmod(const char* aPath, int aMode) {
    return mClient->Chmod(aPath, aMode);
  }
  int Link(const char* aPath, const char* bPath) {
    return mClient->Link(aPath, bPath);
  }
  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);
  }
  int Rename(const char* aPath, const char* bPath) {
    return mClient->Rename(aPath, bPath);
  }
  int Rmdir(const char* aPath) { return mClient->Rmdir(aPath); }
  int Unlink(const char* aPath) { return mClient->Unlink(aPath); }
  ssize_t Readlink(const char* aPath, char* aBuff, size_t aSize) {
    return mClient->Readlink(aPath, aBuff, aSize);
  }

  void SetUp() override {
    ipc::FileDescriptor fd;

    mServer = SandboxBroker::Create(GetPolicy(), getpid(), fd);
    ASSERT_NE(mServer, nullptr);
    ASSERT_TRUE(fd.IsValid());
    auto rawFD = fd.TakePlatformHandle();
    mClient.reset(new SandboxBrokerClient(rawFD.release()));
  }

  template <class C, void (C::*Main)()>
  void StartThread(pthread_t* aThread) {
    ASSERT_EQ(0, pthread_create(aThread, nullptr, ThreadMain<C, Main>,
                                static_cast<C*>(this)));
  }

  template <class C, void (C::*Main)()>
  void RunOnManyThreads() {
    static const int kNumThreads = 5;
    pthread_t threads[kNumThreads];
    for (pthread_t& thread : threads) {
      StartThread<C, Main>(&thread);
    }
    for (pthread_t thread : threads) {
      void* retval;
      ASSERT_EQ(pthread_join(thread, &retval), 0);
      ASSERT_EQ(retval, static_cast<void*>(nullptr));
    }
  }

 public:
  void MultiThreadOpenWorker();
  void MultiThreadStatWorker();
};

UniquePtr<const SandboxBroker::Policy> SandboxBrokerTest::GetPolicy() const {
  UniquePtr<SandboxBroker::Policy> policy(new SandboxBroker::Policy());

  policy->AddPath(MAY_READ | MAY_WRITE, "/dev/null", AddAlways);
  policy->AddPath(MAY_READ, "/dev/zero", AddAlways);
  policy->AddPath(MAY_READ, "/var/empty/qwertyuiop", AddAlways);
  policy->AddPath(MAY_ACCESS, "/proc/self",
                  AddAlways);  // Warning: Linux-specific.
  policy->AddPath(MAY_READ | MAY_WRITE, "/tmp", AddAlways);
  policy->AddPath(MAY_READ | MAY_WRITE | MAY_CREATE, "/tmp/blublu", AddAlways);
  policy->AddPath(MAY_READ | MAY_WRITE | MAY_CREATE, "/tmp/blublublu",
                  AddAlways);
  // This should be non-writable by the user running the test:
  policy->AddPath(MAY_READ | MAY_WRITE, "/etc", AddAlways);

  return std::move(policy);
}

TEST_F(SandboxBrokerTest, OpenForRead) {
  int fd;

  fd = Open("/dev/null", O_RDONLY);
  ASSERT_GE(fd, 0) << "Opening /dev/null failed.";
  close(fd);
  fd = Open("/dev/zero", O_RDONLY);
  ASSERT_GE(fd, 0) << "Opening /dev/zero failed.";
  close(fd);
  fd = Open("/var/empty/qwertyuiop", O_RDONLY);
  EXPECT_EQ(-ENOENT, fd) << "Opening allowed but nonexistent file succeeded.";
  fd = Open("/proc/self", O_RDONLY);
  EXPECT_EQ(-EACCES, fd) << "Opening stat-only file for read succeeded.";
  fd = Open("/proc/self/stat", O_RDONLY);
  EXPECT_EQ(-EACCES, fd) << "Opening disallowed file succeeded.";
}

TEST_F(SandboxBrokerTest, OpenForWrite) {
  int fd;

  fd = Open("/dev/null", O_WRONLY);
  ASSERT_GE(fd, 0) << "Opening /dev/null write-only failed.";
  close(fd);
  fd = Open("/dev/null", O_RDWR);
  ASSERT_GE(fd, 0) << "Opening /dev/null read/write failed.";
  close(fd);
  fd = Open("/dev/zero", O_WRONLY);
  ASSERT_EQ(-EACCES, fd)
      << "Opening read-only-by-policy file write-only succeeded.";
  fd = Open("/dev/zero", O_RDWR);
  ASSERT_EQ(-EACCES, fd)
      << "Opening read-only-by-policy file read/write succeeded.";
}

TEST_F(SandboxBrokerTest, SimpleRead) {
  int fd;
  char c;

  fd = Open("/dev/null", O_RDONLY);
  ASSERT_GE(fd, 0);
  EXPECT_EQ(0, read(fd, &c, 1));
  close(fd);
  fd = Open("/dev/zero", O_RDONLY);
  ASSERT_GE(fd, 0);
  ASSERT_EQ(1, read(fd, &c, 1));
  EXPECT_EQ(c, '\0');
}

TEST_F(SandboxBrokerTest, BadFlags) {
  int fd;

  fd = Open("/dev/null", O_RDWR | O_ASYNC);
  EXPECT_EQ(-EACCES, fd) << "O_ASYNC is banned.";

  fd = Open("/dev/null", O_RDWR | 0x40000000);
  EXPECT_EQ(-EACCES, fd) << "Unknown flag 0x40000000 is banned.";
}

TEST_F(SandboxBrokerTest, Access) {
  EXPECT_EQ(0, Access("/dev/null", F_OK));
  EXPECT_EQ(0, Access("/dev/null", R_OK));
  EXPECT_EQ(0, Access("/dev/null", W_OK));
  EXPECT_EQ(0, Access("/dev/null", R_OK | W_OK));
  EXPECT_EQ(-EACCES, Access("/dev/null", X_OK));
  EXPECT_EQ(-EACCES, Access("/dev/null", R_OK | X_OK));

  EXPECT_EQ(0, Access("/dev/zero", R_OK));
  EXPECT_EQ(-EACCES, Access("/dev/zero", W_OK));
  EXPECT_EQ(-EACCES, Access("/dev/zero", R_OK | W_OK));

  EXPECT_EQ(-ENOENT, Access("/var/empty/qwertyuiop", R_OK));
  EXPECT_EQ(-EACCES, Access("/var/empty/qwertyuiop", W_OK));

  EXPECT_EQ(0, Access("/proc/self", F_OK));
  EXPECT_EQ(-EACCES, Access("/proc/self", R_OK));

  EXPECT_EQ(-EACCES, Access("/proc/self/stat", F_OK));

  EXPECT_EQ(0, Access("/tmp", X_OK));
  EXPECT_EQ(0, Access("/tmp", R_OK | X_OK));
  EXPECT_EQ(0, Access("/tmp", R_OK | W_OK | X_OK));
  EXPECT_EQ(0, Access("/proc/self", X_OK));

  EXPECT_EQ(0, Access("/etc", R_OK | X_OK));
  EXPECT_EQ(-EACCES, Access("/etc", W_OK));
}

TEST_F(SandboxBrokerTest, Stat) {
  statstruct realStat, brokeredStat;
  ASSERT_EQ(0, statsyscall("/dev/null", &realStat)) << "Shouldn't ever fail!";
  EXPECT_EQ(0, Stat("/dev/null", &brokeredStat));
  EXPECT_EQ(realStat.st_ino, brokeredStat.st_ino);
  EXPECT_EQ(realStat.st_rdev, brokeredStat.st_rdev);

#if defined(__clang__) || defined(__GNUC__)
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Wnonnull"
#endif
  EXPECT_EQ(-1, statsyscall(nullptr, &realStat));
  EXPECT_EQ(errno, EFAULT);

  EXPECT_EQ(-EFAULT, Stat(nullptr, &brokeredStat));
#if defined(__clang__) || defined(__GNUC__)
#  pragma GCC diagnostic pop
#endif

  EXPECT_EQ(-ENOENT, Stat("/var/empty/qwertyuiop", &brokeredStat));
  EXPECT_EQ(-EACCES, Stat("/dev", &brokeredStat));

  EXPECT_EQ(0, Stat("/proc/self", &brokeredStat));
  EXPECT_TRUE(S_ISDIR(brokeredStat.st_mode));
}

TEST_F(SandboxBrokerTest, LStat) {
  statstruct realStat, brokeredStat;
  ASSERT_EQ(0, lstatsyscall("/dev/null", &realStat));
  EXPECT_EQ(0, LStat("/dev/null", &brokeredStat));
  EXPECT_EQ(realStat.st_ino, brokeredStat.st_ino);
  EXPECT_EQ(realStat.st_rdev, brokeredStat.st_rdev);

  EXPECT_EQ(-ENOENT, LStat("/var/empty/qwertyuiop", &brokeredStat));
  EXPECT_EQ(-EACCES, LStat("/dev", &brokeredStat));

  EXPECT_EQ(0, LStat("/proc/self", &brokeredStat));
  EXPECT_TRUE(S_ISLNK(brokeredStat.st_mode));
}

static void PrePostTestCleanup(void) {
  unlink("/tmp/blublu");
  rmdir("/tmp/blublu");
  unlink("/tmp/nope");
  rmdir("/tmp/nope");
  unlink("/tmp/blublublu");
  rmdir("/tmp/blublublu");
}

TEST_F(SandboxBrokerTest, Chmod) {
  PrePostTestCleanup();

  int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
  ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
  close(fd);
  // Set read only. SandboxBroker enforces 0600 mode flags.
  ASSERT_EQ(0, Chmod("/tmp/blublu", S_IRUSR));
  EXPECT_EQ(-EACCES, Access("/tmp/blublu", W_OK));
  statstruct realStat;
  EXPECT_EQ(0, statsyscall("/tmp/blublu", &realStat));
  EXPECT_EQ((mode_t)S_IRUSR, realStat.st_mode & 0777);

  ASSERT_EQ(0, Chmod("/tmp/blublu", S_IRUSR | S_IWUSR));
  EXPECT_EQ(0, statsyscall("/tmp/blublu", &realStat));
  EXPECT_EQ((mode_t)(S_IRUSR | S_IWUSR), realStat.st_mode & 0777);
  EXPECT_EQ(0, unlink("/tmp/blublu"));

  PrePostTestCleanup();
}

TEST_F(SandboxBrokerTest, Link) {
  PrePostTestCleanup();

  int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
  ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
  close(fd);
  ASSERT_EQ(0, Link("/tmp/blublu""/tmp/blublublu"));
  EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
  // Not whitelisted target path
  EXPECT_EQ(-EACCES, Link("/tmp/blublu""/tmp/nope"));
  EXPECT_EQ(0, unlink("/tmp/blublublu"));
  EXPECT_EQ(0, unlink("/tmp/blublu"));

  PrePostTestCleanup();
}

TEST_F(SandboxBrokerTest, Symlink) {
  PrePostTestCleanup();

  int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
  ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
  close(fd);
  ASSERT_EQ(0, Symlink("/tmp/blublu""/tmp/blublublu"));
  EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
  statstruct aStat;
  ASSERT_EQ(0, lstatsyscall("/tmp/blublublu", &aStat));
  EXPECT_EQ((mode_t)S_IFLNK, aStat.st_mode & S_IFMT);
  // Not whitelisted target path
  EXPECT_EQ(-EACCES, Symlink("/tmp/blublu""/tmp/nope"));
  EXPECT_EQ(0, unlink("/tmp/blublublu"));
  EXPECT_EQ(0, unlink("/tmp/blublu"));

  PrePostTestCleanup();
}

TEST_F(SandboxBrokerTest, Mkdir) {
  PrePostTestCleanup();

  ASSERT_EQ(0, mkdir("/tmp/blublu", 0600))
      << "Creating dir /tmp/blublu failed.";
  EXPECT_EQ(0, Access("/tmp/blublu", F_OK));
  // Not whitelisted target path
  EXPECT_EQ(-EACCES, Mkdir("/tmp/nope", 0600))
      << "Creating dir without MAY_CREATE succeed.";
  EXPECT_EQ(0, rmdir("/tmp/blublu"));
  EXPECT_EQ(-EEXIST, Mkdir("/proc/self", 0600))
      << "Creating uncreatable dir that already exists didn't fail correctly.";
  EXPECT_EQ(-EEXIST, Mkdir("/dev/zero", 0600))
      << "Creating uncreatable dir over preexisting file didn't fail "
         "correctly.";

  PrePostTestCleanup();
}

TEST_F(SandboxBrokerTest, Rename) {
  PrePostTestCleanup();

  ASSERT_EQ(0, mkdir("/tmp/blublu", 0600))
      << "Creating dir /tmp/blublu failed.";
  EXPECT_EQ(0, Access("/tmp/blublu", F_OK));
  ASSERT_EQ(0, Rename("/tmp/blublu""/tmp/blublublu"));
  EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
  EXPECT_EQ(-ENOENT, Access("/tmp/blublu", F_OK));
  // Not whitelisted target path
  EXPECT_EQ(-EACCES, Rename("/tmp/blublublu""/tmp/nope"))
      << "Renaming dir without write access succeed.";
  EXPECT_EQ(0, rmdir("/tmp/blublublu"));

  PrePostTestCleanup();
}

TEST_F(SandboxBrokerTest, Rmdir) {
  PrePostTestCleanup();

  ASSERT_EQ(0, mkdir("/tmp/blublu", 0600))
      << "Creating dir /tmp/blublu failed.";
  EXPECT_EQ(0, Access("/tmp/blublu", F_OK));
  ASSERT_EQ(0, Rmdir("/tmp/blublu"));
  EXPECT_EQ(-ENOENT, Access("/tmp/blublu", F_OK));
  // Bypass sandbox to create a non-deletable dir
  ASSERT_EQ(0, mkdir("/tmp/nope", 0600));
  EXPECT_EQ(-EACCES, Rmdir("/tmp/nope"));

  PrePostTestCleanup();
}

TEST_F(SandboxBrokerTest, Unlink) {
  PrePostTestCleanup();

  int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
  ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
  close(fd);
  EXPECT_EQ(0, Access("/tmp/blublu", F_OK));
  EXPECT_EQ(0, Unlink("/tmp/blublu"));
  EXPECT_EQ(-ENOENT, Access("/tmp/blublu", F_OK));
  // Bypass sandbox to write a non-deletable file
  fd = open("/tmp/nope", O_WRONLY | O_CREAT, 0600);
  ASSERT_GE(fd, 0) << "Opening /tmp/nope for writing failed.";
  close(fd);
  EXPECT_EQ(-EACCES, Unlink("/tmp/nope"));

  PrePostTestCleanup();
}

TEST_F(SandboxBrokerTest, Readlink) {
  PrePostTestCleanup();

  int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
  ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
  close(fd);
  ASSERT_EQ(0, Symlink("/tmp/blublu""/tmp/blublublu"));
  EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
  char linkBuff[256];
  EXPECT_EQ(11, Readlink("/tmp/blublublu", linkBuff, sizeof(linkBuff)));
  linkBuff[11] = '\0';
  EXPECT_EQ(0, strcmp(linkBuff, "/tmp/blublu"));

  PrePostTestCleanup();
}

TEST_F(SandboxBrokerTest, MultiThreadOpen) {
  RunOnManyThreads<SandboxBrokerTest,
                   &SandboxBrokerTest::MultiThreadOpenWorker>();
}
void SandboxBrokerTest::MultiThreadOpenWorker() {
  static const int kNumLoops = 10000;

  for (int i = 1; i <= kNumLoops; ++i) {
    int nullfd = Open("/dev/null", O_RDONLY);
    int zerofd = Open("/dev/zero", O_RDONLY);
    ASSERT_GE(nullfd, 0) << "Loop " << i << "/" << kNumLoops;
    ASSERT_GE(zerofd, 0) << "Loop " << i << "/" << kNumLoops;
    char c;
    ASSERT_EQ(0, read(nullfd, &c, 1)) << "Loop " << i << "/" << kNumLoops;
    ASSERT_EQ(1, read(zerofd, &c, 1)) << "Loop " << i << "/" << kNumLoops;
    ASSERT_EQ('\0', c) << "Loop " << i << "/" << kNumLoops;
    close(nullfd);
    close(zerofd);
  }
}

TEST_F(SandboxBrokerTest, MultiThreadStat) {
  RunOnManyThreads<SandboxBrokerTest,
                   &SandboxBrokerTest::MultiThreadStatWorker>();
}
void SandboxBrokerTest::MultiThreadStatWorker() {
  static const int kNumLoops = 7500;
  statstruct nullStat, zeroStat, selfStat;
  dev_t realNullDev, realZeroDev;
  ino_t realSelfInode;

  ASSERT_EQ(0, statsyscall("/dev/null", &nullStat)) << "Shouldn't ever fail!";
  ASSERT_EQ(0, statsyscall("/dev/zero", &zeroStat)) << "Shouldn't ever fail!";
  ASSERT_EQ(0, lstatsyscall("/proc/self", &selfStat)) << "Shouldn't ever fail!";
  ASSERT_TRUE(S_ISLNK(selfStat.st_mode))
  << "Shouldn't ever fail!";
  realNullDev = nullStat.st_rdev;
  realZeroDev = zeroStat.st_rdev;
  realSelfInode = selfStat.st_ino;
  for (int i = 1; i <= kNumLoops; ++i) {
    ASSERT_EQ(0, Stat("/dev/null", &nullStat))
        << "Loop " << i << "/" << kNumLoops;
    ASSERT_EQ(0, Stat("/dev/zero", &zeroStat))
        << "Loop " << i << "/" << kNumLoops;
    ASSERT_EQ(0, LStat("/proc/self", &selfStat))
        << "Loop " << i << "/" << kNumLoops;

    ASSERT_EQ(realNullDev, nullStat.st_rdev)
        << "Loop " << i << "/" << kNumLoops;
    ASSERT_EQ(realZeroDev, zeroStat.st_rdev)
        << "Loop " << i << "/" << kNumLoops;
    ASSERT_TRUE(S_ISLNK(selfStat.st_mode))
    << "Loop " << i << "/" << kNumLoops;
    ASSERT_EQ(realSelfInode, selfStat.st_ino)
        << "Loop " << i << "/" << kNumLoops;
  }
}

#if 0
class SandboxBrokerSigStress : public SandboxBrokerTest
{
  int mSigNum;
  struct sigaction mOldAction;
  Atomic<void*> mVoidPtr;

  static void SigHandler(int aSigNum, siginfo_t* aSigInfo, void *aCtx) {
    ASSERT_EQ(SI_QUEUE, aSigInfo->si_code);
    SandboxBrokerSigStress* that =
      static_cast<SandboxBrokerSigStress*>(aSigInfo->si_value.sival_ptr);
    ASSERT_EQ(that->mSigNum, aSigNum);
    that->DoSomething();
  }

protected:
  Atomic<int> mTestIter;
  sem_t mSemaphore;

  void SignalThread(pthread_t aThread) {
    union sigval sv;
    sv.sival_ptr = this;
    ASSERT_NE(0, mSigNum);
    ASSERT_EQ(0, pthread_sigqueue(aThread, mSigNum, sv));
  }

  virtual void SetUp() {
    ASSERT_EQ(0, sem_init(&mSemaphore, 0, 0));
    mVoidPtr = nullptr;
    mSigNum = 0;
    for (int sigNum = SIGRTMIN; sigNum < SIGRTMAX; ++sigNum) {
      ASSERT_EQ(0, sigaction(sigNum, nullptr, &mOldAction));
      if ((mOldAction.sa_flags & SA_SIGINFO) == 0 &&
          mOldAction.sa_handler == SIG_DFL) {
        struct sigaction newAction;
        PodZero(&newAction);
        newAction.sa_flags = SA_SIGINFO;
        newAction.sa_sigaction = SigHandler;
        ASSERT_EQ(0, sigaction(sigNum, &newAction, nullptr));
        mSigNum = sigNum;
        break;
      }
    }
    ASSERT_NE(mSigNum, 0);

    SandboxBrokerTest::SetUp();
  }

  virtual void TearDown() {
    ASSERT_EQ(0, sem_destroy(&mSemaphore));
    if (mSigNum != 0) {
      ASSERT_EQ(0, sigaction(mSigNum, &mOldAction, nullptr));
    }
    if (mVoidPtr) {
      free(mVoidPtr);
    }
  }

  void DoSomething();

public:
  void MallocWorker();
  void FreeWorker();
};

TEST_F(SandboxBrokerSigStress, StressTest)
{
  static const int kIters = 6250;
  static const int kNsecPerIterPerIter = 4;
  struct timespec delay = { 0, 0 };
  pthread_t threads[2];

  mTestIter = kIters;

  StartThread<SandboxBrokerSigStress,
              &SandboxBrokerSigStress::MallocWorker>(&threads[0]);
  StartThread<SandboxBrokerSigStress,
              &SandboxBrokerSigStress::FreeWorker>(&threads[1]);

  for (int i = kIters; i > 0; --i) {
    SignalThread(threads[i % 2]);
    while (sem_wait(&mSemaphore) == -1 && errno == EINTR)
      /* retry */;
    ASSERT_EQ(i - 1, mTestIter);
    delay.tv_nsec += kNsecPerIterPerIter;
    struct timespec req = delay, rem;
    while (nanosleep(&req, &rem) == -1 && errno == EINTR) {
      req = rem;
    }
  }
  void *retval;
  ASSERT_EQ(0, pthread_join(threads[0], &retval));
  ASSERT_EQ(nullptr, retval);
  ASSERT_EQ(0, pthread_join(threads[1], &retval));
  ASSERT_EQ(nullptr, retval);
}

void
SandboxBrokerSigStress::MallocWorker()
{
  static const size_t kSize = 64;

  void* mem = malloc(kSize);
  while (mTestIter > 0) {
    ASSERT_NE(mem, mVoidPtr);
    mem = mVoidPtr.exchange(mem);
    if (mem) {
      sched_yield();
    } else {
      mem = malloc(kSize);
    }
  }
  if (mem) {
    free(mem);
  }
}

void
SandboxBrokerSigStress::FreeWorker()
{
  void *mem = nullptr;
  while (mTestIter > 0) {
    mem = mVoidPtr.exchange(mem);
    if (mem) {
      free(mem);
      mem = nullptr;
    } else {
      sched_yield();
    }
  }
}

void
SandboxBrokerSigStress::DoSomething()
{
  int fd;
  char c;
  struct stat st;

  //fprintf(stderr, "Don't try this at home: %d\n", static_cast<int>(mTestIter));
  switch (mTestIter % 5) {
  case 0:
    fd = Open("/dev/null", O_RDWR);
    ASSERT_GE(fd, 0);
    ASSERT_EQ(0, read(fd, &c, 1));
    close(fd);
    break;
  case 1:
    fd = Open("/dev/zero", O_RDONLY);
    ASSERT_GE(fd, 0);
    ASSERT_EQ(1, read(fd, &c, 1));
    ASSERT_EQ('\0', c);
    close(fd);
    break;
  case 2:
    ASSERT_EQ(0, Access("/dev/null", W_OK));
    break;
  case 3:
    ASSERT_EQ(0, Stat("/proc/self", &st));
    ASSERT_TRUE(S_ISDIR(st.st_mode));
    break;
  case 4:
    ASSERT_EQ(0, LStat("/proc/self", &st));
    ASSERT_TRUE(S_ISLNK(st.st_mode));
    break;
  }
  mTestIter--;
  sem_post(&mSemaphore);
}
#endif

// Check for fd leaks when creating/destroying a broker instance (bug
// 1719391).
//
// (This uses a different test group because it doesn't use the
// fixture class, and gtest doesn't allow mixing TEST and TEST_F in
// the same group.)
TEST(SandboxBrokerMisc, LeakCheck)
{
  // If this value is increased in the future, check that it won't
  // cause the test to take an excessive amount of time:
  static constexpr size_t kCycles = 4096;
  struct rlimit oldLimit;
  bool changedLimit = false;

  // At the time of this writing, we raise the fd soft limit to 4096
  // (or to the hard limit, if less than that), but we don't lower it
  // if it was higher than 4096.  To allow for that case, or if
  // Gecko's preferred limit changes, the limit is reduced while this
  // test is running:
  ASSERT_EQ(getrlimit(RLIMIT_NOFILE, &oldLimit), 0) << strerror(errno);
  if (oldLimit.rlim_cur == RLIM_INFINITY ||
      oldLimit.rlim_cur > static_cast<rlim_t>(kCycles)) {
    struct rlimit newLimit = oldLimit;
    newLimit.rlim_cur = static_cast<rlim_t>(kCycles);
    ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &newLimit), 0) << strerror(errno);
    changedLimit = true;
  }

  pid_t pid = getpid();
  for (size_t i = 0; i < kCycles; ++i) {
    auto policy = MakeUnique<SandboxBroker::Policy>();
    // Currently nothing in `Create` tries to check for or
    // special-case an empty policy, but just in case:
    policy->AddPath(SandboxBroker::MAY_READ, "/dev/null",
                    SandboxBroker::Policy::AddAlways);
    ipc::FileDescriptor fd;
    auto broker = SandboxBroker::Create(std::move(policy), pid, fd);
    ASSERT_TRUE(broker);
    ASSERT_TRUE(fd.IsValid());
  }

  if (changedLimit) {
    ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &oldLimit), 0) << strerror(errno);
  }
}

}  // namespace mozilla

Messung V0.5
C=96 H=91 G=93

¤ Dauer der Verarbeitung: 0.20 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.