blob: 02d1401eba5537a17b4446751b21ffd396ded063 [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>
Mike Frysinger0b5cffa2017-08-15 18:06:18 -04009#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
Mike Frysinger5fdba4e2018-01-17 15:39:48 -050012#include <sys/stat.h>
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040013#include <unistd.h>
14
15#include <gtest/gtest.h>
16
17#include "system.h"
18
19namespace {
20
21// A random path that really really should not exist on the host.
22const char kNoSuchDir[] = "/.x/..x/...x/path/should/not/exist/";
23
Mike Frysingereaab4202017-08-14 14:57:21 -040024// A random file that should exist.
25const char kValidFile[] = "/etc/passwd";
26
27// A random directory that should exist.
28const char kValidDir[] = "/";
29
30// A random character device that should exist.
31const char kValidCharDev[] = "/dev/null";
32
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040033// Return a temp filename in the cwd that this test can manipulate.
34// It will not exist when it returns, and the user has to free the memory.
35char *get_temp_path() {
36 char *path = strdup("minijail.tests.XXXXXX");
37 if (!path)
38 return nullptr;
39
40 // Just create the temp path.
41 int fd = mkstemp(path);
42 if (fd < 0)
43 return nullptr;
44 close(fd);
45 unlink(path);
46
47 return path;
48}
49
50} // namespace
51
52// Sanity check for the cap range.
53TEST(get_last_valid_cap, basic) {
54 unsigned int cap = get_last_valid_cap();
55
56 // We pick 35 as it's been that since at least v3.0.
57 // If this test is run on older kernels, it might fail.
58 EXPECT_GE(cap, 35u);
59
60 // Pick a really large number that we probably won't hit for a long time.
61 // It helps that caps are bitfields.
62 EXPECT_LT(cap, 128u);
63}
64
65// Might be useful to figure out the return value, but for now,
66// just make sure it doesn't crash?
67TEST(cap_ambient_supported, smoke) {
68 cap_ambient_supported();
69}
70
71// Invalid indexes should return errors, not crash.
72TEST(setup_pipe_end, bad_index) {
73 EXPECT_LT(setup_pipe_end(nullptr, 2), 0);
74 EXPECT_LT(setup_pipe_end(nullptr, 3), 0);
75 EXPECT_LT(setup_pipe_end(nullptr, 4), 0);
76}
77
78// Verify getting the first fd works.
79TEST(setup_pipe_end, index0) {
80 int fds[2];
81 EXPECT_EQ(0, pipe(fds));
82 // This should close fds[1] and return fds[0].
83 EXPECT_EQ(fds[0], setup_pipe_end(fds, 0));
84 // Use close() to verify open/close state.
85 EXPECT_EQ(-1, close(fds[1]));
86 EXPECT_EQ(0, close(fds[0]));
87}
88
89// Verify getting the second fd works.
90TEST(setup_pipe_end, index1) {
91 int fds[2];
92 EXPECT_EQ(0, pipe(fds));
93 // This should close fds[0] and return fds[1].
94 EXPECT_EQ(fds[1], setup_pipe_end(fds, 1));
95 // Use close() to verify open/close state.
96 EXPECT_EQ(-1, close(fds[0]));
97 EXPECT_EQ(0, close(fds[1]));
98}
99
100// Invalid indexes should return errors, not crash.
101TEST(setup_and_dupe_pipe_end, bad_index) {
102 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 2, -1), 0);
103 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 3, -1), 0);
104 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 4, -1), 0);
105}
106
107// An invalid path should return an error.
108TEST(write_pid_to_path, bad_path) {
109 EXPECT_NE(0, write_pid_to_path(0, kNoSuchDir));
110}
111
112// Make sure we can write a pid to the file.
113TEST(write_pid_to_path, basic) {
114 char *path = get_temp_path();
115 ASSERT_NE(nullptr, path);
116
117 EXPECT_EQ(0, write_pid_to_path(1234, path));
118 FILE *fp = fopen(path, "re");
119 unlink(path);
120 EXPECT_NE(nullptr, fp);
lhchavez24b64c22017-09-01 03:52:13 +0000121 char data[6] = {};
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400122 EXPECT_EQ(5u, fread(data, 1, sizeof(data), fp));
123 fclose(fp);
124 EXPECT_EQ(0, strcmp(data, "1234\n"));
Mike Frysingereaab4202017-08-14 14:57:21 -0400125
126 free(path);
127}
128
129// If the destination exists, there's nothing to do.
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500130// Also check trailing slash handling.
131TEST(mkdir_p, dest_exists) {
132 EXPECT_EQ(0, mkdir_p("/", 0, true));
133 EXPECT_EQ(0, mkdir_p("///", 0, true));
134 EXPECT_EQ(0, mkdir_p("/proc", 0, true));
135 EXPECT_EQ(0, mkdir_p("/proc/", 0, true));
136 EXPECT_EQ(0, mkdir_p("/dev", 0, true));
137 EXPECT_EQ(0, mkdir_p("/dev/", 0, true));
138}
139
140// Create a directory tree that doesn't exist.
141TEST(mkdir_p, create_tree) {
142 char *path = get_temp_path();
143 ASSERT_NE(nullptr, path);
144 unlink(path);
145
146 // Run `mkdir -p <path>/a/b/c`.
147 char *path_a, *path_a_b, *path_a_b_c;
148 ASSERT_NE(-1, asprintf(&path_a, "%s/a", path));
149 ASSERT_NE(-1, asprintf(&path_a_b, "%s/b", path_a));
150 ASSERT_NE(-1, asprintf(&path_a_b_c, "%s/c", path_a_b));
151
152 // First try creating it as a file.
153 EXPECT_EQ(0, mkdir_p(path_a_b_c, 0700, false));
154
155 // Make sure the final path doesn't exist yet.
156 struct stat st;
157 EXPECT_EQ(0, stat(path_a_b, &st));
158 EXPECT_EQ(true, S_ISDIR(st.st_mode));
159 EXPECT_EQ(-1, stat(path_a_b_c, &st));
160
161 // Then create it as a complete dir.
162 EXPECT_EQ(0, mkdir_p(path_a_b_c, 0700, true));
163
164 // Make sure the final dir actually exists.
165 EXPECT_EQ(0, stat(path_a_b_c, &st));
166 EXPECT_EQ(true, S_ISDIR(st.st_mode));
167
168 // Clean up.
169 ASSERT_EQ(0, rmdir(path_a_b_c));
170 ASSERT_EQ(0, rmdir(path_a_b));
171 ASSERT_EQ(0, rmdir(path_a));
172 ASSERT_EQ(0, rmdir(path));
173
174 free(path_a_b_c);
175 free(path_a_b);
176 free(path_a);
177 free(path);
178}
179
180// If the destination exists, there's nothing to do.
Mike Frysingereaab4202017-08-14 14:57:21 -0400181TEST(setup_mount_destination, dest_exists) {
182 // Pick some paths that should always exist. We pass in invalid pointers
183 // for other args so we crash if the dest check doesn't short circuit.
184 EXPECT_EQ(0, setup_mount_destination(nullptr, kValidDir, 0, 0, false));
185 EXPECT_EQ(0, setup_mount_destination(nullptr, "/proc", 0, 0, true));
186 EXPECT_EQ(0, setup_mount_destination(nullptr, "/dev", 0, 0, false));
187}
188
189// When given a bind mount where the source is relative, reject it.
190TEST(setup_mount_destination, reject_relative_bind) {
191 // Pick a destination we know doesn't exist.
192 EXPECT_NE(0, setup_mount_destination("foo", kNoSuchDir, 0, 0, true));
193}
194
195// A mount of a pseudo filesystem should make the destination dir.
196TEST(setup_mount_destination, create_pseudo_fs) {
197 char *path = get_temp_path();
198 ASSERT_NE(nullptr, path);
199
200 // Passing -1 for uid/gid tells chown to make no changes.
201 EXPECT_EQ(0, setup_mount_destination("none", path, -1, -1, false));
202 // We check it's a directory by deleting it as such.
203 EXPECT_EQ(0, rmdir(path));
yusukes76a9d742018-03-05 10:20:22 -0800204 // Confirm that a bad uid/gid fails the function as expected.
205 EXPECT_NE(0, setup_mount_destination("none", path, UINT_MAX / 2, UINT_MAX / 2, false));
Mike Frysingereaab4202017-08-14 14:57:21 -0400206
207 free(path);
208}
209
210// If the source path does not exist, we should error out.
211TEST(setup_mount_destination, missing_source) {
212 // The missing dest path is so we can exercise the source logic.
213 EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, false));
214 EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, true));
215}
216
217// A bind mount of a directory should create the destination dir.
218TEST(setup_mount_destination, create_bind_dir) {
219 char *path = get_temp_path();
220 ASSERT_NE(nullptr, path);
221
222 // Passing -1 for uid/gid tells chown to make no changes.
223 EXPECT_EQ(0, setup_mount_destination(kValidDir, path, -1, -1, true));
224 // We check it's a directory by deleting it as such.
225 EXPECT_EQ(0, rmdir(path));
226
227 free(path);
228}
229
230// A bind mount of a file should create the destination file.
231TEST(setup_mount_destination, create_bind_file) {
232 char *path = get_temp_path();
233 ASSERT_NE(nullptr, path);
234
235 // Passing -1 for uid/gid tells chown to make no changes.
236 EXPECT_EQ(0, setup_mount_destination(kValidFile, path, -1, -1, true));
237 // We check it's a file by deleting it as such.
238 EXPECT_EQ(0, unlink(path));
239
240 free(path);
241}
242
243// A mount of a character device should create the destination char.
244TEST(setup_mount_destination, create_char_dev) {
245 char *path = get_temp_path();
246 ASSERT_NE(nullptr, path);
247
248 // Passing -1 for uid/gid tells chown to make no changes.
249 EXPECT_EQ(0, setup_mount_destination(kValidCharDev, path, -1, -1, false));
250 // We check it's a directory by deleting it as such.
251 EXPECT_EQ(0, rmdir(path));
252
253 free(path);
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400254}