blob: 60d5c030c4fbd96ba5b669f1cb1108a82693a18c [file] [log] [blame]
Mike Frysinger50e31fa2018-01-19 18:59:49 -05001/* 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 */
Mike Frysinger0b5cffa2017-08-15 18:06:18 -04007
yusukes76a9d742018-03-05 10:20:22 -08008#include <limits.h>
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -04009#include <linux/securebits.h>
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040010#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
Mike Frysinger5fdba4e2018-01-17 15:39:48 -050013#include <sys/stat.h>
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -070014#include <sys/statvfs.h>
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040015#include <unistd.h>
16
17#include <gtest/gtest.h>
18
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -070019#include <string>
20
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040021#include "system.h"
22
23namespace {
24
25// A random path that really really should not exist on the host.
26const char kNoSuchDir[] = "/.x/..x/...x/path/should/not/exist/";
27
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -040028// A random file that should exist on both Linux and Android.
29const char kValidFile[] = "/etc/hosts";
Mike Frysingereaab4202017-08-14 14:57:21 -040030
31// A random directory that should exist.
32const char kValidDir[] = "/";
33
34// A random character device that should exist.
35const char kValidCharDev[] = "/dev/null";
36
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -040037inline bool is_android() {
38#if defined(__ANDROID__)
39 return true;
40#else
41 return false;
42#endif
43}
44
45// Return a temp filename that this test can manipulate.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -070046// It will not exist when it returns.
47std::string get_temp_path() {
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -040048 char *path = nullptr;
49 if (is_android()) {
50 path = strdup("/data/local/tmp/minijail.tests.XXXXXX");
51 } else {
52 path = strdup("minijail.tests.XXXXXX");
53 }
54
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040055 if (!path)
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -070056 return std::string();
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040057
58 // Just create the temp path.
59 int fd = mkstemp(path);
60 if (fd < 0)
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -070061 return std::string();
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040062 close(fd);
63 unlink(path);
64
65 return path;
66}
67
68} // namespace
69
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -040070TEST(secure_noroot_set_and_locked, zero_mask) {
71 ASSERT_EQ(secure_noroot_set_and_locked(0), 0);
72}
73
74TEST(secure_noroot_set_and_locked, set) {
75 ASSERT_EQ(secure_noroot_set_and_locked(issecure_mask(SECURE_NOROOT) |
76 issecure_mask(SECURE_NOROOT_LOCKED)),
77 1);
78}
79
80TEST(secure_noroot_set_and_locked, not_set) {
81 ASSERT_EQ(secure_noroot_set_and_locked(issecure_mask(SECURE_KEEP_CAPS) |
82 issecure_mask(SECURE_NOROOT_LOCKED)),
83 0);
84}
85
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040086// Sanity check for the cap range.
87TEST(get_last_valid_cap, basic) {
88 unsigned int cap = get_last_valid_cap();
89
90 // We pick 35 as it's been that since at least v3.0.
91 // If this test is run on older kernels, it might fail.
92 EXPECT_GE(cap, 35u);
93
94 // Pick a really large number that we probably won't hit for a long time.
95 // It helps that caps are bitfields.
96 EXPECT_LT(cap, 128u);
97}
98
99// Might be useful to figure out the return value, but for now,
100// just make sure it doesn't crash?
101TEST(cap_ambient_supported, smoke) {
102 cap_ambient_supported();
103}
104
105// Invalid indexes should return errors, not crash.
106TEST(setup_pipe_end, bad_index) {
107 EXPECT_LT(setup_pipe_end(nullptr, 2), 0);
108 EXPECT_LT(setup_pipe_end(nullptr, 3), 0);
109 EXPECT_LT(setup_pipe_end(nullptr, 4), 0);
110}
111
112// Verify getting the first fd works.
113TEST(setup_pipe_end, index0) {
114 int fds[2];
115 EXPECT_EQ(0, pipe(fds));
116 // This should close fds[1] and return fds[0].
117 EXPECT_EQ(fds[0], setup_pipe_end(fds, 0));
118 // Use close() to verify open/close state.
119 EXPECT_EQ(-1, close(fds[1]));
120 EXPECT_EQ(0, close(fds[0]));
121}
122
123// Verify getting the second fd works.
124TEST(setup_pipe_end, index1) {
125 int fds[2];
126 EXPECT_EQ(0, pipe(fds));
127 // This should close fds[0] and return fds[1].
128 EXPECT_EQ(fds[1], setup_pipe_end(fds, 1));
129 // Use close() to verify open/close state.
130 EXPECT_EQ(-1, close(fds[0]));
131 EXPECT_EQ(0, close(fds[1]));
132}
133
134// Invalid indexes should return errors, not crash.
135TEST(setup_and_dupe_pipe_end, bad_index) {
136 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 2, -1), 0);
137 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 3, -1), 0);
138 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 4, -1), 0);
139}
140
141// An invalid path should return an error.
142TEST(write_pid_to_path, bad_path) {
143 EXPECT_NE(0, write_pid_to_path(0, kNoSuchDir));
144}
145
146// Make sure we can write a pid to the file.
147TEST(write_pid_to_path, basic) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700148 std::string path = get_temp_path();
149 ASSERT_NE(std::string(), path);
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400150
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700151 EXPECT_EQ(0, write_pid_to_path(1234, path.c_str()));
152 FILE *fp = fopen(path.c_str(), "re");
153 unlink(path.c_str());
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400154 EXPECT_NE(nullptr, fp);
lhchavez24b64c22017-09-01 03:52:13 +0000155 char data[6] = {};
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400156 EXPECT_EQ(5u, fread(data, 1, sizeof(data), fp));
157 fclose(fp);
158 EXPECT_EQ(0, strcmp(data, "1234\n"));
Mike Frysingereaab4202017-08-14 14:57:21 -0400159}
160
161// If the destination exists, there's nothing to do.
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500162// Also check trailing slash handling.
163TEST(mkdir_p, dest_exists) {
164 EXPECT_EQ(0, mkdir_p("/", 0, true));
165 EXPECT_EQ(0, mkdir_p("///", 0, true));
166 EXPECT_EQ(0, mkdir_p("/proc", 0, true));
167 EXPECT_EQ(0, mkdir_p("/proc/", 0, true));
168 EXPECT_EQ(0, mkdir_p("/dev", 0, true));
169 EXPECT_EQ(0, mkdir_p("/dev/", 0, true));
170}
171
172// Create a directory tree that doesn't exist.
173TEST(mkdir_p, create_tree) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700174 std::string path = get_temp_path();
175 ASSERT_NE(std::string(), path);
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500176
177 // Run `mkdir -p <path>/a/b/c`.
178 char *path_a, *path_a_b, *path_a_b_c;
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700179 ASSERT_NE(-1, asprintf(&path_a, "%s/a", path.c_str()));
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500180 ASSERT_NE(-1, asprintf(&path_a_b, "%s/b", path_a));
181 ASSERT_NE(-1, asprintf(&path_a_b_c, "%s/c", path_a_b));
182
183 // First try creating it as a file.
184 EXPECT_EQ(0, mkdir_p(path_a_b_c, 0700, false));
185
186 // Make sure the final path doesn't exist yet.
187 struct stat st;
188 EXPECT_EQ(0, stat(path_a_b, &st));
189 EXPECT_EQ(true, S_ISDIR(st.st_mode));
190 EXPECT_EQ(-1, stat(path_a_b_c, &st));
191
192 // Then create it as a complete dir.
193 EXPECT_EQ(0, mkdir_p(path_a_b_c, 0700, true));
194
195 // Make sure the final dir actually exists.
196 EXPECT_EQ(0, stat(path_a_b_c, &st));
197 EXPECT_EQ(true, S_ISDIR(st.st_mode));
198
199 // Clean up.
200 ASSERT_EQ(0, rmdir(path_a_b_c));
201 ASSERT_EQ(0, rmdir(path_a_b));
202 ASSERT_EQ(0, rmdir(path_a));
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700203 ASSERT_EQ(0, rmdir(path.c_str()));
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500204
205 free(path_a_b_c);
206 free(path_a_b);
207 free(path_a);
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500208}
209
210// If the destination exists, there's nothing to do.
Mike Frysingereaab4202017-08-14 14:57:21 -0400211TEST(setup_mount_destination, dest_exists) {
212 // Pick some paths that should always exist. We pass in invalid pointers
213 // for other args so we crash if the dest check doesn't short circuit.
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700214 EXPECT_EQ(0, setup_mount_destination(nullptr, kValidDir, 0, 0, false,
215 nullptr));
216 EXPECT_EQ(0, setup_mount_destination(nullptr, "/proc", 0, 0, true, nullptr));
217 EXPECT_EQ(0, setup_mount_destination(nullptr, "/dev", 0, 0, false, nullptr));
218}
219
220// Mount flags should be obtained for bind-mounts
221TEST(setup_mount_destination, mount_flags) {
222 struct statvfs stvfs_buf;
223 ASSERT_EQ(0, statvfs("/proc", &stvfs_buf));
224
225 std::string path = get_temp_path();
226 ASSERT_NE(std::string(), path);
227
228 unsigned long mount_flags = -1;
229 // Passing -1 for user ID/group ID tells chown to make no changes.
230 EXPECT_EQ(0, setup_mount_destination("/proc", path.c_str(), -1, -1, true,
231 &mount_flags));
232 EXPECT_EQ(stvfs_buf.f_flag, mount_flags);
233 EXPECT_EQ(0, rmdir(path.c_str()));
234
235 // Same thing holds for children of a mount.
236 mount_flags = -1;
237 EXPECT_EQ(0, setup_mount_destination("/proc/self", path.c_str(), -1, -1, true,
238 &mount_flags));
239 EXPECT_EQ(stvfs_buf.f_flag, mount_flags);
240 EXPECT_EQ(0, rmdir(path.c_str()));
Mike Frysingereaab4202017-08-14 14:57:21 -0400241}
242
243// When given a bind mount where the source is relative, reject it.
244TEST(setup_mount_destination, reject_relative_bind) {
245 // Pick a destination we know doesn't exist.
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700246 EXPECT_NE(0, setup_mount_destination("foo", kNoSuchDir, 0, 0, true, nullptr));
Mike Frysingereaab4202017-08-14 14:57:21 -0400247}
248
249// A mount of a pseudo filesystem should make the destination dir.
250TEST(setup_mount_destination, create_pseudo_fs) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700251 std::string path = get_temp_path();
252 ASSERT_NE(std::string(), path);
Mike Frysingereaab4202017-08-14 14:57:21 -0400253
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400254 // Passing -1 for user ID/group ID tells chown to make no changes.
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700255 EXPECT_EQ(0, setup_mount_destination("none", path.c_str(), -1, -1, false,
256 nullptr));
Mike Frysingereaab4202017-08-14 14:57:21 -0400257 // We check it's a directory by deleting it as such.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700258 EXPECT_EQ(0, rmdir(path.c_str()));
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400259
260 // Confirm that a bad user ID/group ID fails the function as expected.
261 // On Android, Bionic manages user IDs directly: there is no /etc/passwd file.
262 // This results in most user IDs being valid. Instead of trying to find an
263 // invalid user ID, just skip this check.
264 if (!is_android()) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700265 EXPECT_NE(0, setup_mount_destination("none", path.c_str(),
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700266 UINT_MAX / 2, UINT_MAX / 2, false,
267 nullptr));
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400268 }
Mike Frysingereaab4202017-08-14 14:57:21 -0400269}
270
271// If the source path does not exist, we should error out.
272TEST(setup_mount_destination, missing_source) {
273 // The missing dest path is so we can exercise the source logic.
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700274 EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, false,
275 nullptr));
276 EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, true,
277 nullptr));
Mike Frysingereaab4202017-08-14 14:57:21 -0400278}
279
280// A bind mount of a directory should create the destination dir.
281TEST(setup_mount_destination, create_bind_dir) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700282 std::string path = get_temp_path();
283 ASSERT_NE(std::string(), path);
Mike Frysingereaab4202017-08-14 14:57:21 -0400284
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400285 // Passing -1 for user ID/group ID tells chown to make no changes.
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700286 EXPECT_EQ(0, setup_mount_destination(kValidDir, path.c_str(), -1, -1, true,
287 nullptr));
Mike Frysingereaab4202017-08-14 14:57:21 -0400288 // We check it's a directory by deleting it as such.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700289 EXPECT_EQ(0, rmdir(path.c_str()));
Mike Frysingereaab4202017-08-14 14:57:21 -0400290}
291
292// A bind mount of a file should create the destination file.
293TEST(setup_mount_destination, create_bind_file) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700294 std::string path = get_temp_path();
295 ASSERT_NE(std::string(), path);
Mike Frysingereaab4202017-08-14 14:57:21 -0400296
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400297 // Passing -1 for user ID/group ID tells chown to make no changes.
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700298 EXPECT_EQ(0, setup_mount_destination(kValidFile, path.c_str(), -1, -1, true,
299 nullptr));
Mike Frysingereaab4202017-08-14 14:57:21 -0400300 // We check it's a file by deleting it as such.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700301 EXPECT_EQ(0, unlink(path.c_str()));
Mike Frysingereaab4202017-08-14 14:57:21 -0400302}
303
304// A mount of a character device should create the destination char.
305TEST(setup_mount_destination, create_char_dev) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700306 std::string path = get_temp_path();
307 ASSERT_NE(std::string(), path);
Mike Frysingereaab4202017-08-14 14:57:21 -0400308
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400309 // Passing -1 for user ID/group ID tells chown to make no changes.
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700310 EXPECT_EQ(0, setup_mount_destination(kValidCharDev, path.c_str(), -1, -1,
311 false, nullptr));
Mike Frysingereaab4202017-08-14 14:57:21 -0400312 // We check it's a directory by deleting it as such.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700313 EXPECT_EQ(0, rmdir(path.c_str()));
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400314}