blob: fda4dd43a72d576535c53ca60ce5d2ecbffcb98e [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>
Allen Webb05af7762021-07-16 12:56:44 -050012#include <sys/mman.h>
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070013#include <sys/mount.h>
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -040014#include <sys/stat.h>
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070015#include <sys/types.h>
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -040016#include <sys/wait.h>
Dylan Reid0412dcc2017-08-24 11:33:15 -070017#include <unistd.h>
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -040018
19#include <gtest/gtest.h>
20
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070021#include <functional>
22#include <map>
23#include <set>
24#include <string>
25
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -040026#include "libminijail-private.h"
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070027#include "libminijail.h"
28#include "scoped_minijail.h"
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -040029#include "util.h"
30
Mike Frysingerb2c12d12017-09-29 21:18:59 -040031namespace {
32
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -040033#if defined(__ANDROID__)
Mike Frysingerb2c12d12017-09-29 21:18:59 -040034# define ROOT_PREFIX "/system"
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -040035#else
Mike Frysingerb2c12d12017-09-29 21:18:59 -040036# define ROOT_PREFIX ""
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -040037#endif
38
Luis Hector Chavez9acba452018-10-11 10:13:25 -070039constexpr char kShellPath[] = ROOT_PREFIX "/bin/sh";
40constexpr char kCatPath[] = ROOT_PREFIX "/bin/cat";
41constexpr char kPreloadPath[] = "./libminijailpreload.so";
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070042constexpr size_t kBufferSize = 128;
43
44std::set<pid_t> GetProcessSubtreePids(pid_t root_pid) {
45 std::set<pid_t> pids{root_pid};
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070046 bool progress = false;
47
48 do {
49 progress = false;
50 DIR* d = opendir("/proc");
51 if (!d)
52 pdie("opendir(\"/proc\")");
53
54 struct dirent* dir_entry;
55 while ((dir_entry = readdir(d)) != nullptr) {
56 if (dir_entry->d_type != DT_DIR)
57 continue;
58 char* end;
59 const int pid = strtol(dir_entry->d_name, &end, 10);
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070060 if (*end != '\0')
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070061 continue;
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070062 std::string path = "/proc/" + std::to_string(pid) + "/stat";
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070063
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070064 FILE* f = fopen(path.c_str(), "re");
Luis Héctor Chávez25214912021-01-03 05:47:09 -080065 if (!f) {
66 if (errno == ENOENT) {
67 // This loop is inherently racy, since PIDs can be reaped in the
68 // middle of this. Not being able to find one /proc/PID/stat file is
69 // completely normal.
70 continue;
71 }
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070072 pdie("fopen(%s)", path.c_str());
Luis Héctor Chávez25214912021-01-03 05:47:09 -080073 }
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070074 pid_t ppid;
75 int ret = fscanf(f, "%*d (%*[^)]) %*c %d", &ppid);
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070076 fclose(f);
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070077 if (ret != 1) {
78 continue;
79 }
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070080 if (pids.find(ppid) == pids.end())
81 continue;
82 progress |= pids.insert(pid).second;
83 }
84 closedir(d);
85 } while (progress);
86 return pids;
87}
88
89std::map<std::string, std::string> GetNamespaces(
90 pid_t pid,
91 const std::vector<std::string>& namespace_names) {
92 std::map<std::string, std::string> namespaces;
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070093 char buf[kBufferSize];
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070094 for (const auto& namespace_name : namespace_names) {
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070095 std::string path = "/proc/" + std::to_string(pid) + "/ns/" + namespace_name;
96 ssize_t len = readlink(path.c_str(), buf, sizeof(buf));
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070097 if (len == -1)
Luis Hector Chavezac0acf32018-10-15 09:45:01 -070098 pdie("readlink(\"%s\")", path.c_str());
Luis Hector Chavez86e10d32018-10-11 20:36:48 -070099 namespaces.emplace(namespace_name, std::string(buf, len));
100 }
101 return namespaces;
102}
Mike Frysingerb2c12d12017-09-29 21:18:59 -0400103
104} // namespace
105
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400106/* Silence unused variable warnings. */
107TEST(silence, silence_unused) {
108 EXPECT_STREQ(kLdPreloadEnvVar, kLdPreloadEnvVar);
109 EXPECT_STREQ(kFdEnvVar, kFdEnvVar);
110 EXPECT_STRNE(kFdEnvVar, kLdPreloadEnvVar);
111}
112
113TEST(consumebytes, zero) {
114 char buf[1024];
115 size_t len = sizeof(buf);
116 char *pos = &buf[0];
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400117 EXPECT_NE(nullptr, consumebytes(0, &pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400118 EXPECT_EQ(&buf[0], pos);
119 EXPECT_EQ(sizeof(buf), len);
120}
121
122TEST(consumebytes, exact) {
123 char buf[1024];
124 size_t len = sizeof(buf);
125 char *pos = &buf[0];
126 /* One past the end since it consumes the whole buffer. */
127 char *end = &buf[sizeof(buf)];
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400128 EXPECT_NE(nullptr, consumebytes(len, &pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400129 EXPECT_EQ((size_t)0, len);
130 EXPECT_EQ(end, pos);
131}
132
133TEST(consumebytes, half) {
134 char buf[1024];
135 size_t len = sizeof(buf);
136 char *pos = &buf[0];
137 /* One past the end since it consumes the whole buffer. */
138 char *end = &buf[sizeof(buf) / 2];
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400139 EXPECT_NE(nullptr, consumebytes(len / 2, &pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400140 EXPECT_EQ(sizeof(buf) / 2, len);
141 EXPECT_EQ(end, pos);
142}
143
144TEST(consumebytes, toolong) {
145 char buf[1024];
146 size_t len = sizeof(buf);
147 char *pos = &buf[0];
148 /* One past the end since it consumes the whole buffer. */
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400149 EXPECT_EQ(nullptr, consumebytes(len + 1, &pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400150 EXPECT_EQ(sizeof(buf), len);
151 EXPECT_EQ(&buf[0], pos);
152}
153
154TEST(consumestr, zero) {
155 char buf[1024];
156 size_t len = 0;
157 char *pos = &buf[0];
158 memset(buf, 0xff, sizeof(buf));
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400159 EXPECT_EQ(nullptr, consumestr(&pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400160 EXPECT_EQ((size_t)0, len);
161 EXPECT_EQ(&buf[0], pos);
162}
163
164TEST(consumestr, nonul) {
165 char buf[1024];
166 size_t len = sizeof(buf);
167 char *pos = &buf[0];
168 memset(buf, 0xff, sizeof(buf));
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400169 EXPECT_EQ(nullptr, consumestr(&pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400170 EXPECT_EQ(sizeof(buf), len);
171 EXPECT_EQ(&buf[0], pos);
172}
173
174TEST(consumestr, full) {
175 char buf[1024];
176 size_t len = sizeof(buf);
177 char *pos = &buf[0];
178 memset(buf, 0xff, sizeof(buf));
179 buf[sizeof(buf)-1] = '\0';
180 EXPECT_EQ((void *)buf, consumestr(&pos, &len));
181 EXPECT_EQ((size_t)0, len);
182 EXPECT_EQ(&buf[sizeof(buf)], pos);
183}
184
185TEST(consumestr, trailing_nul) {
186 char buf[1024];
187 size_t len = sizeof(buf) - 1;
188 char *pos = &buf[0];
189 memset(buf, 0xff, sizeof(buf));
190 buf[sizeof(buf)-1] = '\0';
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400191 EXPECT_EQ(nullptr, consumestr(&pos, &len));
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400192 EXPECT_EQ(sizeof(buf) - 1, len);
193 EXPECT_EQ(&buf[0], pos);
194}
195
196class MarshalTest : public ::testing::Test {
197 protected:
198 virtual void SetUp() {
199 m_ = minijail_new();
200 j_ = minijail_new();
201 size_ = minijail_size(m_);
202 }
203 virtual void TearDown() {
204 minijail_destroy(m_);
205 minijail_destroy(j_);
206 }
207
208 char buf_[4096];
209 struct minijail *m_;
210 struct minijail *j_;
211 size_t size_;
212};
213
214TEST_F(MarshalTest, empty) {
215 ASSERT_EQ(0, minijail_marshal(m_, buf_, sizeof(buf_)));
216 EXPECT_EQ(0, minijail_unmarshal(j_, buf_, size_));
217}
218
219TEST_F(MarshalTest, 0xff) {
220 memset(buf_, 0xff, sizeof(buf_));
221 /* Should fail on the first consumestr since a NUL will never be found. */
222 EXPECT_EQ(-EINVAL, minijail_unmarshal(j_, buf_, sizeof(buf_)));
223}
224
Dylan Reid6dc224f2021-05-12 17:06:25 -0700225TEST_F(MarshalTest, copy_empty) {
226 ASSERT_EQ(0, minijail_copy_jail(m_, j_));
227}
228
François Degros47e63352019-10-01 13:01:53 +1000229TEST(KillTest, running_process) {
230 const ScopedMinijail j(minijail_new());
231 char* const argv[] = {"sh", "-c", "sleep 1000", nullptr};
232 EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0);
233 EXPECT_EQ(minijail_kill(j.get()), 128 + SIGTERM);
234 EXPECT_EQ(minijail_kill(j.get()), -ESRCH);
235}
236
237TEST(KillTest, process_already_awaited) {
238 const ScopedMinijail j(minijail_new());
239 char* const argv[] = {"sh", "-c", "sleep 1; exit 42", nullptr};
240 EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0);
241 EXPECT_EQ(minijail_wait(j.get()), 42);
242 EXPECT_EQ(minijail_kill(j.get()), -ESRCH);
243}
244
245TEST(KillTest, process_already_finished_but_not_awaited) {
246 int fds[2];
247 const ScopedMinijail j(minijail_new());
248 char* const argv[] = {"sh", "-c", "exit 42", nullptr};
249 ASSERT_EQ(pipe(fds), 0);
250 EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0);
251 ASSERT_EQ(close(fds[1]), 0);
252 // Wait for process to finish.
253 char buf[PIPE_BUF];
254 EXPECT_EQ(read(fds[0], buf, PIPE_BUF), 0);
255 EXPECT_EQ(minijail_kill(j.get()), 42);
256 EXPECT_EQ(minijail_wait(j.get()), -ECHILD);
257}
258
259TEST(KillTest, process_not_started) {
260 const ScopedMinijail j(minijail_new());
261 EXPECT_EQ(minijail_kill(j.get()), -ECHILD);
262}
263
François Degros08b10f72019-10-09 12:44:05 +1100264TEST(WaitTest, return_zero) {
265 const ScopedMinijail j(minijail_new());
266 char* const argv[] = {"sh", "-c", "exit 0", nullptr};
267 EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0);
268 EXPECT_EQ(minijail_wait(j.get()), 0);
269}
270
271TEST(WaitTest, return_max) {
272 const ScopedMinijail j(minijail_new());
273 char* const argv[] = {"sh", "-c", "exit 255", nullptr};
274 EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0);
275 EXPECT_EQ(minijail_wait(j.get()), 255);
276}
277
278TEST(WaitTest, return_modulo) {
279 const ScopedMinijail j(minijail_new());
280 char* const argv[] = {"sh", "-c", "exit 256", nullptr};
281 EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0);
282 EXPECT_EQ(minijail_wait(j.get()), 0);
283}
284
285TEST(WaitTest, killed_by_sigkill) {
286 const ScopedMinijail j(minijail_new());
287 char* const argv[] = {"sh", "-c", "kill -KILL $$; sleep 1000", nullptr};
288 EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0);
289 EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_SIG_BASE + SIGKILL);
290}
291
292TEST(WaitTest, killed_by_sigsys) {
293 const ScopedMinijail j(minijail_new());
294 char* const argv[] = {"sh", "-c", "kill -SYS $$; sleep 1000", nullptr};
295 EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0);
296 EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_JAIL);
297}
298
299TEST(WaitTest, command_not_found) {
300 const ScopedMinijail j(minijail_new());
301 char* const argv[] = {"whatever", nullptr};
302 EXPECT_EQ(minijail_run(j.get(), "command that cannot be found", argv), 0);
303 EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_NO_COMMAND);
304}
305
306TEST(WaitTest, command_not_run) {
307 const ScopedMinijail j(minijail_new());
308 char* const argv[] = {"whatever", nullptr};
309 EXPECT_EQ(minijail_run(j.get(), "/dev/null", argv), 0);
310 EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_NO_ACCESS);
311}
312
313TEST(WaitTest, no_process) {
314 const ScopedMinijail j(minijail_new());
315 EXPECT_EQ(minijail_wait(j.get()), -ECHILD);
316}
317
318TEST(WaitTest, can_wait_only_once) {
319 const ScopedMinijail j(minijail_new());
320 char* const argv[] = {"sh", "-c", "exit 0", nullptr};
321 EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0);
322 EXPECT_EQ(minijail_wait(j.get()), 0);
323 EXPECT_EQ(minijail_wait(j.get()), -ECHILD);
324}
325
François Degros26f8e4e2020-04-17 09:31:02 +1000326TEST(Test, minijail_preserve_fd_no_leak) {
327 const ScopedMinijail j(minijail_new());
328 char* const script = R"(
329 echo Hi >&1;
330 exec 1>&-;
331 read line1;
332 read line2;
333 echo "$line1$line2 and Goodbye" >&2;
334 exit 42;
335 )";
336 char* const argv[] = {"sh", "-c", script, nullptr};
337
338 const int npipes = 3;
339 int fds[npipes][2];
340
341 // Create pipes.
342 for (int i = 0; i < npipes; ++i) {
343 ASSERT_EQ(pipe(fds[i]), 0);
344 }
345
346 // All pipes are output pipes except for the first one which is used as
347 // input pipe.
348 std::swap(fds[0][0], fds[0][1]);
349
350 for (int i = 0; i < npipes; ++i) {
351 const int fd = fds[i][1];
352 minijail_preserve_fd(j.get(), fd, i);
353 }
354
355 minijail_close_open_fds(j.get());
356
357 EXPECT_EQ(minijail_run_no_preload(j.get(), kShellPath, argv), 0);
358
359 // Close unused end of pipes.
360 for (int i = 0; i < npipes; ++i) {
361 const int fd = fds[i][1];
362 ASSERT_EQ(close(fd), 0);
363 }
364
365 const int in = fds[0][0];
366 const int out = fds[1][0];
367 const int err = fds[2][0];
368
369 char buf[PIPE_BUF];
370 ssize_t nbytes;
371
372 // Check that stdout pipe works.
373 nbytes = read(out, buf, PIPE_BUF);
374 ASSERT_GT(nbytes, 0);
375 EXPECT_EQ(std::string(buf, nbytes), "Hi\n");
376
377 // Check that the write end of stdout pipe got closed by the child process. If
378 // the child process kept other file descriptors connected to stdout, then the
379 // parent process wouldn't be able to detect that all write ends of this pipe
380 // are closed and it would block here.
381 EXPECT_EQ(read(out, buf, PIPE_BUF), 0);
382 ASSERT_EQ(close(out), 0);
383
384 // Check that stdin pipe works.
385 const std::string s = "Greetings\n";
386 EXPECT_EQ(write(in, s.data(), s.size()), s.size());
387
388 // Close write end of pipe connected to child's stdin. If there was another
389 // file descriptor connected to this write end, then the child process
390 // wouldn't be able to detect that this write end is closed and it would
391 // block.
392 ASSERT_EQ(close(in), 0);
393
394 // Check that child process continued and ended.
395 nbytes = read(err, buf, PIPE_BUF);
396 ASSERT_GT(nbytes, 0);
397 EXPECT_EQ(std::string(buf, nbytes), "Greetings and Goodbye\n");
398
399 // Check that the write end of the stderr pipe is closed when the child
400 // process finishes.
401 EXPECT_EQ(read(err, buf, PIPE_BUF), 0);
402 ASSERT_EQ(close(err), 0);
403
404 // Check the child process termination status.
405 EXPECT_EQ(minijail_wait(j.get()), 42);
406}
407
François Degrosa8be2c42019-10-01 12:06:42 +1000408TEST(Test, close_original_pipes_after_dup2) {
409 // Pipe used by child process to signal that it continued after reading from
410 // stdin.
411 int to_wait[2];
412 ASSERT_EQ(pipe(to_wait), 0);
413
414 const ScopedMinijail j(minijail_new());
415 char* program;
416 ASSERT_GT(asprintf(&program, R"(
417 echo Hi >&1;
418 echo There >&2;
419 exec 1>&-;
420 exec 2>&-;
421 read line1;
422 read line2;
423 echo "$line1$line2 and Goodbye" >&%d;
424 exit 42;
425 )", to_wait[1]), 0);
François Degros87f0e832019-10-10 10:22:39 +1100426 char* const argv[] = {"sh", "-c", program, nullptr};
François Degrosa8be2c42019-10-01 12:06:42 +1000427
428 int in = -1;
429 int out = -1;
430 int err = -1;
Jorge Lucangeli Obes7c696e42019-10-24 11:19:51 -0400431 EXPECT_EQ(minijail_run_pid_pipes_no_preload(j.get(), kShellPath, argv,
432 nullptr, &in, &out, &err),
433 0);
François Degrosa8be2c42019-10-01 12:06:42 +1000434 free(program);
435
436 EXPECT_GT(in, 0);
437 EXPECT_GT(out, 0);
438 EXPECT_GT(err, 0);
439
440 char buf[PIPE_BUF];
441 ssize_t n;
442
443 // Check that stdout and stderr pipes work.
444 n = read(out, buf, PIPE_BUF);
445 ASSERT_GT(n, 0);
446 EXPECT_EQ(std::string(buf, n), "Hi\n");
447
448 n = read(err, buf, PIPE_BUF);
449 ASSERT_GT(n, 0);
450 EXPECT_EQ(std::string(buf, n), "There\n");
451
452 // Check that the write ends of stdout and stderr pipes got closed by the
453 // child process. If the child process kept other file descriptors connected
454 // to stdout and stderr, then the parent process wouldn't be able to detect
455 // that all write ends of these pipes are closed and it would block here.
456 EXPECT_EQ(read(out, buf, PIPE_BUF), 0);
François Degros26f8e4e2020-04-17 09:31:02 +1000457 ASSERT_EQ(close(out), 0);
François Degrosa8be2c42019-10-01 12:06:42 +1000458 EXPECT_EQ(read(err, buf, PIPE_BUF), 0);
François Degros26f8e4e2020-04-17 09:31:02 +1000459 ASSERT_EQ(close(err), 0);
François Degrosa8be2c42019-10-01 12:06:42 +1000460
461 // Check that stdin pipe works.
462 const std::string s = "Greetings\n";
463 EXPECT_EQ(write(in, s.data(), s.size()), s.size());
464
465 // Close write end of pipe connected to child's stdin. If there was another
466 // file descriptor connected to this write end, then the child wouldn't be
467 // able to detect that this write end is closed and it would block.
468 ASSERT_EQ(close(in), 0);
469
470 // Check that child process continued and ended.
471 n = read(to_wait[0], buf, PIPE_BUF);
472 ASSERT_GT(n, 0);
473 EXPECT_EQ(std::string(buf, n), "Greetings and Goodbye\n");
474 EXPECT_EQ(minijail_wait(j.get()), 42);
475}
476
Allen Webb66417bd2021-07-16 15:07:24 -0700477TEST(Test, minijail_no_clobber_pipe_fd) {
478 const ScopedMinijail j(minijail_new());
479 char* const script = R"(
480 echo Hi >&1;
481 exec 1>&-;
482 exec 4>&-;
483 exec 7>&-;
484 read line1;
485 read line2;
486 echo "$line1$line2 and Goodbye" >&2;
487 exit 42;
488 )";
489 char* const argv[] = {"sh", "-c", script, nullptr};
490
491 const int npipes = 3;
492 int fds[npipes][2];
493
494 // Create pipes.
495 for (int i = 0; i < npipes; ++i) {
496 ASSERT_EQ(pipe(fds[i]), 0);
497 }
498
499 // All pipes are output pipes except for the first one which is used as
500 // input pipe.
501 std::swap(fds[0][0], fds[0][1]);
502
503 // Generate a lot of mappings to try to clobber any file descriptors generated
504 // by libminijail.
505 for (int offset = 0; offset < npipes * 3; offset += npipes) {
506 for (int i = 0 ; i < npipes; ++i) {
507 const int fd = fds[i][1];
508 minijail_preserve_fd(j.get(), fd, i + offset);
509 }
510 }
511
512 minijail_close_open_fds(j.get());
513
514 EXPECT_EQ(minijail_run_no_preload(j.get(), kShellPath, argv), 0);
515
516 // Close unused end of pipes.
517 for (int i = 0; i < npipes; ++i) {
518 const int fd = fds[i][1];
519 ASSERT_EQ(close(fd), 0);
520 }
521
522 const int in = fds[0][0];
523 const int out = fds[1][0];
524 const int err = fds[2][0];
525
526 char buf[PIPE_BUF];
527 ssize_t nbytes;
528
529 // Check that stdout pipe works.
530 nbytes = read(out, buf, PIPE_BUF);
531 ASSERT_GT(nbytes, 0);
532 EXPECT_EQ(std::string(buf, nbytes), "Hi\n");
533
534 // Check that the write end of stdout pipe got closed by the child process. If
535 // the child process kept other file descriptors connected to stdout, then the
536 // parent process wouldn't be able to detect that all write ends of this pipe
537 // are closed and it would block here.
538 EXPECT_EQ(read(out, buf, PIPE_BUF), 0);
539 ASSERT_EQ(close(out), 0);
540
541 // Check that stdin pipe works.
542 const std::string s = "Greetings\n";
543 EXPECT_EQ(write(in, s.data(), s.size()), s.size());
544
545 // Close write end of pipe connected to child's stdin. If there was another
546 // file descriptor connected to this write end, then the child process
547 // wouldn't be able to detect that this write end is closed and it would
548 // block.
549 ASSERT_EQ(close(in), 0);
550
551 // Check that child process continued and ended.
552 nbytes = read(err, buf, PIPE_BUF);
553 ASSERT_GT(nbytes, 0);
554 EXPECT_EQ(std::string(buf, nbytes), "Greetings and Goodbye\n");
555
556 // Check that the write end of the stderr pipe is closed when the child
557 // process finishes.
558 EXPECT_EQ(read(err, buf, PIPE_BUF), 0);
559 ASSERT_EQ(close(err), 0);
560
561 // Check the child process termination status.
562 EXPECT_EQ(minijail_wait(j.get()), 42);
563}
564
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100565TEST(Test, minijail_run_env_pid_pipes) {
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700566 // TODO(crbug.com/895875): The preload library interferes with ASan since they
567 // both need to use LD_PRELOAD.
Luis Héctor Chávez4f830bd2021-01-03 05:47:11 -0800568 if (running_with_asan())
569 GTEST_SKIP();
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700570
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700571 ScopedMinijail j(minijail_new());
572 minijail_set_preload_path(j.get(), kPreloadPath);
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700573
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100574 char *argv[4];
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700575 argv[0] = const_cast<char*>(kCatPath);
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100576 argv[1] = NULL;
577
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700578 pid_t pid;
579 int child_stdin, child_stdout;
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100580 int mj_run_ret = minijail_run_pid_pipes(
581 j.get(), argv[0], argv, &pid, &child_stdin, &child_stdout, NULL);
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700582 EXPECT_EQ(mj_run_ret, 0);
583
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100584 char teststr[] = "test\n";
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700585 const size_t teststr_len = strlen(teststr);
586 ssize_t write_ret = write(child_stdin, teststr, teststr_len);
587 EXPECT_EQ(write_ret, static_cast<ssize_t>(teststr_len));
588
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100589 char buf[kBufferSize] = {};
590 ssize_t read_ret = read(child_stdout, buf, sizeof(buf) - 1);
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700591 EXPECT_EQ(read_ret, static_cast<ssize_t>(teststr_len));
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100592 EXPECT_STREQ(buf, teststr);
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700593
594 int status;
595 EXPECT_EQ(kill(pid, SIGTERM), 0);
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100596 EXPECT_EQ(waitpid(pid, &status, 0), pid);
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700597 ASSERT_TRUE(WIFSIGNALED(status));
598 EXPECT_EQ(WTERMSIG(status), SIGTERM);
599
600 argv[0] = const_cast<char*>(kShellPath);
601 argv[1] = "-c";
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100602 argv[2] = "echo \"${TEST_PARENT+set}|${TEST_VAR}\" >&2";
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700603 argv[3] = nullptr;
Luis Hector Chavez9acba452018-10-11 10:13:25 -0700604
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500605 char *envp[2];
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100606 envp[0] = "TEST_VAR=test";
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500607 envp[1] = NULL;
608
609 // Set a canary env var in the parent that should not be present in the child.
610 ASSERT_EQ(setenv("TEST_PARENT", "test", 1 /*overwrite*/), 0);
611
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100612 int child_stderr;
613 mj_run_ret =
614 minijail_run_env_pid_pipes(j.get(), argv[0], argv, envp, &pid,
615 &child_stdin, &child_stdout, &child_stderr);
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500616 EXPECT_EQ(mj_run_ret, 0);
617
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100618 memset(buf, 0, sizeof(buf));
619 read_ret = read(child_stderr, buf, sizeof(buf) - 1);
620 EXPECT_GE(read_ret, 0);
621 EXPECT_STREQ(buf, "|test\n");
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500622
623 EXPECT_EQ(waitpid(pid, &status, 0), pid);
624 ASSERT_TRUE(WIFEXITED(status));
625 EXPECT_EQ(WEXITSTATUS(status), 0);
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100626}
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500627
Allen Webb05af7762021-07-16 12:56:44 -0500628TEST(Test, minijail_run_fd_env_pid_pipes) {
629 // TODO(crbug.com/895875): The preload library interferes with ASan since they
630 // both need to use LD_PRELOAD.
631 if (running_with_asan())
632 GTEST_SKIP();
633
634 ScopedMinijail j(minijail_new());
635 minijail_set_preload_path(j.get(), kPreloadPath);
636
637 char *argv[4];
638 argv[0] = const_cast<char*>(kCatPath);
639 argv[1] = nullptr;
640
641 pid_t pid;
642 int child_stdin, child_stdout;
643 int mj_run_ret = minijail_run_pid_pipes(
644 j.get(), argv[0], argv, &pid, &child_stdin, &child_stdout, NULL);
645 EXPECT_EQ(mj_run_ret, 0);
646
647 char teststr[] = "test\n";
648 const size_t teststr_len = strlen(teststr);
649 ssize_t write_ret = write(child_stdin, teststr, teststr_len);
650 EXPECT_EQ(write_ret, static_cast<ssize_t>(teststr_len));
651
652 char buf[kBufferSize] = {};
653 ssize_t read_ret = read(child_stdout, buf, sizeof(buf) - 1);
654 EXPECT_EQ(read_ret, static_cast<ssize_t>(teststr_len));
655 EXPECT_STREQ(buf, teststr);
656
657 int status;
658 EXPECT_EQ(kill(pid, SIGTERM), 0);
659 EXPECT_EQ(waitpid(pid, &status, 0), pid);
660 ASSERT_TRUE(WIFSIGNALED(status));
661 EXPECT_EQ(WTERMSIG(status), SIGTERM);
662
663 argv[0] = const_cast<char*>(kShellPath);
664 argv[1] = "-c";
665 argv[2] = "#!/bin/sh\necho \"${TEST_PARENT+set}|${TEST_VAR}\" >&2\n";
666 argv[3] = nullptr;
667
668 char *envp[2];
669 envp[0] = "TEST_VAR=test";
670 envp[1] = nullptr;
671
672 // Set a canary env var in the parent that should not be present in the child.
673 ASSERT_EQ(setenv("TEST_PARENT", "test", 1 /*overwrite*/), 0);
674
675 // MFD_CLOEXEC cannot be used because the test fd begins with #!.
676 int elf_fd = memfd_create("test", MFD_ALLOW_SEALING);
677 if (elf_fd < 0) {
678 pdie("memfd_create(elf_fd):");
679 }
680
681 // This is technically not an ELF, but it works for the purposes of the test.
682 ssize_t script_len = strlen(argv[2]);
683 if (write(elf_fd, argv[2], script_len) != script_len) {
684 pdie("write(elf_fd):");
685 }
686 if (ftruncate(elf_fd, script_len) == -1) {
687 pdie("ftruncate(elf_fd):");
688 }
689 if (lseek(elf_fd, 0, SEEK_SET) == -1) {
690 pdie("lseek(elf_fd):");
691 }
692 if (fcntl(elf_fd, F_ADD_SEALS,
693 F_SEAL_WRITE | F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL) == -1) {
694 pdie("fcntl(elf_fd):");
695 }
696
697 int dev_null = open("/dev/null", O_RDONLY);
698 ASSERT_NE(dev_null, -1);
699 // Create a mapping to dev_null that would clobber elf_fd if it is not
700 // relocated.
701 minijail_preserve_fd(j.get(), dev_null, elf_fd);
702
703 int child_stderr;
704 mj_run_ret =
705 minijail_run_fd_env_pid_pipes(j.get(), elf_fd, argv, envp, &pid,
706 &child_stdin, &child_stdout, &child_stderr);
707 EXPECT_EQ(mj_run_ret, 0);
708 close(dev_null);
709
710 memset(buf, 0, sizeof(buf));
711 read_ret = read(child_stderr, buf, sizeof(buf) - 1);
712 EXPECT_GE(read_ret, 0);
713 EXPECT_STREQ(buf, "|test\n");
714
715 EXPECT_EQ(waitpid(pid, &status, 0), pid);
716 ASSERT_TRUE(WIFEXITED(status));
717 EXPECT_EQ(WEXITSTATUS(status), 0);
718}
719
Luis Héctor Chávez4baeafa2021-01-03 05:47:13 -0800720TEST(Test, minijail_run_env_pid_pipes_with_local_preload) {
721 // TODO(crbug.com/895875): The preload library interferes with ASan since they
722 // both need to use LD_PRELOAD.
Luis Héctor Chávez4f830bd2021-01-03 05:47:11 -0800723 if (running_with_asan())
724 GTEST_SKIP();
Luis Héctor Chávez4baeafa2021-01-03 05:47:13 -0800725
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100726 ScopedMinijail j(minijail_new());
727
728 char *argv[4];
729 argv[0] = const_cast<char*>(kCatPath);
730 argv[1] = NULL;
731
732 pid_t pid;
733 int child_stdin, child_stdout;
734 int mj_run_ret = minijail_run_pid_pipes(
735 j.get(), argv[0], argv, &pid, &child_stdin, &child_stdout, NULL);
736 EXPECT_EQ(mj_run_ret, 0);
737
738 char teststr[] = "test\n";
739 const size_t teststr_len = strlen(teststr);
740 ssize_t write_ret = write(child_stdin, teststr, teststr_len);
741 EXPECT_EQ(write_ret, static_cast<ssize_t>(teststr_len));
742
743 char buf[kBufferSize] = {};
Luis Héctor Chávez4baeafa2021-01-03 05:47:13 -0800744 ssize_t read_ret = read(child_stdout, buf, sizeof(buf) - 1);
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100745 EXPECT_EQ(read_ret, static_cast<ssize_t>(teststr_len));
746 EXPECT_STREQ(buf, teststr);
747
748 int status;
749 EXPECT_EQ(kill(pid, SIGTERM), 0);
750 EXPECT_EQ(waitpid(pid, &status, 0), pid);
751 ASSERT_TRUE(WIFSIGNALED(status));
752 EXPECT_EQ(WTERMSIG(status), SIGTERM);
753
754 argv[0] = const_cast<char*>(kShellPath);
755 argv[1] = "-c";
756 argv[2] = "echo \"${TEST_PARENT+set}|${TEST_VAR}\" >&2";
757 argv[3] = nullptr;
758
759 char *envp[2];
760 envp[0] = "TEST_VAR=test";
761 envp[1] = NULL;
762
763 // Set a canary env var in the parent that should not be present in the child.
764 ASSERT_EQ(setenv("TEST_PARENT", "test", 1 /*overwrite*/), 0);
765
Dylan Reid83276a02020-06-10 15:17:13 -0700766 // Use the preload library from this test build.
767 ASSERT_EQ(0, minijail_set_preload_path(j.get(), "./libminijailpreload.so"));
768
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100769 int child_stderr;
770 mj_run_ret =
771 minijail_run_env_pid_pipes(j.get(), argv[0], argv, envp, &pid,
772 &child_stdin, &child_stdout, &child_stderr);
773 EXPECT_EQ(mj_run_ret, 0);
774
775 memset(buf, 0, sizeof(buf));
Luis Héctor Chávez4baeafa2021-01-03 05:47:13 -0800776 read_ret = read(child_stderr, buf, sizeof(buf) - 1);
Mattias Nisslerb35f2c12020-02-07 13:37:36 +0100777 EXPECT_GE(read_ret, 0);
778 EXPECT_STREQ(buf, "|test\n");
779
780 EXPECT_EQ(waitpid(pid, &status, 0), pid);
781 ASSERT_TRUE(WIFEXITED(status));
782 EXPECT_EQ(WEXITSTATUS(status), 0);
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500783}
784
Allen Webb66417bd2021-07-16 15:07:24 -0700785TEST(Test, test_minijail_no_clobber_fds) {
786 int dev_null = open("/dev/null", O_RDONLY);
787 ASSERT_NE(dev_null, -1);
788
789 ScopedMinijail j(minijail_new());
790
791 // Keep stderr.
792 minijail_preserve_fd(j.get(), 2, 2);
793 // Create a lot of mappings to dev_null to possibly clobber libminijail.c fds.
794 for (int i = 3; i < 15; ++i) {
795 minijail_preserve_fd(j.get(), dev_null, i);
796 }
797
798 char *argv[4];
799 argv[0] = const_cast<char*>(kShellPath);
800 argv[1] = "-c";
801 argv[2] = "echo Hello; read line1; echo \"${line1}\" >&2";
802 argv[3] = nullptr;
803
804 pid_t pid;
805 int child_stdin;
806 int child_stdout;
807 int child_stderr;
808 int mj_run_ret = minijail_run_pid_pipes_no_preload(
809 j.get(), argv[0], argv, &pid, &child_stdin, &child_stdout, &child_stderr);
810 EXPECT_EQ(mj_run_ret, 0);
811
812 char buf[kBufferSize];
813 ssize_t read_ret = read(child_stdout, buf, sizeof(buf));
814 EXPECT_GE(read_ret, 0);
815 buf[read_ret] = '\0';
816 EXPECT_STREQ(buf, "Hello\n");
817
818 constexpr char to_write[] = "test in and err\n";
819 ssize_t write_ret = write(child_stdin, to_write, sizeof(to_write));
820 EXPECT_EQ(write_ret, sizeof(to_write));
821
822 read_ret = read(child_stderr, buf, sizeof(buf));
823 EXPECT_GE(read_ret, 0);
824 buf[read_ret] = '\0';
825 EXPECT_STREQ(buf, to_write);
826
827 int status;
828 waitpid(pid, &status, 0);
829 ASSERT_TRUE(WIFEXITED(status));
830 EXPECT_EQ(WEXITSTATUS(status), 0);
831
832 close(dev_null);
833}
834
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400835TEST(Test, test_minijail_no_fd_leaks) {
836 pid_t pid;
837 int child_stdout;
838 int mj_run_ret;
839 ssize_t read_ret;
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700840 char buf[kBufferSize];
841 char script[kBufferSize];
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400842 int status;
843 char *argv[4];
844
845 int dev_null = open("/dev/null", O_RDONLY);
846 ASSERT_NE(dev_null, -1);
847 snprintf(script,
848 sizeof(script),
849 "[ -e /proc/self/fd/%d ] && echo yes || echo no",
850 dev_null);
851
852 struct minijail *j = minijail_new();
853
François Degros87f0e832019-10-10 10:22:39 +1100854 argv[0] = const_cast<char*>(kShellPath);
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400855 argv[1] = "-c";
856 argv[2] = script;
857 argv[3] = NULL;
858 mj_run_ret = minijail_run_pid_pipes_no_preload(
859 j, argv[0], argv, &pid, NULL, &child_stdout, NULL);
860 EXPECT_EQ(mj_run_ret, 0);
861
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700862 read_ret = read(child_stdout, buf, sizeof(buf));
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400863 EXPECT_GE(read_ret, 0);
864 buf[read_ret] = '\0';
865 EXPECT_STREQ(buf, "yes\n");
866
867 waitpid(pid, &status, 0);
868 ASSERT_TRUE(WIFEXITED(status));
869 EXPECT_EQ(WEXITSTATUS(status), 0);
870
871 minijail_close_open_fds(j);
872 mj_run_ret = minijail_run_pid_pipes_no_preload(
873 j, argv[0], argv, &pid, NULL, &child_stdout, NULL);
874 EXPECT_EQ(mj_run_ret, 0);
875
Luis Hector Chavez86e10d32018-10-11 20:36:48 -0700876 read_ret = read(child_stdout, buf, sizeof(buf));
Jorge Lucangeli Obese9740af2016-11-02 13:39:24 -0400877 EXPECT_GE(read_ret, 0);
878 buf[read_ret] = '\0';
879 EXPECT_STREQ(buf, "no\n");
880
881 waitpid(pid, &status, 0);
882 ASSERT_TRUE(WIFEXITED(status));
883 EXPECT_EQ(WEXITSTATUS(status), 0);
884
885 minijail_destroy(j);
886
887 close(dev_null);
888}
Martin Pelikánab9eb442017-01-25 11:53:58 +1100889
Dylan Reid0412dcc2017-08-24 11:33:15 -0700890TEST(Test, test_minijail_fork) {
891 pid_t mj_fork_ret;
892 int status;
893 int pipe_fds[2];
894 ssize_t pid_size = sizeof(mj_fork_ret);
895
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700896 ScopedMinijail j(minijail_new());
Dylan Reid0412dcc2017-08-24 11:33:15 -0700897
898 ASSERT_EQ(pipe(pipe_fds), 0);
899
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -0700900 mj_fork_ret = minijail_fork(j.get());
Dylan Reid0412dcc2017-08-24 11:33:15 -0700901 ASSERT_GE(mj_fork_ret, 0);
902 if (mj_fork_ret == 0) {
903 pid_t pid_in_parent;
904 // Wait for the parent to tell us the pid in the parent namespace.
Luis Hector Chavez7e333012018-04-05 12:15:27 -0700905 ASSERT_EQ(read(pipe_fds[0], &pid_in_parent, pid_size), pid_size);
906 ASSERT_EQ(pid_in_parent, getpid());
Dylan Reid0412dcc2017-08-24 11:33:15 -0700907 exit(0);
908 }
909
910 EXPECT_EQ(write(pipe_fds[1], &mj_fork_ret, pid_size), pid_size);
911 waitpid(mj_fork_ret, &status, 0);
912 ASSERT_TRUE(WIFEXITED(status));
913 EXPECT_EQ(WEXITSTATUS(status), 0);
Dylan Reid0412dcc2017-08-24 11:33:15 -0700914}
915
Luis Hector Chaveze0ba4ce2017-07-20 15:12:22 -0700916static int early_exit(void* payload) {
917 exit(static_cast<int>(reinterpret_cast<intptr_t>(payload)));
918}
919
920TEST(Test, test_minijail_callback) {
921 pid_t pid;
922 int mj_run_ret;
923 int status;
Luis Hector Chaveze0ba4ce2017-07-20 15:12:22 -0700924 char *argv[2];
925 int exit_code = 42;
926
927 struct minijail *j = minijail_new();
928
929 status =
930 minijail_add_hook(j, &early_exit, reinterpret_cast<void *>(exit_code),
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500931 MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS);
Luis Hector Chaveze0ba4ce2017-07-20 15:12:22 -0700932 EXPECT_EQ(status, 0);
933
François Degros87f0e832019-10-10 10:22:39 +1100934 argv[0] = const_cast<char*>(kCatPath);
Luis Hector Chaveze0ba4ce2017-07-20 15:12:22 -0700935 argv[1] = NULL;
936 mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, NULL,
Jorge Lucangeli Obesd2c951d2019-02-01 15:43:36 -0500937 NULL, NULL);
Luis Hector Chaveze0ba4ce2017-07-20 15:12:22 -0700938 EXPECT_EQ(mj_run_ret, 0);
939
940 status = minijail_wait(j);
941 EXPECT_EQ(status, exit_code);
942
943 minijail_destroy(j);
944}
945
Luis Hector Chavez1617f632017-08-01 18:32:30 -0700946TEST(Test, test_minijail_preserve_fd) {
947 int mj_run_ret;
948 int status;
Luis Hector Chavez1617f632017-08-01 18:32:30 -0700949 char *argv[2];
950 char teststr[] = "test\n";
951 size_t teststr_len = strlen(teststr);
952 int read_pipe[2];
953 int write_pipe[2];
954 char buf[1024];
955
956 struct minijail *j = minijail_new();
957
958 status = pipe(read_pipe);
959 ASSERT_EQ(status, 0);
960 status = pipe(write_pipe);
961 ASSERT_EQ(status, 0);
962
963 status = minijail_preserve_fd(j, write_pipe[0], STDIN_FILENO);
964 ASSERT_EQ(status, 0);
965 status = minijail_preserve_fd(j, read_pipe[1], STDOUT_FILENO);
966 ASSERT_EQ(status, 0);
967 minijail_close_open_fds(j);
968
François Degros87f0e832019-10-10 10:22:39 +1100969 argv[0] = const_cast<char*>(kCatPath);
Luis Hector Chavez1617f632017-08-01 18:32:30 -0700970 argv[1] = NULL;
971 mj_run_ret = minijail_run_no_preload(j, argv[0], argv);
972 EXPECT_EQ(mj_run_ret, 0);
973
974 close(write_pipe[0]);
975 status = write(write_pipe[1], teststr, teststr_len);
976 EXPECT_EQ(status, (int)teststr_len);
977 close(write_pipe[1]);
978
979 close(read_pipe[1]);
980 status = read(read_pipe[0], buf, 8);
981 EXPECT_EQ(status, (int)teststr_len);
982 buf[teststr_len] = 0;
983 EXPECT_EQ(strcmp(buf, teststr), 0);
984
985 status = minijail_wait(j);
986 EXPECT_EQ(status, 0);
987
988 minijail_destroy(j);
989}
990
Luis Hector Chavez7e333012018-04-05 12:15:27 -0700991TEST(Test, test_minijail_reset_signal_mask) {
992 struct minijail *j = minijail_new();
993
994 sigset_t original_signal_mask;
995 {
996 sigset_t signal_mask;
997 ASSERT_EQ(0, sigemptyset(&signal_mask));
998 ASSERT_EQ(0, sigaddset(&signal_mask, SIGUSR1));
999 ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &signal_mask, &original_signal_mask));
1000 }
1001
1002 minijail_reset_signal_mask(j);
1003
1004 pid_t mj_fork_ret = minijail_fork(j);
1005 ASSERT_GE(mj_fork_ret, 0);
1006 if (mj_fork_ret == 0) {
1007 sigset_t signal_mask;
1008 ASSERT_EQ(0, sigprocmask(SIG_SETMASK, NULL, &signal_mask));
1009 ASSERT_EQ(0, sigismember(&signal_mask, SIGUSR1));
Jorge Lucangeli Obes16532802018-05-24 13:03:34 -04001010 minijail_destroy(j);
Luis Hector Chavez7e333012018-04-05 12:15:27 -07001011 exit(0);
1012 }
1013
1014 ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &original_signal_mask, NULL));
1015
1016 int status;
1017 waitpid(mj_fork_ret, &status, 0);
1018 ASSERT_TRUE(WIFEXITED(status));
1019 EXPECT_EQ(WEXITSTATUS(status), 0);
1020
1021 minijail_destroy(j);
1022}
1023
Luis Hector Chaveza27118a2018-04-04 08:18:01 -07001024TEST(Test, test_minijail_reset_signal_handlers) {
1025 struct minijail *j = minijail_new();
1026
1027 ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_DFL));
1028 ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_IGN));
1029 ASSERT_EQ(SIG_IGN, signal(SIGUSR1, SIG_IGN));
1030
1031 minijail_reset_signal_handlers(j);
1032
1033 pid_t mj_fork_ret = minijail_fork(j);
1034 ASSERT_GE(mj_fork_ret, 0);
1035 if (mj_fork_ret == 0) {
1036 ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_DFL));
Jorge Lucangeli Obes16532802018-05-24 13:03:34 -04001037 minijail_destroy(j);
Luis Hector Chaveza27118a2018-04-04 08:18:01 -07001038 exit(0);
1039 }
1040
1041 ASSERT_NE(SIG_ERR, signal(SIGUSR1, SIG_DFL));
1042
1043 int status;
1044 waitpid(mj_fork_ret, &status, 0);
1045 ASSERT_TRUE(WIFEXITED(status));
1046 EXPECT_EQ(WEXITSTATUS(status), 0);
1047
1048 minijail_destroy(j);
1049}
1050
Mike Frysinger780aef72017-10-03 01:39:52 -04001051namespace {
1052
1053// Tests that require userns access.
1054// Android unit tests don't currently support entering user namespaces as
1055// unprivileged users due to having an older kernel. Chrome OS unit tests
1056// don't support it either due to being in a chroot environment (see man 2
1057// clone for more information about failure modes with the CLONE_NEWUSER flag).
1058class NamespaceTest : public ::testing::Test {
1059 protected:
1060 static void SetUpTestCase() {
1061 userns_supported_ = UsernsSupported();
1062 }
1063
1064 // Whether userns is supported.
1065 static bool userns_supported_;
1066
1067 static bool UsernsSupported() {
1068 pid_t pid = fork();
1069 if (pid == -1)
1070 pdie("could not fork");
1071
1072 if (pid == 0)
1073 _exit(unshare(CLONE_NEWUSER) == 0 ? 0 : 1);
1074
1075 int status;
1076 if (waitpid(pid, &status, 0) < 0)
1077 pdie("could not wait");
1078
1079 if (!WIFEXITED(status))
1080 die("child did not exit properly: %#x", status);
1081
1082 bool ret = WEXITSTATUS(status) == 0;
1083 if (!ret)
1084 warn("Skipping userns related tests");
1085 return ret;
1086 }
1087};
1088
1089bool NamespaceTest::userns_supported_;
1090
1091} // namespace
1092
1093TEST_F(NamespaceTest, test_tmpfs_userns) {
Luis Hector Chavez71323552017-09-05 09:17:22 -07001094 int mj_run_ret;
1095 int status;
1096 char *argv[4];
Luis Hector Chavez86e10d32018-10-11 20:36:48 -07001097 char uidmap[kBufferSize], gidmap[kBufferSize];
Luis Hector Chavez71323552017-09-05 09:17:22 -07001098 constexpr uid_t kTargetUid = 1000; // Any non-zero value will do.
1099 constexpr gid_t kTargetGid = 1000;
1100
Luis Héctor Chávez4f830bd2021-01-03 05:47:11 -08001101 if (!userns_supported_)
1102 GTEST_SKIP();
Mike Frysinger780aef72017-10-03 01:39:52 -04001103
Luis Hector Chavez71323552017-09-05 09:17:22 -07001104 struct minijail *j = minijail_new();
1105
1106 minijail_namespace_pids(j);
1107 minijail_namespace_vfs(j);
1108 minijail_mount_tmp(j);
1109 minijail_run_as_init(j);
1110
1111 // Perform userns mapping.
1112 minijail_namespace_user(j);
1113 snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid());
1114 snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid());
1115 minijail_change_uid(j, kTargetUid);
1116 minijail_change_gid(j, kTargetGid);
1117 minijail_uidmap(j, uidmap);
1118 minijail_gidmap(j, gidmap);
1119 minijail_namespace_user_disable_setgroups(j);
1120
François Degros87f0e832019-10-10 10:22:39 +11001121 argv[0] = const_cast<char*>(kShellPath);
Luis Hector Chavez71323552017-09-05 09:17:22 -07001122 argv[1] = "-c";
1123 argv[2] = "exec touch /tmp/foo";
1124 argv[3] = NULL;
1125 mj_run_ret = minijail_run_no_preload(j, argv[0], argv);
1126 EXPECT_EQ(mj_run_ret, 0);
1127
1128 status = minijail_wait(j);
1129 EXPECT_EQ(status, 0);
1130
1131 minijail_destroy(j);
1132}
1133
Luis Hector Chavez86e10d32018-10-11 20:36:48 -07001134TEST_F(NamespaceTest, test_namespaces) {
1135 constexpr char teststr[] = "test\n";
1136
Luis Hector Chavezeb42bb72018-10-15 09:46:29 -07001137 // TODO(crbug.com/895875): The preload library interferes with ASan since they
1138 // both need to use LD_PRELOAD.
Luis Héctor Chávez4f830bd2021-01-03 05:47:11 -08001139 if (!userns_supported_ || running_with_asan())
1140 GTEST_SKIP();
Luis Hector Chavez86e10d32018-10-11 20:36:48 -07001141
Luis Hector Chavezac0acf32018-10-15 09:45:01 -07001142 std::string uidmap = "0 " + std::to_string(getuid()) + " 1";
1143 std::string gidmap = "0 " + std::to_string(getgid()) + " 1";
Luis Hector Chavez86e10d32018-10-11 20:36:48 -07001144
Luis Hector Chavez83a44892018-10-12 08:56:20 -07001145 const std::vector<std::string> namespace_names = {"pid", "mnt", "user",
1146 "net", "cgroup", "uts"};
Luis Hector Chavez86e10d32018-10-11 20:36:48 -07001147 // Grab the set of namespaces outside the container.
1148 std::map<std::string, std::string> init_namespaces =
1149 GetNamespaces(getpid(), namespace_names);
1150 std::function<void(struct minijail*)> test_functions[] = {
1151 [](struct minijail* j attribute_unused) {},
1152 [](struct minijail* j) {
1153 minijail_mount(j, "/", "/", "", MS_BIND | MS_REC | MS_RDONLY);
1154 minijail_enter_pivot_root(j, "/tmp");
1155 },
1156 [](struct minijail* j) { minijail_enter_chroot(j, "/"); },
1157 };
1158
1159 // This test is run with and without the preload library.
1160 for (const auto& run_function :
1161 {minijail_run_pid_pipes, minijail_run_pid_pipes_no_preload}) {
1162 for (const auto& test_function : test_functions) {
1163 ScopedMinijail j(minijail_new());
1164 minijail_set_preload_path(j.get(), kPreloadPath);
1165
1166 // Enter all the namespaces we can.
1167 minijail_namespace_cgroups(j.get());
1168 minijail_namespace_net(j.get());
1169 minijail_namespace_pids(j.get());
1170 minijail_namespace_user(j.get());
1171 minijail_namespace_vfs(j.get());
1172 minijail_namespace_uts(j.get());
1173
Luis Hector Chavezac0acf32018-10-15 09:45:01 -07001174 // Set up the user namespace.
1175 minijail_uidmap(j.get(), uidmap.c_str());
1176 minijail_gidmap(j.get(), gidmap.c_str());
Luis Hector Chavez86e10d32018-10-11 20:36:48 -07001177 minijail_namespace_user_disable_setgroups(j.get());
1178
1179 minijail_close_open_fds(j.get());
1180 test_function(j.get());
1181
François Degros87f0e832019-10-10 10:22:39 +11001182 char* const argv[] = {const_cast<char*>(kCatPath), nullptr};
Luis Hector Chavez86e10d32018-10-11 20:36:48 -07001183 pid_t container_pid;
1184 int child_stdin, child_stdout;
1185 int mj_run_ret =
François Degros87f0e832019-10-10 10:22:39 +11001186 run_function(j.get(), argv[0], argv,
Luis Hector Chavez86e10d32018-10-11 20:36:48 -07001187 &container_pid, &child_stdin, &child_stdout, nullptr);
1188 EXPECT_EQ(mj_run_ret, 0);
1189
1190 // Send some data to stdin and read it back to ensure that the child
1191 // process is running.
1192 const size_t teststr_len = strlen(teststr);
1193 ssize_t write_ret = write(child_stdin, teststr, teststr_len);
1194 EXPECT_EQ(write_ret, static_cast<ssize_t>(teststr_len));
1195
1196 char buf[kBufferSize];
1197 ssize_t read_ret = read(child_stdout, buf, 8);
1198 EXPECT_EQ(read_ret, static_cast<ssize_t>(teststr_len));
1199 buf[teststr_len] = 0;
1200 EXPECT_EQ(strcmp(buf, teststr), 0);
1201
1202 // Grab the set of namespaces in every container process. They must not
1203 // match the ones in the init namespace, and they must all match each
1204 // other.
1205 std::map<std::string, std::string> container_namespaces =
1206 GetNamespaces(container_pid, namespace_names);
1207 EXPECT_NE(container_namespaces, init_namespaces);
1208 for (pid_t pid : GetProcessSubtreePids(container_pid))
1209 EXPECT_EQ(container_namespaces, GetNamespaces(pid, namespace_names));
1210
1211 EXPECT_EQ(0, close(child_stdin));
1212
1213 int status = minijail_wait(j.get());
1214 EXPECT_EQ(status, 0);
1215 }
1216 }
1217}
1218
Mike Frysinger902a4492018-12-27 05:22:56 -05001219TEST_F(NamespaceTest, test_enter_ns) {
1220 char uidmap[kBufferSize], gidmap[kBufferSize];
1221
Luis Héctor Chávez4f830bd2021-01-03 05:47:11 -08001222 if (!userns_supported_)
1223 GTEST_SKIP();
Mike Frysinger902a4492018-12-27 05:22:56 -05001224
1225 // We first create a child in a new userns so we have privs to run more tests.
1226 // We can't combine the steps as the kernel disallows many resource sharing
1227 // from outside the userns.
1228 struct minijail *j = minijail_new();
1229
1230 minijail_namespace_vfs(j);
1231 minijail_namespace_pids(j);
1232 minijail_run_as_init(j);
1233
1234 // Perform userns mapping.
1235 minijail_namespace_user(j);
1236 snprintf(uidmap, sizeof(uidmap), "0 %d 1", getuid());
1237 snprintf(gidmap, sizeof(gidmap), "0 %d 1", getgid());
1238 minijail_uidmap(j, uidmap);
1239 minijail_gidmap(j, gidmap);
1240 minijail_namespace_user_disable_setgroups(j);
1241
1242 pid_t pid = minijail_fork(j);
1243 if (pid == 0) {
1244 // Child.
1245 minijail_destroy(j);
1246
1247 // Create new namespaces inside this userns which we may enter.
1248 j = minijail_new();
1249 minijail_namespace_net(j);
1250 minijail_namespace_vfs(j);
1251 pid = minijail_fork(j);
1252 if (pid == 0) {
1253 // Child.
1254 minijail_destroy(j);
1255
1256 // Finally enter those namespaces.
1257 j = minijail_new();
1258
1259 // We need to get the absolute path because entering a new mntns will
1260 // implicitly chdir(/) for us.
1261 char *path = realpath(kPreloadPath, nullptr);
1262 ASSERT_NE(nullptr, path);
1263 minijail_set_preload_path(j, path);
1264
1265 minijail_namespace_net(j);
1266 minijail_namespace_vfs(j);
1267
1268 minijail_namespace_enter_net(j, "/proc/self/ns/net");
1269 minijail_namespace_enter_vfs(j, "/proc/self/ns/mnt");
1270
1271 char *argv[] = {"/bin/true", nullptr};
1272 EXPECT_EQ(0, minijail_run(j, argv[0], argv));
1273 EXPECT_EQ(0, minijail_wait(j));
1274 minijail_destroy(j);
1275 exit(0);
1276 } else {
1277 ASSERT_GT(pid, 0);
1278 EXPECT_EQ(0, minijail_wait(j));
1279 minijail_destroy(j);
1280 exit(0);
1281 }
1282 } else {
1283 ASSERT_GT(pid, 0);
1284 EXPECT_EQ(0, minijail_wait(j));
1285 minijail_destroy(j);
1286 }
1287}
1288
Nicole Anderson-Au835f7172021-01-13 21:18:13 +00001289TEST_F(NamespaceTest, test_remount_all_private) {
1290 pid_t pid;
1291 int child_stdout;
1292 int mj_run_ret;
1293 ssize_t read_ret;
1294 char buf[kBufferSize];
1295 int status;
1296 char *argv[4];
1297 char uidmap[kBufferSize], gidmap[kBufferSize];
1298 constexpr uid_t kTargetUid = 1000; // Any non-zero value will do.
1299 constexpr gid_t kTargetGid = 1000;
1300
Luis Héctor Chávez4f830bd2021-01-03 05:47:11 -08001301 if (!userns_supported_)
1302 GTEST_SKIP();
Nicole Anderson-Au835f7172021-01-13 21:18:13 +00001303
1304 struct minijail *j = minijail_new();
1305
1306 minijail_namespace_pids(j);
1307 minijail_namespace_vfs(j);
1308 minijail_run_as_init(j);
1309
1310 // Perform userns mapping.
1311 minijail_namespace_user(j);
1312 snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid());
1313 snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid());
1314 minijail_change_uid(j, kTargetUid);
1315 minijail_change_gid(j, kTargetGid);
1316 minijail_uidmap(j, uidmap);
1317 minijail_gidmap(j, gidmap);
1318 minijail_namespace_user_disable_setgroups(j);
1319
1320 minijail_namespace_vfs(j);
1321 minijail_remount_mode(j, MS_PRIVATE);
1322
1323 argv[0] = const_cast<char*>(kShellPath);
1324 argv[1] = "-c";
1325 argv[2] = "grep -E 'shared:|master:|propagate_from:|unbindable:' "
Allen Webb05af7762021-07-16 12:56:44 -05001326 "/proc/self/mountinfo";
Nicole Anderson-Au835f7172021-01-13 21:18:13 +00001327 argv[3] = NULL;
1328 mj_run_ret = minijail_run_pid_pipes_no_preload(
1329 j, argv[0], argv, &pid, NULL, &child_stdout, NULL);
1330 EXPECT_EQ(mj_run_ret, 0);
1331
1332 // There should be no output because all mounts should be remounted as
1333 // private.
1334 read_ret = read(child_stdout, buf, sizeof(buf));
1335 EXPECT_EQ(read_ret, 0);
1336
1337 // grep will exit with 1 if it does not find anything which is what we
1338 // expect.
1339 status = minijail_wait(j);
1340 EXPECT_EQ(status, 1);
1341
1342 minijail_destroy(j);
1343}
1344
Nicole Anderson-Aue119bbb2021-02-04 23:12:12 +00001345TEST_F(NamespaceTest, test_fail_to_remount_one_private) {
1346 int status;
1347 char uidmap[kBufferSize], gidmap[kBufferSize];
1348 constexpr uid_t kTargetUid = 1000; // Any non-zero value will do.
1349 constexpr gid_t kTargetGid = 1000;
1350
Luis Héctor Chávez4f830bd2021-01-03 05:47:11 -08001351 if (!userns_supported_)
1352 GTEST_SKIP();
Nicole Anderson-Aue119bbb2021-02-04 23:12:12 +00001353
1354 struct minijail *j = minijail_new();
1355
1356 minijail_namespace_pids(j);
1357 minijail_namespace_vfs(j);
1358 minijail_mount_tmp(j);
1359 minijail_run_as_init(j);
1360
1361 // Perform userns mapping.
1362 minijail_namespace_user(j);
1363 snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid());
1364 snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid());
1365 minijail_change_uid(j, kTargetUid);
1366 minijail_change_gid(j, kTargetGid);
1367 minijail_uidmap(j, uidmap);
1368 minijail_gidmap(j, gidmap);
1369 minijail_namespace_user_disable_setgroups(j);
1370
1371 minijail_namespace_vfs(j);
1372 minijail_remount_mode(j, MS_SHARED);
1373 minijail_add_remount(j, "/proc", MS_PRIVATE);
1374
1375 char *argv[] = {"/bin/true", nullptr};
1376 minijail_run(j, argv[0], argv);
1377
1378 status = minijail_wait(j);
1379 EXPECT_GT(status, 0);
1380
1381 minijail_destroy(j);
1382}
1383
Nicole Anderson-Au835f7172021-01-13 21:18:13 +00001384TEST_F(NamespaceTest, test_remount_one_shared) {
1385 pid_t pid;
1386 int child_stdout;
1387 int mj_run_ret;
1388 ssize_t read_ret;
1389 char buf[kBufferSize * 4];
1390 int status;
1391 char *argv[4];
1392 char uidmap[kBufferSize], gidmap[kBufferSize];
1393 constexpr uid_t kTargetUid = 1000; // Any non-zero value will do.
1394 constexpr gid_t kTargetGid = 1000;
1395
Luis Héctor Chávez4f830bd2021-01-03 05:47:11 -08001396 if (!userns_supported_)
1397 GTEST_SKIP();
Nicole Anderson-Au835f7172021-01-13 21:18:13 +00001398
1399 struct minijail *j = minijail_new();
1400
1401 minijail_namespace_pids(j);
1402 minijail_namespace_vfs(j);
1403 minijail_mount_tmp(j);
1404 minijail_run_as_init(j);
1405
1406 // Perform userns mapping.
1407 minijail_namespace_user(j);
1408 snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid());
1409 snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid());
1410 minijail_change_uid(j, kTargetUid);
1411 minijail_change_gid(j, kTargetGid);
1412 minijail_uidmap(j, uidmap);
1413 minijail_gidmap(j, gidmap);
1414 minijail_namespace_user_disable_setgroups(j);
1415
1416 minijail_namespace_vfs(j);
1417 minijail_remount_mode(j, MS_PRIVATE);
1418 minijail_add_remount(j, "/proc", MS_SHARED);
1419
1420 argv[0] = const_cast<char*>(kShellPath);
1421 argv[1] = "-c";
1422 argv[2] = "grep -E 'shared:' /proc/self/mountinfo";
1423 argv[3] = NULL;
1424 mj_run_ret = minijail_run_pid_pipes_no_preload(
1425 j, argv[0], argv, &pid, NULL, &child_stdout, NULL);
1426 EXPECT_EQ(mj_run_ret, 0);
1427
1428 // There should be no output because all mounts should be remounted as
1429 // private.
1430 read_ret = read(child_stdout, buf, sizeof(buf));
1431 EXPECT_GE(read_ret, 0);
1432 buf[read_ret] = '\0';
1433 EXPECT_NE(std::string(buf).find("/proc"), std::string::npos);
1434
1435 status = minijail_wait(j);
1436 EXPECT_EQ(status, 0);
1437
1438 minijail_destroy(j);
1439}
1440
Xiyuan Xia9b41e652019-05-23 11:03:04 -07001441void TestCreateSession(bool create_session) {
1442 int status;
1443 int pipe_fds[2];
1444 pid_t child_pid;
1445 pid_t parent_sid = getsid(0);
1446 ssize_t pid_size = sizeof(pid_t);
1447
1448 ScopedMinijail j(minijail_new());
1449 // stdin/stdout/stderr might be attached to TTYs. Close them to avoid creating
1450 // a new session because of that.
1451 minijail_close_open_fds(j.get());
1452
1453 if (create_session)
1454 minijail_create_session(j.get());
1455
1456 ASSERT_EQ(pipe(pipe_fds), 0);
1457 minijail_preserve_fd(j.get(), pipe_fds[0], pipe_fds[0]);
1458
1459 child_pid = minijail_fork(j.get());
1460 ASSERT_GE(child_pid, 0);
1461 if (child_pid == 0) {
1462 pid_t sid_in_parent;
1463 ASSERT_EQ(read(pipe_fds[0], &sid_in_parent, pid_size), pid_size);
1464 if (create_session)
1465 ASSERT_NE(sid_in_parent, getsid(0));
1466 else
1467 ASSERT_EQ(sid_in_parent, getsid(0));
1468 exit(0);
1469 }
1470
1471 EXPECT_EQ(write(pipe_fds[1], &parent_sid, pid_size), pid_size);
1472 waitpid(child_pid, &status, 0);
1473 ASSERT_TRUE(WIFEXITED(status));
1474 EXPECT_EQ(WEXITSTATUS(status), 0);
1475}
1476
1477TEST(Test, default_no_new_session) {
1478 TestCreateSession(/*create_session=*/false);
1479}
1480
1481TEST(Test, create_new_session) {
1482 TestCreateSession(/*create_session=*/true);
1483}