blob: 9a046244ac1b44fc94648cab3effdd105b83d7a1 [file] [log] [blame]
Mike Frysinger50e31fa2018-01-19 18:59:49 -05001/* Copyright 2016 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 platform independent logic of Minijail using gtest.
6 */
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -04007
8#include <errno.h>
9
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070010#include <dirent.h>
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -040011#include <fcntl.h>
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070012#include <sys/mount.h>
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -040013#include <sys/stat.h>
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070014#include <sys/types.h>
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -040015#include <sys/wait.h>
Dylan Reid0412dcc2017-08-24 11:33:15 -070016#include <unistd.h>
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -040017
18#include <gtest/gtest.h>
19
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070020#include <functional>
21#include <map>
22#include <set>
23#include <string>
24
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -040025#include "libminijail-private.h"
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070026#include "libminijail.h"
27#include "scoped_minijail.h"
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -040028#include "util.h"
29
Mike Frysingerb2c12d12017-09-29 21:18:59 -040030namespace {
31
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -040032#if defined(__ANDROID__)
Mike Frysingerb2c12d12017-09-29 21:18:59 -040033# define ROOT_PREFIX "/system"
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -040034#else
Mike Frysingerb2c12d12017-09-29 21:18:59 -040035# define ROOT_PREFIX ""
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -040036#endif
37
Luis Hector Chavez9acba452018-10-11 10:13:25 -070038constexpr char kShellPath[] = ROOT_PREFIX "/bin/sh";
39constexpr char kCatPath[] = ROOT_PREFIX "/bin/cat";
40constexpr char kPreloadPath[] = "./libminijailpreload.so";
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070041constexpr size_t kBufferSize = 128;
42
43std::set<pid_t> GetProcessSubtreePids(pid_t root_pid) {
44 std::set<pid_t> pids{root_pid};
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070045 bool progress = false;
46
47 do {
48 progress = false;
49 DIR* d = opendir("/proc");
50 if (!d)
51 pdie("opendir(\"/proc\")");
52
53 struct dirent* dir_entry;
54 while ((dir_entry = readdir(d)) != nullptr) {
55 if (dir_entry->d_type != DT_DIR)
56 continue;
57 char* end;
58 const int pid = strtol(dir_entry->d_name, &end, 10);
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070059 if (*end != '\0')
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070060 continue;
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070061 std::string path = "/proc/" + std::to_string(pid) + "/stat";
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070062
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070063 FILE* f = fopen(path.c_str(), "re");
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070064 if (!f)
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070065 pdie("fopen(%s)", path.c_str());
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070066 pid_t ppid;
67 int ret = fscanf(f, "%*d (%*[^)]) %*c %d", &ppid);
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070068 fclose(f);
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070069 if (ret != 1) {
70 continue;
71 }
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070072 if (pids.find(ppid) == pids.end())
73 continue;
74 progress |= pids.insert(pid).second;
75 }
76 closedir(d);
77 } while (progress);
78 return pids;
79}
80
81std::map<std::string, std::string> GetNamespaces(
82 pid_t pid,
83 const std::vector<std::string>& namespace_names) {
84 std::map<std::string, std::string> namespaces;
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070085 char buf[kBufferSize];
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070086 for (const auto& namespace_name : namespace_names) {
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070087 std::string path = "/proc/" + std::to_string(pid) + "/ns/" + namespace_name;
88 ssize_t len = readlink(path.c_str(), buf, sizeof(buf));
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070089 if (len == -1)
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070090 pdie("readlink(\"%s\")", path.c_str());
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070091 namespaces.emplace(namespace_name, std::string(buf, len));
92 }
93 return namespaces;
94}
Mike Frysingerb2c12d12017-09-29 21:18:59 -040095
96} // namespace
97
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -040098/* Prototypes needed only by test. */
Martin Pelikánab9eb442017-01-25 11:53:58 +110099size_t minijail_get_tmpfs_size(const struct minijail *);
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400100
101/* Silence unused variable warnings. */
102TEST(silence, silence_unused) {
103 EXPECT_STREQ(kLdPreloadEnvVar, kLdPreloadEnvVar);
104 EXPECT_STREQ(kFdEnvVar, kFdEnvVar);
105 EXPECT_STRNE(kFdEnvVar, kLdPreloadEnvVar);
106}
107
108TEST(consumebytes, zero) {
109 char buf[1024];
110 size_t len = sizeof(buf);
111 char *pos = &buf[0];
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400112 EXPECT_NE(nullptr, consumebytes(0, &pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400113 EXPECT_EQ(&buf[0], pos);
114 EXPECT_EQ(sizeof(buf), len);
115}
116
117TEST(consumebytes, exact) {
118 char buf[1024];
119 size_t len = sizeof(buf);
120 char *pos = &buf[0];
121 /* One past the end since it consumes the whole buffer. */
122 char *end = &buf[sizeof(buf)];
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400123 EXPECT_NE(nullptr, consumebytes(len, &pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400124 EXPECT_EQ((size_t)0, len);
125 EXPECT_EQ(end, pos);
126}
127
128TEST(consumebytes, half) {
129 char buf[1024];
130 size_t len = sizeof(buf);
131 char *pos = &buf[0];
132 /* One past the end since it consumes the whole buffer. */
133 char *end = &buf[sizeof(buf) / 2];
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400134 EXPECT_NE(nullptr, consumebytes(len / 2, &pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400135 EXPECT_EQ(sizeof(buf) / 2, len);
136 EXPECT_EQ(end, pos);
137}
138
139TEST(consumebytes, toolong) {
140 char buf[1024];
141 size_t len = sizeof(buf);
142 char *pos = &buf[0];
143 /* One past the end since it consumes the whole buffer. */
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400144 EXPECT_EQ(nullptr, consumebytes(len + 1, &pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400145 EXPECT_EQ(sizeof(buf), len);
146 EXPECT_EQ(&buf[0], pos);
147}
148
149TEST(consumestr, zero) {
150 char buf[1024];
151 size_t len = 0;
152 char *pos = &buf[0];
153 memset(buf, 0xff, sizeof(buf));
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400154 EXPECT_EQ(nullptr, consumestr(&pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400155 EXPECT_EQ((size_t)0, len);
156 EXPECT_EQ(&buf[0], pos);
157}
158
159TEST(consumestr, nonul) {
160 char buf[1024];
161 size_t len = sizeof(buf);
162 char *pos = &buf[0];
163 memset(buf, 0xff, sizeof(buf));
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400164 EXPECT_EQ(nullptr, consumestr(&pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400165 EXPECT_EQ(sizeof(buf), len);
166 EXPECT_EQ(&buf[0], pos);
167}
168
169TEST(consumestr, full) {
170 char buf[1024];
171 size_t len = sizeof(buf);
172 char *pos = &buf[0];
173 memset(buf, 0xff, sizeof(buf));
174 buf[sizeof(buf)-1] = '\0';
175 EXPECT_EQ((void *)buf, consumestr(&pos, &len));
176 EXPECT_EQ((size_t)0, len);
177 EXPECT_EQ(&buf[sizeof(buf)], pos);
178}
179
180TEST(consumestr, trailing_nul) {
181 char buf[1024];
182 size_t len = sizeof(buf) - 1;
183 char *pos = &buf[0];
184 memset(buf, 0xff, sizeof(buf));
185 buf[sizeof(buf)-1] = '\0';
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400186 EXPECT_EQ(nullptr, consumestr(&pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400187 EXPECT_EQ(sizeof(buf) - 1, len);
188 EXPECT_EQ(&buf[0], pos);
189}
190
191class MarshalTest : public ::testing::Test {
192 protected:
193 virtual void SetUp() {
194 m_ = minijail_new();
195 j_ = minijail_new();
196 size_ = minijail_size(m_);
197 }
198 virtual void TearDown() {
199 minijail_destroy(m_);
200 minijail_destroy(j_);
201 }
202
203 char buf_[4096];
204 struct minijail *m_;
205 struct minijail *j_;
206 size_t size_;
207};
208
209TEST_F(MarshalTest, empty) {
210 ASSERT_EQ(0, minijail_marshal(m_, buf_, sizeof(buf_)));
211 EXPECT_EQ(0, minijail_unmarshal(j_, buf_, size_));
212}
213
214TEST_F(MarshalTest, 0xff) {
215 memset(buf_, 0xff, sizeof(buf_));
216 /* Should fail on the first consumestr since a NUL will never be found. */
217 EXPECT_EQ(-EINVAL, minijail_unmarshal(j_, buf_, sizeof(buf_)));
218}
219
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700220TEST(Test, minijail_run_pid_pipes) {
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700221 // TODO(crbug.com/895875): The preload library interferes with ASan since they
222 // both need to use LD_PRELOAD.
223 if (running_with_asan()) {
224 SUCCEED();
225 return;
226 }
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700227 constexpr char teststr[] = "test\n";
228
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700229 ScopedMinijail j(minijail_new());
230 minijail_set_preload_path(j.get(), kPreloadPath);
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700231
232 char* argv[4];
233 argv[0] = const_cast<char*>(kCatPath);
234 argv[1] = nullptr;
235 pid_t pid;
236 int child_stdin, child_stdout;
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700237 int mj_run_ret = minijail_run_pid_pipes(j.get(), argv[0], argv, &pid,
238 &child_stdin, &child_stdout, nullptr);
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700239 EXPECT_EQ(mj_run_ret, 0);
240
241 const size_t teststr_len = strlen(teststr);
242 ssize_t write_ret = write(child_stdin, teststr, teststr_len);
243 EXPECT_EQ(write_ret, static_cast<ssize_t>(teststr_len));
244
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700245 char buf[kBufferSize];
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700246 ssize_t read_ret = read(child_stdout, buf, 8);
247 EXPECT_EQ(read_ret, static_cast<ssize_t>(teststr_len));
248 buf[teststr_len] = 0;
249 EXPECT_EQ(strcmp(buf, teststr), 0);
250
251 int status;
252 EXPECT_EQ(kill(pid, SIGTERM), 0);
253 waitpid(pid, &status, 0);
254 ASSERT_TRUE(WIFSIGNALED(status));
255 EXPECT_EQ(WTERMSIG(status), SIGTERM);
256
257 argv[0] = const_cast<char*>(kShellPath);
258 argv[1] = "-c";
259 argv[2] = "echo test >&2";
260 argv[3] = nullptr;
261 int child_stderr;
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700262 mj_run_ret = minijail_run_pid_pipes(j.get(), argv[0], argv, &pid,
263 &child_stdin, &child_stdout,
264 &child_stderr);
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700265 EXPECT_EQ(mj_run_ret, 0);
266
267 read_ret = read(child_stderr, buf, sizeof(buf));
268 EXPECT_GE(read_ret, static_cast<ssize_t>(teststr_len));
269
270 waitpid(pid, &status, 0);
271 ASSERT_TRUE(WIFEXITED(status));
272 EXPECT_EQ(WEXITSTATUS(status), 0);
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700273}
274
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400275TEST(Test, minijail_run_pid_pipes_no_preload) {
276 pid_t pid;
277 int child_stdin, child_stdout, child_stderr;
278 int mj_run_ret;
279 ssize_t write_ret, read_ret;
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700280 char buf[kBufferSize];
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400281 int status;
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400282 char teststr[] = "test\n";
283 size_t teststr_len = strlen(teststr);
284 char *argv[4];
285
286 struct minijail *j = minijail_new();
287
Mike Frysingerb2c12d12017-09-29 21:18:59 -0400288 argv[0] = (char*)kCatPath;
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400289 argv[1] = NULL;
290 mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv,
291 &pid,
292 &child_stdin, &child_stdout,
293 NULL);
294 EXPECT_EQ(mj_run_ret, 0);
295
296 write_ret = write(child_stdin, teststr, teststr_len);
297 EXPECT_EQ(write_ret, (int)teststr_len);
298
299 read_ret = read(child_stdout, buf, 8);
300 EXPECT_EQ(read_ret, (int)teststr_len);
301 buf[teststr_len] = 0;
302 EXPECT_EQ(strcmp(buf, teststr), 0);
303
304 EXPECT_EQ(kill(pid, SIGTERM), 0);
305 waitpid(pid, &status, 0);
306 ASSERT_TRUE(WIFSIGNALED(status));
307 EXPECT_EQ(WTERMSIG(status), SIGTERM);
308
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400309 argv[0] = (char*)kShellPath;
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400310 argv[1] = "-c";
311 argv[2] = "echo test >&2";
312 argv[3] = NULL;
313 mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid,
314 &child_stdin, &child_stdout,
315 &child_stderr);
316 EXPECT_EQ(mj_run_ret, 0);
317
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700318 read_ret = read(child_stderr, buf, sizeof(buf));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400319 EXPECT_GE(read_ret, (int)teststr_len);
320
321 waitpid(pid, &status, 0);
322 ASSERT_TRUE(WIFEXITED(status));
323 EXPECT_EQ(WEXITSTATUS(status), 0);
324
325 minijail_destroy(j);
326}
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400327
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500328TEST(Test, minijail_run_env_pid_pipes_no_preload) {
329 pid_t pid;
330 int child_stdin, child_stdout, child_stderr;
331 int mj_run_ret;
332 ssize_t read_ret;
333 char buf[kBufferSize];
334 int status;
335 char test_envvar[] = "TEST_VAR=test";
336 size_t testvar_len = strlen("test");
337 char *argv[4];
338 char *envp[2];
339
340 struct minijail *j = minijail_new();
341
342 argv[0] = (char*)kShellPath;
343 argv[1] = "-c";
344 argv[2] = "echo \"${TEST_PARENT+set}|${TEST_VAR}\"";
345 argv[3] = NULL;
346
347 envp[0] = test_envvar;
348 envp[1] = NULL;
349
350 // Set a canary env var in the parent that should not be present in the child.
351 ASSERT_EQ(setenv("TEST_PARENT", "test", 1 /*overwrite*/), 0);
352
353 mj_run_ret = minijail_run_env_pid_pipes_no_preload(
354 j, argv[0], argv, envp, &pid, &child_stdin, &child_stdout, &child_stderr);
355 EXPECT_EQ(mj_run_ret, 0);
356
357 read_ret = read(child_stdout, buf, sizeof(buf));
Jorge Lucangeli Obes8b285412019-06-03 16:28:16 -0400358 EXPECT_EQ(read_ret, (int)testvar_len + 2);
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500359
Jorge Lucangeli Obes8b285412019-06-03 16:28:16 -0400360 EXPECT_EQ("|test\n", std::string(buf, 0, testvar_len + 2));
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500361
362 EXPECT_EQ(waitpid(pid, &status, 0), pid);
363 ASSERT_TRUE(WIFEXITED(status));
364 EXPECT_EQ(WEXITSTATUS(status), 0);
365
366 minijail_destroy(j);
367}
368
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400369TEST(Test, test_minijail_no_fd_leaks) {
370 pid_t pid;
371 int child_stdout;
372 int mj_run_ret;
373 ssize_t read_ret;
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700374 char buf[kBufferSize];
375 char script[kBufferSize];
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400376 int status;
377 char *argv[4];
378
379 int dev_null = open("/dev/null", O_RDONLY);
380 ASSERT_NE(dev_null, -1);
381 snprintf(script,
382 sizeof(script),
383 "[ -e /proc/self/fd/%d ] && echo yes || echo no",
384 dev_null);
385
386 struct minijail *j = minijail_new();
387
388 argv[0] = (char*)kShellPath;
389 argv[1] = "-c";
390 argv[2] = script;
391 argv[3] = NULL;
392 mj_run_ret = minijail_run_pid_pipes_no_preload(
393 j, argv[0], argv, &pid, NULL, &child_stdout, NULL);
394 EXPECT_EQ(mj_run_ret, 0);
395
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700396 read_ret = read(child_stdout, buf, sizeof(buf));
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400397 EXPECT_GE(read_ret, 0);
398 buf[read_ret] = '\0';
399 EXPECT_STREQ(buf, "yes\n");
400
401 waitpid(pid, &status, 0);
402 ASSERT_TRUE(WIFEXITED(status));
403 EXPECT_EQ(WEXITSTATUS(status), 0);
404
405 minijail_close_open_fds(j);
406 mj_run_ret = minijail_run_pid_pipes_no_preload(
407 j, argv[0], argv, &pid, NULL, &child_stdout, NULL);
408 EXPECT_EQ(mj_run_ret, 0);
409
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700410 read_ret = read(child_stdout, buf, sizeof(buf));
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400411 EXPECT_GE(read_ret, 0);
412 buf[read_ret] = '\0';
413 EXPECT_STREQ(buf, "no\n");
414
415 waitpid(pid, &status, 0);
416 ASSERT_TRUE(WIFEXITED(status));
417 EXPECT_EQ(WEXITSTATUS(status), 0);
418
419 minijail_destroy(j);
420
421 close(dev_null);
422}
Martin Pelikánab9eb442017-01-25 11:53:58 +1100423
Dylan Reid0412dcc2017-08-24 11:33:15 -0700424TEST(Test, test_minijail_fork) {
425 pid_t mj_fork_ret;
426 int status;
427 int pipe_fds[2];
428 ssize_t pid_size = sizeof(mj_fork_ret);
429
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700430 ScopedMinijail j(minijail_new());
Dylan Reid0412dcc2017-08-24 11:33:15 -0700431
432 ASSERT_EQ(pipe(pipe_fds), 0);
433
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700434 mj_fork_ret = minijail_fork(j.get());
Dylan Reid0412dcc2017-08-24 11:33:15 -0700435 ASSERT_GE(mj_fork_ret, 0);
436 if (mj_fork_ret == 0) {
437 pid_t pid_in_parent;
438 // Wait for the parent to tell us the pid in the parent namespace.
Luis Hector Chavez7e333012018-04-05 12:15:27 -0700439 ASSERT_EQ(read(pipe_fds[0], &pid_in_parent, pid_size), pid_size);
440 ASSERT_EQ(pid_in_parent, getpid());
Dylan Reid0412dcc2017-08-24 11:33:15 -0700441 exit(0);
442 }
443
444 EXPECT_EQ(write(pipe_fds[1], &mj_fork_ret, pid_size), pid_size);
445 waitpid(mj_fork_ret, &status, 0);
446 ASSERT_TRUE(WIFEXITED(status));
447 EXPECT_EQ(WEXITSTATUS(status), 0);
Dylan Reid0412dcc2017-08-24 11:33:15 -0700448}
449
Luis Hector Chaveze0ba4ce2017-07-20 15:12:22 -0700450static int early_exit(void* payload) {
451 exit(static_cast<int>(reinterpret_cast<intptr_t>(payload)));
452}
453
454TEST(Test, test_minijail_callback) {
455 pid_t pid;
456 int mj_run_ret;
457 int status;
Luis Hector Chaveze0ba4ce2017-07-20 15:12:22 -0700458 char *argv[2];
459 int exit_code = 42;
460
461 struct minijail *j = minijail_new();
462
463 status =
464 minijail_add_hook(j, &early_exit, reinterpret_cast<void *>(exit_code),
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500465 MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS);
Luis Hector Chaveze0ba4ce2017-07-20 15:12:22 -0700466 EXPECT_EQ(status, 0);
467
Mike Frysingerb2c12d12017-09-29 21:18:59 -0400468 argv[0] = (char*)kCatPath;
Luis Hector Chaveze0ba4ce2017-07-20 15:12:22 -0700469 argv[1] = NULL;
470 mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, NULL,
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500471 NULL, NULL);
Luis Hector Chaveze0ba4ce2017-07-20 15:12:22 -0700472 EXPECT_EQ(mj_run_ret, 0);
473
474 status = minijail_wait(j);
475 EXPECT_EQ(status, exit_code);
476
477 minijail_destroy(j);
478}
479
Luis Hector Chavez1617f632017-08-01 18:32:30 -0700480TEST(Test, test_minijail_preserve_fd) {
481 int mj_run_ret;
482 int status;
Luis Hector Chavez1617f632017-08-01 18:32:30 -0700483 char *argv[2];
484 char teststr[] = "test\n";
485 size_t teststr_len = strlen(teststr);
486 int read_pipe[2];
487 int write_pipe[2];
488 char buf[1024];
489
490 struct minijail *j = minijail_new();
491
492 status = pipe(read_pipe);
493 ASSERT_EQ(status, 0);
494 status = pipe(write_pipe);
495 ASSERT_EQ(status, 0);
496
497 status = minijail_preserve_fd(j, write_pipe[0], STDIN_FILENO);
498 ASSERT_EQ(status, 0);
499 status = minijail_preserve_fd(j, read_pipe[1], STDOUT_FILENO);
500 ASSERT_EQ(status, 0);
501 minijail_close_open_fds(j);
502
Mike Frysingerb2c12d12017-09-29 21:18:59 -0400503 argv[0] = (char*)kCatPath;
Luis Hector Chavez1617f632017-08-01 18:32:30 -0700504 argv[1] = NULL;
505 mj_run_ret = minijail_run_no_preload(j, argv[0], argv);
506 EXPECT_EQ(mj_run_ret, 0);
507
508 close(write_pipe[0]);
509 status = write(write_pipe[1], teststr, teststr_len);
510 EXPECT_EQ(status, (int)teststr_len);
511 close(write_pipe[1]);
512
513 close(read_pipe[1]);
514 status = read(read_pipe[0], buf, 8);
515 EXPECT_EQ(status, (int)teststr_len);
516 buf[teststr_len] = 0;
517 EXPECT_EQ(strcmp(buf, teststr), 0);
518
519 status = minijail_wait(j);
520 EXPECT_EQ(status, 0);
521
522 minijail_destroy(j);
523}
524
Luis Hector Chavez7e333012018-04-05 12:15:27 -0700525TEST(Test, test_minijail_reset_signal_mask) {
526 struct minijail *j = minijail_new();
527
528 sigset_t original_signal_mask;
529 {
530 sigset_t signal_mask;
531 ASSERT_EQ(0, sigemptyset(&signal_mask));
532 ASSERT_EQ(0, sigaddset(&signal_mask, SIGUSR1));
533 ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &signal_mask, &original_signal_mask));
534 }
535
536 minijail_reset_signal_mask(j);
537
538 pid_t mj_fork_ret = minijail_fork(j);
539 ASSERT_GE(mj_fork_ret, 0);
540 if (mj_fork_ret == 0) {
541 sigset_t signal_mask;
542 ASSERT_EQ(0, sigprocmask(SIG_SETMASK, NULL, &signal_mask));
543 ASSERT_EQ(0, sigismember(&signal_mask, SIGUSR1));
Jorge Lucangeli Obes16532802018-05-24 13:03:34 -0400544 minijail_destroy(j);
Luis Hector Chavez7e333012018-04-05 12:15:27 -0700545 exit(0);
546 }
547
548 ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &original_signal_mask, NULL));
549
550 int status;
551 waitpid(mj_fork_ret, &status, 0);
552 ASSERT_TRUE(WIFEXITED(status));
553 EXPECT_EQ(WEXITSTATUS(status), 0);
554
555 minijail_destroy(j);
556}
557
Luis Hector Chaveza27118a2018-04-04 08:18:01 -0700558TEST(Test, test_minijail_reset_signal_handlers) {
559 struct minijail *j = minijail_new();
560
561 ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_DFL));
562 ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_IGN));
563 ASSERT_EQ(SIG_IGN, signal(SIGUSR1, SIG_IGN));
564
565 minijail_reset_signal_handlers(j);
566
567 pid_t mj_fork_ret = minijail_fork(j);
568 ASSERT_GE(mj_fork_ret, 0);
569 if (mj_fork_ret == 0) {
570 ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_DFL));
Jorge Lucangeli Obes16532802018-05-24 13:03:34 -0400571 minijail_destroy(j);
Luis Hector Chaveza27118a2018-04-04 08:18:01 -0700572 exit(0);
573 }
574
575 ASSERT_NE(SIG_ERR, signal(SIGUSR1, SIG_DFL));
576
577 int status;
578 waitpid(mj_fork_ret, &status, 0);
579 ASSERT_TRUE(WIFEXITED(status));
580 EXPECT_EQ(WEXITSTATUS(status), 0);
581
582 minijail_destroy(j);
583}
584
Mike Frysinger780aef72017-10-03 01:39:52 -0400585namespace {
586
587// Tests that require userns access.
588// Android unit tests don't currently support entering user namespaces as
589// unprivileged users due to having an older kernel. Chrome OS unit tests
590// don't support it either due to being in a chroot environment (see man 2
591// clone for more information about failure modes with the CLONE_NEWUSER flag).
592class NamespaceTest : public ::testing::Test {
593 protected:
594 static void SetUpTestCase() {
595 userns_supported_ = UsernsSupported();
596 }
597
598 // Whether userns is supported.
599 static bool userns_supported_;
600
601 static bool UsernsSupported() {
602 pid_t pid = fork();
603 if (pid == -1)
604 pdie("could not fork");
605
606 if (pid == 0)
607 _exit(unshare(CLONE_NEWUSER) == 0 ? 0 : 1);
608
609 int status;
610 if (waitpid(pid, &status, 0) < 0)
611 pdie("could not wait");
612
613 if (!WIFEXITED(status))
614 die("child did not exit properly: %#x", status);
615
616 bool ret = WEXITSTATUS(status) == 0;
617 if (!ret)
618 warn("Skipping userns related tests");
619 return ret;
620 }
621};
622
623bool NamespaceTest::userns_supported_;
624
625} // namespace
626
627TEST_F(NamespaceTest, test_tmpfs_userns) {
Luis Hector Chavez71323552017-09-05 09:17:22 -0700628 int mj_run_ret;
629 int status;
630 char *argv[4];
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700631 char uidmap[kBufferSize], gidmap[kBufferSize];
Luis Hector Chavez71323552017-09-05 09:17:22 -0700632 constexpr uid_t kTargetUid = 1000; // Any non-zero value will do.
633 constexpr gid_t kTargetGid = 1000;
634
Mike Frysinger780aef72017-10-03 01:39:52 -0400635 if (!userns_supported_) {
636 SUCCEED();
637 return;
638 }
639
Luis Hector Chavez71323552017-09-05 09:17:22 -0700640 struct minijail *j = minijail_new();
641
642 minijail_namespace_pids(j);
643 minijail_namespace_vfs(j);
644 minijail_mount_tmp(j);
645 minijail_run_as_init(j);
646
647 // Perform userns mapping.
648 minijail_namespace_user(j);
649 snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid());
650 snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid());
651 minijail_change_uid(j, kTargetUid);
652 minijail_change_gid(j, kTargetGid);
653 minijail_uidmap(j, uidmap);
654 minijail_gidmap(j, gidmap);
655 minijail_namespace_user_disable_setgroups(j);
656
657 argv[0] = (char*)kShellPath;
658 argv[1] = "-c";
659 argv[2] = "exec touch /tmp/foo";
660 argv[3] = NULL;
661 mj_run_ret = minijail_run_no_preload(j, argv[0], argv);
662 EXPECT_EQ(mj_run_ret, 0);
663
664 status = minijail_wait(j);
665 EXPECT_EQ(status, 0);
666
667 minijail_destroy(j);
668}
669
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700670TEST_F(NamespaceTest, test_namespaces) {
671 constexpr char teststr[] = "test\n";
672
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700673 // TODO(crbug.com/895875): The preload library interferes with ASan since they
674 // both need to use LD_PRELOAD.
675 if (!userns_supported_ || running_with_asan()) {
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700676 SUCCEED();
677 return;
678 }
679
Luis Hector Chavezac0acf32018-10-15 09:45:01 -0700680 std::string uidmap = "0 " + std::to_string(getuid()) + " 1";
681 std::string gidmap = "0 " + std::to_string(getgid()) + " 1";
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700682
Luis Hector Chavez83a44892018-10-12 08:56:20 -0700683 const std::vector<std::string> namespace_names = {"pid", "mnt", "user",
684 "net", "cgroup", "uts"};
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700685 // Grab the set of namespaces outside the container.
686 std::map<std::string, std::string> init_namespaces =
687 GetNamespaces(getpid(), namespace_names);
688 std::function<void(struct minijail*)> test_functions[] = {
689 [](struct minijail* j attribute_unused) {},
690 [](struct minijail* j) {
691 minijail_mount(j, "/", "/", "", MS_BIND | MS_REC | MS_RDONLY);
692 minijail_enter_pivot_root(j, "/tmp");
693 },
694 [](struct minijail* j) { minijail_enter_chroot(j, "/"); },
695 };
696
697 // This test is run with and without the preload library.
698 for (const auto& run_function :
699 {minijail_run_pid_pipes, minijail_run_pid_pipes_no_preload}) {
700 for (const auto& test_function : test_functions) {
701 ScopedMinijail j(minijail_new());
702 minijail_set_preload_path(j.get(), kPreloadPath);
703
704 // Enter all the namespaces we can.
705 minijail_namespace_cgroups(j.get());
706 minijail_namespace_net(j.get());
707 minijail_namespace_pids(j.get());
708 minijail_namespace_user(j.get());
709 minijail_namespace_vfs(j.get());
710 minijail_namespace_uts(j.get());
711
Luis Hector Chavezac0acf32018-10-15 09:45:01 -0700712 // Set up the user namespace.
713 minijail_uidmap(j.get(), uidmap.c_str());
714 minijail_gidmap(j.get(), gidmap.c_str());
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700715 minijail_namespace_user_disable_setgroups(j.get());
716
717 minijail_close_open_fds(j.get());
718 test_function(j.get());
719
720 const char* argv[] = {kCatPath, nullptr};
721 pid_t container_pid;
722 int child_stdin, child_stdout;
723 int mj_run_ret =
724 run_function(j.get(), argv[0], const_cast<char* const*>(argv),
725 &container_pid, &child_stdin, &child_stdout, nullptr);
726 EXPECT_EQ(mj_run_ret, 0);
727
728 // Send some data to stdin and read it back to ensure that the child
729 // process is running.
730 const size_t teststr_len = strlen(teststr);
731 ssize_t write_ret = write(child_stdin, teststr, teststr_len);
732 EXPECT_EQ(write_ret, static_cast<ssize_t>(teststr_len));
733
734 char buf[kBufferSize];
735 ssize_t read_ret = read(child_stdout, buf, 8);
736 EXPECT_EQ(read_ret, static_cast<ssize_t>(teststr_len));
737 buf[teststr_len] = 0;
738 EXPECT_EQ(strcmp(buf, teststr), 0);
739
740 // Grab the set of namespaces in every container process. They must not
741 // match the ones in the init namespace, and they must all match each
742 // other.
743 std::map<std::string, std::string> container_namespaces =
744 GetNamespaces(container_pid, namespace_names);
745 EXPECT_NE(container_namespaces, init_namespaces);
746 for (pid_t pid : GetProcessSubtreePids(container_pid))
747 EXPECT_EQ(container_namespaces, GetNamespaces(pid, namespace_names));
748
749 EXPECT_EQ(0, close(child_stdin));
750
751 int status = minijail_wait(j.get());
752 EXPECT_EQ(status, 0);
753 }
754 }
755}
756
Mike Frysinger902a4492018-12-27 05:22:56 -0500757TEST_F(NamespaceTest, test_enter_ns) {
758 char uidmap[kBufferSize], gidmap[kBufferSize];
759
760 if (!userns_supported_) {
761 SUCCEED();
762 return;
763 }
764
765 // We first create a child in a new userns so we have privs to run more tests.
766 // We can't combine the steps as the kernel disallows many resource sharing
767 // from outside the userns.
768 struct minijail *j = minijail_new();
769
770 minijail_namespace_vfs(j);
771 minijail_namespace_pids(j);
772 minijail_run_as_init(j);
773
774 // Perform userns mapping.
775 minijail_namespace_user(j);
776 snprintf(uidmap, sizeof(uidmap), "0 %d 1", getuid());
777 snprintf(gidmap, sizeof(gidmap), "0 %d 1", getgid());
778 minijail_uidmap(j, uidmap);
779 minijail_gidmap(j, gidmap);
780 minijail_namespace_user_disable_setgroups(j);
781
782 pid_t pid = minijail_fork(j);
783 if (pid == 0) {
784 // Child.
785 minijail_destroy(j);
786
787 // Create new namespaces inside this userns which we may enter.
788 j = minijail_new();
789 minijail_namespace_net(j);
790 minijail_namespace_vfs(j);
791 pid = minijail_fork(j);
792 if (pid == 0) {
793 // Child.
794 minijail_destroy(j);
795
796 // Finally enter those namespaces.
797 j = minijail_new();
798
799 // We need to get the absolute path because entering a new mntns will
800 // implicitly chdir(/) for us.
801 char *path = realpath(kPreloadPath, nullptr);
802 ASSERT_NE(nullptr, path);
803 minijail_set_preload_path(j, path);
804
805 minijail_namespace_net(j);
806 minijail_namespace_vfs(j);
807
808 minijail_namespace_enter_net(j, "/proc/self/ns/net");
809 minijail_namespace_enter_vfs(j, "/proc/self/ns/mnt");
810
811 char *argv[] = {"/bin/true", nullptr};
812 EXPECT_EQ(0, minijail_run(j, argv[0], argv));
813 EXPECT_EQ(0, minijail_wait(j));
814 minijail_destroy(j);
815 exit(0);
816 } else {
817 ASSERT_GT(pid, 0);
818 EXPECT_EQ(0, minijail_wait(j));
819 minijail_destroy(j);
820 exit(0);
821 }
822 } else {
823 ASSERT_GT(pid, 0);
824 EXPECT_EQ(0, minijail_wait(j));
825 minijail_destroy(j);
826 }
827}
828
Martin Pelikánab9eb442017-01-25 11:53:58 +1100829TEST(Test, parse_size) {
830 size_t size;
831
832 ASSERT_EQ(0, parse_size(&size, "42"));
833 ASSERT_EQ(42U, size);
834
835 ASSERT_EQ(0, parse_size(&size, "16K"));
836 ASSERT_EQ(16384U, size);
837
838 ASSERT_EQ(0, parse_size(&size, "1M"));
839 ASSERT_EQ(1024U * 1024, size);
840
841 uint64_t gigabyte = 1024ULL * 1024 * 1024;
842 ASSERT_EQ(0, parse_size(&size, "3G"));
843 ASSERT_EQ(3U, size / gigabyte);
844 ASSERT_EQ(0U, size % gigabyte);
845
846 ASSERT_EQ(0, parse_size(&size, "4294967294"));
847 ASSERT_EQ(3U, size / gigabyte);
848 ASSERT_EQ(gigabyte - 2, size % gigabyte);
849
850#if __WORDSIZE == 64
851 uint64_t exabyte = gigabyte * 1024 * 1024 * 1024;
852 ASSERT_EQ(0, parse_size(&size, "9E"));
853 ASSERT_EQ(9U, size / exabyte);
854 ASSERT_EQ(0U, size % exabyte);
855
856 ASSERT_EQ(0, parse_size(&size, "15E"));
857 ASSERT_EQ(15U, size / exabyte);
858 ASSERT_EQ(0U, size % exabyte);
859
860 ASSERT_EQ(0, parse_size(&size, "18446744073709551614"));
861 ASSERT_EQ(15U, size / exabyte);
862 ASSERT_EQ(exabyte - 2, size % exabyte);
863
864 ASSERT_EQ(-ERANGE, parse_size(&size, "16E"));
865 ASSERT_EQ(-ERANGE, parse_size(&size, "19E"));
Chirantan Ekbote8c13d102017-02-09 12:33:19 -0800866 ASSERT_EQ(-EINVAL, parse_size(&size, "7GTPE"));
Martin Pelikánab9eb442017-01-25 11:53:58 +1100867#elif __WORDSIZE == 32
868 ASSERT_EQ(-ERANGE, parse_size(&size, "5G"));
869 ASSERT_EQ(-ERANGE, parse_size(&size, "9G"));
870 ASSERT_EQ(-ERANGE, parse_size(&size, "9E"));
Chirantan Ekbote8c13d102017-02-09 12:33:19 -0800871 ASSERT_EQ(-ERANGE, parse_size(&size, "7GTPE"));
Martin Pelikánab9eb442017-01-25 11:53:58 +1100872#endif
873
874 ASSERT_EQ(-EINVAL, parse_size(&size, ""));
875 ASSERT_EQ(-EINVAL, parse_size(&size, "14u"));
876 ASSERT_EQ(-EINVAL, parse_size(&size, "14.2G"));
Martin Pelikánab9eb442017-01-25 11:53:58 +1100877 ASSERT_EQ(-EINVAL, parse_size(&size, "-1G"));
878 ASSERT_EQ(-EINVAL, parse_size(&size, "; /bin/rm -- "));
879}
Xiyuan Xia9b41e652019-05-23 11:03:04 -0700880
881void TestCreateSession(bool create_session) {
882 int status;
883 int pipe_fds[2];
884 pid_t child_pid;
885 pid_t parent_sid = getsid(0);
886 ssize_t pid_size = sizeof(pid_t);
887
888 ScopedMinijail j(minijail_new());
889 // stdin/stdout/stderr might be attached to TTYs. Close them to avoid creating
890 // a new session because of that.
891 minijail_close_open_fds(j.get());
892
893 if (create_session)
894 minijail_create_session(j.get());
895
896 ASSERT_EQ(pipe(pipe_fds), 0);
897 minijail_preserve_fd(j.get(), pipe_fds[0], pipe_fds[0]);
898
899 child_pid = minijail_fork(j.get());
900 ASSERT_GE(child_pid, 0);
901 if (child_pid == 0) {
902 pid_t sid_in_parent;
903 ASSERT_EQ(read(pipe_fds[0], &sid_in_parent, pid_size), pid_size);
904 if (create_session)
905 ASSERT_NE(sid_in_parent, getsid(0));
906 else
907 ASSERT_EQ(sid_in_parent, getsid(0));
908 exit(0);
909 }
910
911 EXPECT_EQ(write(pipe_fds[1], &parent_sid, pid_size), pid_size);
912 waitpid(child_pid, &status, 0);
913 ASSERT_TRUE(WIFEXITED(status));
914 EXPECT_EQ(WEXITSTATUS(status), 0);
915}
916
917TEST(Test, default_no_new_session) {
918 TestCreateSession(/*create_session=*/false);
919}
920
921TEST(Test, create_new_session) {
922 TestCreateSession(/*create_session=*/true);
923}