blob: 40db4e3a20fd09339a4fadd030ef3e3d59c3d76b [file] [log] [blame]
Dylan Reid837c74a2016-01-22 17:25:21 -08001/* 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
6#define _GNU_SOURCE /* For asprintf */
7
8#include <errno.h>
9#include <signal.h>
10#include <sys/mount.h>
11#include <sys/stat.h>
12#include <sys/types.h>
13#include <unistd.h>
14
15#include "test_harness.h"
16
17#include "container_cgroup.h"
18#include "libcontainer.h"
19
20static const pid_t INIT_TEST_PID = 5555;
21
22struct mount_args {
23 char *source;
24 char *target;
25 char *filesystemtype;
26 unsigned long mountflags;
27 const void *data;
28};
29static struct mount_args mount_call_args[5];
30static int mount_called;
31
32struct mknod_args {
33 char *pathname;
34 mode_t mode;
35 dev_t dev;
36};
37static struct mknod_args mknod_call_args;
38
39static int kill_called;
40static int kill_sig;
41static const char *minijail_alt_syscall_table;
42static int minijail_ipc_called;
43static int minijail_vfs_called;
44static int minijail_net_called;
45static int minijail_pids_called;
46static int minijail_run_as_init_called;
47static int minijail_user_called;
48static int minijail_wait_called;
49static int minijail_reset_signal_mask_called;
50static int mount_ret;
51static char *mkdtemp_root;
52
53/* global mock cgroup. */
Dylan Reid355d5e42016-04-29 16:53:31 -070054#define MAX_ADD_DEVICE_CALLS 2
Dylan Reid837c74a2016-01-22 17:25:21 -080055struct mock_cgroup {
56 struct container_cgroup cg;
57 int freeze_ret;
58 int thaw_ret;
59 int deny_all_devs_ret;
60 int add_device_ret;
61
62 int init_called_count;
63 int deny_all_devs_called_count;
64
Dylan Reid355d5e42016-04-29 16:53:31 -070065 int add_dev_major[MAX_ADD_DEVICE_CALLS];
66 int add_dev_minor[MAX_ADD_DEVICE_CALLS];
67 int add_dev_read[MAX_ADD_DEVICE_CALLS];
68 int add_dev_write[MAX_ADD_DEVICE_CALLS];
69 int add_dev_modify[MAX_ADD_DEVICE_CALLS];
70 char add_dev_type[MAX_ADD_DEVICE_CALLS];
71 int add_dev_called_count;
Dylan Reid837c74a2016-01-22 17:25:21 -080072};
73
74static struct mock_cgroup gmcg;
75
76static int mock_freeze(const struct container_cgroup *cg)
77{
78 struct mock_cgroup *mcg = (struct mock_cgroup *)cg;
79 return mcg->freeze_ret;
80}
81
82static int mock_thaw(const struct container_cgroup *cg)
83{
84 struct mock_cgroup *mcg = (struct mock_cgroup *)cg;
85 return mcg->thaw_ret;
86}
87
88static int mock_deny_all_devices(const struct container_cgroup *cg)
89{
90 struct mock_cgroup *mcg = (struct mock_cgroup *)cg;
91 ++mcg->deny_all_devs_called_count;
92 return mcg->deny_all_devs_ret;
93}
94
95static int mock_add_device(const struct container_cgroup *cg, int major,
96 int minor, int read, int write, int modify,
97 char type)
98{
99 struct mock_cgroup *mcg = (struct mock_cgroup *)cg;
Dylan Reid355d5e42016-04-29 16:53:31 -0700100
101 if (mcg->add_dev_called_count >= MAX_ADD_DEVICE_CALLS)
102 return mcg->add_device_ret;
103 mcg->add_dev_major[mcg->add_dev_called_count] = major;
104 mcg->add_dev_minor[mcg->add_dev_called_count] = minor;
105 mcg->add_dev_read[mcg->add_dev_called_count] = read;
106 mcg->add_dev_write[mcg->add_dev_called_count] = write;
107 mcg->add_dev_modify[mcg->add_dev_called_count] = modify;
108 mcg->add_dev_type[mcg->add_dev_called_count] = type;
109 mcg->add_dev_called_count++;
Dylan Reid837c74a2016-01-22 17:25:21 -0800110 return mcg->add_device_ret;
111}
112
113struct container_cgroup *container_cgroup_new(const char *name,
114 const char *cgroup_root)
115{
116 gmcg.cg.name = strdup(name);
117 return &gmcg.cg;
118}
119
120void container_cgroup_destroy(struct container_cgroup *c)
121{
122 free(c->name);
123}
124
125/* Start of tests. */
126FIXTURE(container_test) {
127 struct container_config *config;
128 struct container *container;
129 int mount_flags;
130 char *rootfs;
131};
132
133FIXTURE_SETUP(container_test)
134{
135 char temp_template[] = "/tmp/cgtestXXXXXX";
136 char rundir_template[] = "/tmp/cgtest_runXXXXXX";
137 char *rundir;
138 char path[256];
139 char *pargs[] = {
140 "/sbin/init",
141 };
142
143 memset(&mount_call_args, 0, sizeof(mount_call_args));
144 mount_called = 0;
145 memset(&mknod_call_args, 0, sizeof(mknod_call_args));
146 mkdtemp_root = NULL;
147
148 memset(&gmcg, 0, sizeof(gmcg));
149 static const struct cgroup_ops cgops = {
150 .freeze = mock_freeze,
151 .thaw = mock_thaw,
152 .deny_all_devices = mock_deny_all_devices,
153 .add_device = mock_add_device,
154 };
155 gmcg.cg.ops = &cgops;
156
157 self->rootfs = strdup(mkdtemp(temp_template));
158
159 kill_called = 0;
160 minijail_alt_syscall_table = NULL;
161 minijail_ipc_called = 0;
162 minijail_vfs_called = 0;
163 minijail_net_called = 0;
164 minijail_pids_called = 0;
165 minijail_run_as_init_called = 0;
166 minijail_user_called = 0;
167 minijail_wait_called = 0;
168 minijail_reset_signal_mask_called = 0;
169 mount_ret = 0;
170
171 snprintf(path, sizeof(path), "%s/dev", self->rootfs);
172 //mkdir(path, S_IRWXU | S_IRWXG);
173
174 self->mount_flags = MS_NOSUID | MS_NODEV | MS_NOEXEC;
175
176 self->config = container_config_create();
177 container_config_rootfs(self->config, self->rootfs);
178 container_config_program_argv(self->config, pargs, 1);
179 container_config_alt_syscall_table(self->config, "testsyscalltable");
180 container_config_add_mount(self->config,
181 "testtmpfs",
182 "tmpfs",
183 "/tmp",
184 "tmpfs",
185 NULL,
186 self->mount_flags,
187 1000,
188 1000,
189 0x666,
190 0,
191 1);
192 container_config_add_device(self->config,
193 'c',
194 "/dev/foo",
195 S_IRWXU | S_IRWXG,
196 245,
197 2,
Dylan Reid355d5e42016-04-29 16:53:31 -0700198 0,
199 1000,
200 1001,
201 1,
202 1,
203 0);
204 /* test dynamic minor on /dev/null */
205 container_config_add_device(self->config,
206 'c',
207 "/dev/null",
208 S_IRWXU | S_IRWXG,
209 1,
210 -1,
211 1,
Dylan Reid837c74a2016-01-22 17:25:21 -0800212 1000,
213 1001,
214 1,
215 1,
216 0);
217
218 rundir = mkdtemp(rundir_template);
219 self->container = container_new("containerUT", rundir, self->config);
220 ASSERT_NE(NULL, self->container);
221}
222
223FIXTURE_TEARDOWN(container_test)
224{
225 char path[256];
226 int i;
227
228 container_destroy(self->container);
229 snprintf(path, sizeof(path), "rm -rf %s", self->rootfs);
230 EXPECT_EQ(0, system(path));
231 free(self->rootfs);
232
233 for (i = 0; i < mount_called; i++) {
234 free(mount_call_args[i].source);
235 free(mount_call_args[i].target);
236 free(mount_call_args[i].filesystemtype);
237 }
238 free(mknod_call_args.pathname);
239 free(mkdtemp_root);
240}
241
242TEST_F(container_test, test_mount_tmp_start)
243{
244 char *path;
245
246 EXPECT_EQ(0, container_start(self->container));
247 EXPECT_EQ(2, mount_called);
248 EXPECT_EQ(0, strcmp(mount_call_args[1].source, "tmpfs"));
249 EXPECT_LT(0, asprintf(&path, "%s/root/tmp", mkdtemp_root));
250 EXPECT_EQ(0, strcmp(mount_call_args[1].target, path));
251 free(path);
252 EXPECT_EQ(0, strcmp(mount_call_args[1].filesystemtype,
253 "tmpfs"));
254 EXPECT_EQ(mount_call_args[1].mountflags, self->mount_flags);
255 EXPECT_EQ(mount_call_args[1].data, NULL);
256
257 EXPECT_EQ(1, minijail_ipc_called);
258 EXPECT_EQ(1, minijail_vfs_called);
259 EXPECT_EQ(1, minijail_net_called);
260 EXPECT_EQ(1, minijail_pids_called);
261 EXPECT_EQ(1, minijail_user_called);
262 EXPECT_EQ(1, minijail_run_as_init_called);
263 EXPECT_EQ(1, gmcg.deny_all_devs_called_count);
264
265 EXPECT_LT(0, asprintf(&path, "%s/root/dev/foo", mkdtemp_root));
266 EXPECT_EQ(0, strcmp(mknod_call_args.pathname, path));
267 free(path);
268 EXPECT_EQ(mknod_call_args.mode, S_IRWXU | S_IRWXG | S_IFCHR);
269 EXPECT_EQ(mknod_call_args.dev, makedev(245, 2));
270
Dylan Reid355d5e42016-04-29 16:53:31 -0700271 EXPECT_EQ(245, gmcg.add_dev_major[0]);
272 EXPECT_EQ(2, gmcg.add_dev_minor[0]);
273 EXPECT_EQ(1, gmcg.add_dev_read[0]);
274 EXPECT_EQ(1, gmcg.add_dev_write[0]);
275 EXPECT_EQ(0, gmcg.add_dev_modify[0]);
276 EXPECT_EQ('c', gmcg.add_dev_type[0]);
277
278 EXPECT_EQ(1, gmcg.add_dev_major[1]);
279 EXPECT_EQ(3, gmcg.add_dev_minor[1]);
280 EXPECT_EQ(1, gmcg.add_dev_read[1]);
281 EXPECT_EQ(1, gmcg.add_dev_write[1]);
282 EXPECT_EQ(0, gmcg.add_dev_modify[1]);
283 EXPECT_EQ('c', gmcg.add_dev_type[1]);
Dylan Reid837c74a2016-01-22 17:25:21 -0800284
285 ASSERT_NE(NULL, minijail_alt_syscall_table);
286 EXPECT_EQ(0, strcmp(minijail_alt_syscall_table,
287 "testsyscalltable"));
288
289 EXPECT_EQ(0, container_wait(self->container));
290 EXPECT_EQ(1, minijail_wait_called);
291 EXPECT_EQ(1, minijail_reset_signal_mask_called);
292}
293
294TEST_F(container_test, test_kill_container)
295{
296 EXPECT_EQ(0, container_start(self->container));
297 EXPECT_EQ(0, container_kill(self->container));
298 EXPECT_EQ(1, kill_called);
299 EXPECT_EQ(SIGKILL, kill_sig);
300 EXPECT_EQ(1, minijail_wait_called);
301}
302
303/* libc stubs so the UT doesn't need root to call mount, etc. */
304int mount(const char *source, const char *target,
305 const char *filesystemtype, unsigned long mountflags,
306 const void *data)
307{
308 if (mount_called >= 5)
309 return 0;
310
311 mount_call_args[mount_called].source = strdup(source);
312 mount_call_args[mount_called].target = strdup(target);
313 mount_call_args[mount_called].filesystemtype = strdup(filesystemtype);
314 mount_call_args[mount_called].mountflags = mountflags;
315 mount_call_args[mount_called].data = data;
316 ++mount_called;
317 return 0;
318}
319
320int umount(const char *target)
321{
322 return 0;
323}
324
325int mknod(const char *pathname, mode_t mode, dev_t dev)
326{
327 mknod_call_args.pathname = strdup(pathname);
328 mknod_call_args.mode = mode;
329 mknod_call_args.dev = dev;
330 return 0;
331}
332
333int chown(const char *path, uid_t owner, gid_t group)
334{
335 return 0;
336};
337
338int kill(pid_t pid, int sig)
339{
340 ++kill_called;
341 kill_sig = sig;
342 return 0;
343}
344
345int stat(const char *path, struct stat *buf)
346{
347 return 0;
348}
349
350int chmod(const char *path, mode_t mode)
351{
352 return 0;
353}
354
355char *mkdtemp(char *template)
356{
357 mkdtemp_root = strdup(template);
358 return template;
359}
360
361int mkdir(const char *pathname, mode_t mode)
362{
363 return 0;
364}
365
366int rmdir(const char *pathname)
367{
368 return 0;
369}
370
371int unlink(const char *pathname)
372{
373 return 0;
374}
375
376/* Minijail stubs */
377struct minijail *minijail_new(void)
378{
379 return NULL;
380}
381
382int minijail_mount(struct minijail *j, const char *src, const char *dest,
383 const char *type, unsigned long flags)
384{
385 return 0;
386}
387
388void minijail_namespace_vfs(struct minijail *j)
389{
390 ++minijail_vfs_called;
391}
392
393void minijail_namespace_ipc(struct minijail *j)
394{
395 ++minijail_ipc_called;
396}
397
398void minijail_namespace_net(struct minijail *j)
399{
400 ++minijail_net_called;
401}
402
403void minijail_namespace_pids(struct minijail *j)
404{
405 ++minijail_pids_called;
406}
407
408void minijail_namespace_user(struct minijail *j)
409{
410 ++minijail_user_called;
411}
412
413int minijail_uidmap(struct minijail *j, const char *uidmap)
414{
415 return 0;
416}
417
418int minijail_gidmap(struct minijail *j, const char *gidmap)
419{
420 return 0;
421}
422
423int minijail_enter_pivot_root(struct minijail *j, const char *dir)
424{
425 return 0;
426}
427
428void minijail_run_as_init(struct minijail *j)
429{
430 ++minijail_run_as_init_called;
431}
432
433int minijail_run_pid_pipes_no_preload(struct minijail *j, const char *filename,
434 char *const argv[], pid_t *pchild_pid,
435 int *pstdin_fd, int *pstdout_fd,
436 int *pstderr_fd)
437{
438 *pchild_pid = INIT_TEST_PID;
439 return 0;
440}
441
442int minijail_write_pid_file(struct minijail *j, const char *path)
443{
444 return 0;
445}
446
447int minijail_wait(struct minijail *j)
448{
449 ++minijail_wait_called;
450 return 0;
451}
452
453int minijail_use_alt_syscall(struct minijail *j, const char *table)
454{
455 minijail_alt_syscall_table = table;
456 return 0;
457}
458
459void minijail_add_to_cgroup(struct minijail *j, const char *cg_path)
460{
461}
462
463void minijail_reset_signal_mask(struct minijail *j)
464{
465 ++minijail_reset_signal_mask_called;
466}
467
468TEST_HARNESS_MAIN