code-intelligence.com/cifuzz@v0.40.0/third-party/minijail/system_unittest.cc (about)

     1  /* Copyright 2017 The Chromium OS Authors. All rights reserved.
     2   * Use of this source code is governed by a BSD-style license that can be
     3   * found in the LICENSE file.
     4   *
     5   * Test system.[ch] module code using gtest.
     6   */
     7  
     8  #include <ftw.h>
     9  #include <limits.h>
    10  #include <linux/securebits.h>
    11  #include <stdio.h>
    12  #include <stdlib.h>
    13  #include <string.h>
    14  #include <sys/stat.h>
    15  #include <sys/statvfs.h>
    16  #include <unistd.h>
    17  
    18  #include <gtest/gtest.h>
    19  
    20  #include <string>
    21  
    22  #include "system.h"
    23  
    24  namespace {
    25  
    26  // A random path that really really should not exist on the host.
    27  constexpr const char kNoSuchDir[] = "/.x/..x/...x/path/should/not/exist/";
    28  
    29  // A random file that should exist on both Linux and Android.
    30  constexpr const char kValidFile[] = "/etc/hosts";
    31  
    32  // A random directory that should exist.
    33  constexpr const char kValidDir[] = "/";
    34  
    35  // A random character device that should exist.
    36  constexpr const char kValidCharDev[] = "/dev/null";
    37  
    38  constexpr bool is_android() {
    39  #if defined(__ANDROID__)
    40    return true;
    41  #else
    42    return false;
    43  #endif
    44  }
    45  
    46  // Returns a template path that can be used as an argument to mkstemp / mkdtemp.
    47  constexpr const char* temp_path_pattern() {
    48    if (is_android())
    49      return "/data/local/tmp/minijail.tests.XXXXXX";
    50    else
    51      return "minijail.tests.XXXXXX";
    52  }
    53  
    54  // Recursively deletes the subtree rooted at |path|.
    55  bool rmdir_recursive(const std::string& path) {
    56    auto callback = [](const char* child, const struct stat*, int file_type,
    57                       struct FTW*) -> int {
    58      if (file_type == FTW_DP) {
    59        if (rmdir(child) == -1) {
    60          fprintf(stderr, "rmdir(%s): %s", child, strerror(errno));
    61          return -1;
    62        }
    63      } else if (file_type == FTW_F) {
    64        if (unlink(child) == -1) {
    65          fprintf(stderr, "unlink(%s): %s", child, strerror(errno));
    66          return -1;
    67        }
    68      }
    69      return 0;
    70    };
    71  
    72    return nftw(path.c_str(), callback, 128, FTW_DEPTH) == 0;
    73  }
    74  
    75  // Creates a temporary directory that will be cleaned up upon leaving scope.
    76  class TemporaryDir {
    77   public:
    78    TemporaryDir() : path(temp_path_pattern()) {
    79      if (mkdtemp(const_cast<char*>(path.c_str())) == nullptr)
    80        path.clear();
    81    }
    82    ~TemporaryDir() {
    83      if (!is_valid())
    84        return;
    85      rmdir_recursive(path.c_str());
    86    }
    87  
    88    bool is_valid() const { return !path.empty(); }
    89  
    90    std::string path;
    91  
    92   private:
    93    TemporaryDir(const TemporaryDir&) = delete;
    94    TemporaryDir& operator=(const TemporaryDir&) = delete;
    95  };
    96  
    97  // Creates a named temporary file that will be cleaned up upon leaving scope.
    98  class TemporaryFile {
    99   public:
   100    TemporaryFile() : path(temp_path_pattern()) {
   101      int fd = mkstemp(const_cast<char*>(path.c_str()));
   102      if (fd == -1) {
   103        path.clear();
   104        return;
   105      }
   106      close(fd);
   107    }
   108    ~TemporaryFile() {
   109      if (!is_valid())
   110        return;
   111      unlink(path.c_str());
   112    }
   113  
   114    bool is_valid() const { return !path.empty(); }
   115  
   116    std::string path;
   117  
   118   private:
   119    TemporaryFile(const TemporaryFile&) = delete;
   120    TemporaryFile& operator=(const TemporaryFile&) = delete;
   121  };
   122  
   123  }  // namespace
   124  
   125  TEST(secure_noroot_set_and_locked, zero_mask) {
   126    ASSERT_EQ(secure_noroot_set_and_locked(0), 0);
   127  }
   128  
   129  TEST(secure_noroot_set_and_locked, set) {
   130    ASSERT_EQ(secure_noroot_set_and_locked(issecure_mask(SECURE_NOROOT) |
   131                                           issecure_mask(SECURE_NOROOT_LOCKED)),
   132              1);
   133  }
   134  
   135  TEST(secure_noroot_set_and_locked, not_set) {
   136    ASSERT_EQ(secure_noroot_set_and_locked(issecure_mask(SECURE_KEEP_CAPS) |
   137                                           issecure_mask(SECURE_NOROOT_LOCKED)),
   138              0);
   139  }
   140  
   141  // Sanity check for the cap range.
   142  TEST(get_last_valid_cap, basic) {
   143    unsigned int cap = get_last_valid_cap();
   144  
   145    // We pick 35 as it's been that since at least v3.0.
   146    // If this test is run on older kernels, it might fail.
   147    EXPECT_GE(cap, 35u);
   148  
   149    // Pick a really large number that we probably won't hit for a long time.
   150    // It helps that caps are bitfields.
   151    EXPECT_LT(cap, 128u);
   152  }
   153  
   154  // Might be useful to figure out the return value, but for now,
   155  // just make sure it doesn't crash?
   156  TEST(cap_ambient_supported, smoke) {
   157    cap_ambient_supported();
   158  }
   159  
   160  // An invalid path should return an error.
   161  TEST(write_pid_to_path, bad_path) {
   162    EXPECT_NE(0, write_pid_to_path(0, kNoSuchDir));
   163  }
   164  
   165  // Make sure we can write a pid to the file.
   166  TEST(write_pid_to_path, basic) {
   167    TemporaryFile tmp;
   168    ASSERT_TRUE(tmp.is_valid());
   169  
   170    EXPECT_EQ(0, write_pid_to_path(1234, tmp.path.c_str()));
   171    FILE *fp = fopen(tmp.path.c_str(), "re");
   172    EXPECT_NE(nullptr, fp);
   173    char data[6] = {};
   174    EXPECT_EQ(5u, fread(data, 1, sizeof(data), fp));
   175    fclose(fp);
   176    EXPECT_EQ(0, strcmp(data, "1234\n"));
   177  }
   178  
   179  // If the destination exists, there's nothing to do.
   180  // Also check trailing slash handling.
   181  TEST(mkdir_p, dest_exists) {
   182    EXPECT_EQ(0, mkdir_p("/", 0, true));
   183    EXPECT_EQ(0, mkdir_p("///", 0, true));
   184    EXPECT_EQ(0, mkdir_p("/proc", 0, true));
   185    EXPECT_EQ(0, mkdir_p("/proc/", 0, true));
   186    EXPECT_EQ(0, mkdir_p("/dev", 0, true));
   187    EXPECT_EQ(0, mkdir_p("/dev/", 0, true));
   188  }
   189  
   190  // Create a directory tree that doesn't exist.
   191  TEST(mkdir_p, create_tree) {
   192    TemporaryDir dir;
   193    ASSERT_TRUE(dir.is_valid());
   194  
   195    // Run `mkdir -p <path>/a/b/c`.
   196    std::string path_a = dir.path + "/a";
   197    std::string path_a_b = path_a + "/b";
   198    std::string path_a_b_c = path_a_b + "/c";
   199  
   200    // First try creating it as a file.
   201    EXPECT_EQ(0, mkdir_p(path_a_b_c.c_str(), 0700, false));
   202  
   203    // Make sure the final path doesn't exist yet.
   204    struct stat st;
   205    EXPECT_EQ(0, stat(path_a_b.c_str(), &st));
   206    EXPECT_EQ(true, S_ISDIR(st.st_mode));
   207    EXPECT_EQ(-1, stat(path_a_b_c.c_str(), &st));
   208  
   209    // Then create it as a complete dir.
   210    EXPECT_EQ(0, mkdir_p(path_a_b_c.c_str(), 0700, true));
   211  
   212    // Make sure the final dir actually exists.
   213    EXPECT_EQ(0, stat(path_a_b_c.c_str(), &st));
   214    EXPECT_EQ(true, S_ISDIR(st.st_mode));
   215  }
   216  
   217  // If the destination exists, there's nothing to do.
   218  TEST(setup_mount_destination, dest_exists) {
   219    // Pick some paths that should always exist.  We pass in invalid pointers
   220    // for other args so we crash if the dest check doesn't short circuit.
   221    EXPECT_EQ(0, setup_mount_destination(nullptr, kValidDir, 0, 0, false,
   222                                         nullptr));
   223    EXPECT_EQ(0, setup_mount_destination(nullptr, "/proc", 0, 0, true, nullptr));
   224    EXPECT_EQ(0, setup_mount_destination(nullptr, "/dev", 0, 0, false, nullptr));
   225  }
   226  
   227  // Mount flags should be obtained for bind-mounts.
   228  TEST(setup_mount_destination, mount_flags) {
   229    struct statvfs stvfs_buf;
   230    ASSERT_EQ(0, statvfs("/proc", &stvfs_buf));
   231  
   232    TemporaryDir dir;
   233    ASSERT_TRUE(dir.is_valid());
   234  
   235    unsigned long mount_flags = -1;
   236    // Passing -1 for user ID/group ID tells chown to make no changes.
   237    std::string proc = dir.path + "/proc";
   238    EXPECT_EQ(0, setup_mount_destination("/proc", proc.c_str(), -1, -1, true,
   239                                         &mount_flags));
   240    EXPECT_EQ(stvfs_buf.f_flag, mount_flags);
   241    EXPECT_EQ(0, rmdir(proc.c_str()));
   242  
   243    // Same thing holds for children of a mount.
   244    mount_flags = -1;
   245    std::string proc_self = dir.path + "/proc_self";
   246    EXPECT_EQ(0, setup_mount_destination("/proc/self", proc_self.c_str(), -1, -1,
   247                                         true, &mount_flags));
   248    EXPECT_EQ(stvfs_buf.f_flag, mount_flags);
   249    EXPECT_EQ(0, rmdir(proc_self.c_str()));
   250  }
   251  
   252  // When given a bind mount where the source is relative, reject it.
   253  TEST(setup_mount_destination, reject_relative_bind) {
   254    // Pick a destination we know doesn't exist.
   255    EXPECT_NE(0, setup_mount_destination("foo", kNoSuchDir, 0, 0, true, nullptr));
   256  }
   257  
   258  // A mount of a pseudo filesystem should make the destination dir.
   259  TEST(setup_mount_destination, create_pseudo_fs) {
   260    TemporaryDir dir;
   261    ASSERT_TRUE(dir.is_valid());
   262  
   263    // Passing -1 for user ID/group ID tells chown to make no changes.
   264    std::string no_chmod = dir.path + "/no_chmod";
   265    EXPECT_EQ(0, setup_mount_destination("none", no_chmod.c_str(), -1, -1, false,
   266                                         nullptr));
   267    // We check it's a directory by deleting it as such.
   268    EXPECT_EQ(0, rmdir(no_chmod.c_str()));
   269  
   270    // Confirm that a bad user ID/group ID fails the function as expected.
   271    // On Android, Bionic manages user IDs directly: there is no /etc/passwd file.
   272    // This results in most user IDs being valid. Instead of trying to find an
   273    // invalid user ID, just skip this check.
   274    if (!is_android()) {
   275      std::string with_chmod = dir.path + "/with_chmod";
   276      EXPECT_NE(0, setup_mount_destination("none", with_chmod.c_str(),
   277                                           UINT_MAX / 2, UINT_MAX / 2, false,
   278                                           nullptr));
   279    }
   280  }
   281  
   282  // If the source path does not exist, we should error out.
   283  TEST(setup_mount_destination, missing_source) {
   284    // The missing dest path is so we can exercise the source logic.
   285    EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, false,
   286                                         nullptr));
   287    EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, true,
   288                                         nullptr));
   289  }
   290  
   291  // A bind mount of a directory should create the destination dir.
   292  TEST(setup_mount_destination, create_bind_dir) {
   293    TemporaryDir dir;
   294    ASSERT_TRUE(dir.is_valid());
   295  
   296    // Passing -1 for user ID/group ID tells chown to make no changes.
   297    std::string child_dir = dir.path + "/child_dir";
   298    EXPECT_EQ(0, setup_mount_destination(kValidDir, child_dir.c_str(), -1, -1,
   299                                         true, nullptr));
   300    // We check it's a directory by deleting it as such.
   301    EXPECT_EQ(0, rmdir(child_dir.c_str()));
   302  }
   303  
   304  // A bind mount of a file should create the destination file.
   305  TEST(setup_mount_destination, create_bind_file) {
   306    TemporaryDir dir;
   307    ASSERT_TRUE(dir.is_valid());
   308  
   309    // Passing -1 for user ID/group ID tells chown to make no changes.
   310    std::string child_file = dir.path + "/child_file";
   311    EXPECT_EQ(0, setup_mount_destination(kValidFile, child_file.c_str(), -1, -1,
   312                                         true, nullptr));
   313    // We check it's a file by deleting it as such.
   314    EXPECT_EQ(0, unlink(child_file.c_str()));
   315  }
   316  
   317  // A mount of a character device should create the destination char.
   318  TEST(setup_mount_destination, create_char_dev) {
   319    TemporaryDir dir;
   320    ASSERT_TRUE(dir.is_valid());
   321  
   322    // Passing -1 for user ID/group ID tells chown to make no changes.
   323    std::string child_dev = dir.path + "/child_dev";
   324    EXPECT_EQ(0, setup_mount_destination(kValidCharDev, child_dev.c_str(), -1, -1,
   325                                         false, nullptr));
   326    // We check it's a directory by deleting it as such.
   327    EXPECT_EQ(0, rmdir(child_dev.c_str()));
   328  }
   329  
   330  TEST(seccomp_actions_available, smoke) {
   331    seccomp_ret_log_available();
   332    seccomp_ret_kill_process_available();
   333  }