The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxc/pull/2300
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Hi there! This is an initial step for moving the mount injection stuff from LXD to LXC. In this PR, the new `lxc_container` API method `mount` is introduced, alongside with the supporting config option `shmounts` for `lxc.mount.auto` (because in order to enable mount injection in the running container, a shared mount point is established between on a startup) and the necessary utils.
From 5f8aabfc231000249d2d11fd0f3b49aee6826570 Mon Sep 17 00:00:00 2001 From: Liza Tretyakova <[email protected]> Date: Wed, 2 May 2018 10:09:34 +0300 Subject: [PATCH 1/6] conf, confile: introduce basic structs for shared mount point Signed-off-by: Liza Tretyakova <[email protected]> --- src/lxc/conf.c | 4 ++++ src/lxc/conf.h | 10 ++++++++++ src/lxc/confile.c | 1 + 3 files changed, 15 insertions(+) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 48c4b4ecc..a52331c86 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2665,6 +2665,8 @@ struct lxc_conf *lxc_conf_init(void) new->lsm_aa_profile = NULL; new->lsm_se_context = NULL; new->tmp_umount_proc = 0; + new->lxc_shmount.path_host = NULL; + new->lxc_shmount.path_cont = NULL; /* if running in a new user namespace, init and COMMAND * default to running as UID/GID 0 when using lxc-execute */ @@ -3869,6 +3871,8 @@ void lxc_conf_free(struct lxc_conf *conf) lxc_clear_procs(conf, "lxc.proc"); free(conf->cgroup_meta.dir); free(conf->cgroup_meta.controllers); + free(conf->lxc_shmount.path_host); + free(conf->lxc_shmount.path_cont); free(conf); } diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 351059c94..542fdd61f 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -185,6 +185,9 @@ enum { LXC_AUTO_CGROUP_FULL_NOSPEC = 0x0E0, /* /sys/fs/cgroup (full mount, r/w or mixed, depending on caps) */ LXC_AUTO_CGROUP_FORCE = 0x100, /* mount cgroups even when cgroup namespaces are supported */ LXC_AUTO_CGROUP_MASK = 0x1F0, /* all known cgroup options, doe not contain LXC_AUTO_CGROUP_FORCE */ + + LXC_AUTO_SHMOUNTS = 0x200, /* shared mount point */ + LXC_AUTO_SHMOUNTS_MASK = 0x200, /* shared mount point mask */ LXC_AUTO_ALL_MASK = 0x1FF, /* all known settings */ }; @@ -365,6 +368,13 @@ struct lxc_conf { /* procs */ struct lxc_list procs; + + struct lxc_shmount { + /* Absolute path to the shared mount point on the host */ + char *path_host; + /* Absolute path (in the container) to the shared mount point */ + char *path_cont; + } lxc_shmount; }; extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 9704bbbec..d0770542a 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1743,6 +1743,7 @@ static int set_config_mount_auto(const char *key, const char *value, { "cgroup-full:mixed:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_MIXED | LXC_AUTO_CGROUP_FORCE }, { "cgroup-full:ro:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RO | LXC_AUTO_CGROUP_FORCE }, { "cgroup-full:rw:force", LXC_AUTO_CGROUP_MASK, LXC_AUTO_CGROUP_FULL_RW | LXC_AUTO_CGROUP_FORCE }, + { "shmounts:", LXC_AUTO_SHMOUNTS_MASK, LXC_AUTO_SHMOUNTS }, /* For adding anything that is just a single on/off, but has no * options: keep mask and flag identical and just define the enum * value as an unused bit so far From 17e460874cc28edec34cc3139151d14843af88cc Mon Sep 17 00:00:00 2001 From: Liza Tretyakova <[email protected]> Date: Wed, 2 May 2018 10:28:39 +0300 Subject: [PATCH 2/6] conf, confile: add parsing of a shmounts config parameter Signed-off-by: Liza Tretyakova <[email protected]> --- src/lxc/conf.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/lxc/confile.c | 20 ++++++++++++++++++++ src/lxc/confile.h | 2 ++ 3 files changed, 64 insertions(+) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index a52331c86..7819ac20c 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -23,6 +23,7 @@ #define _GNU_SOURCE #include "config.h" +#include "confile.h" #include <arpa/inet.h> #include <dirent.h> @@ -630,6 +631,39 @@ unsigned long add_required_remount_flags(const char *s, const char *d, #endif } +static int add_shmount_to_list(struct lxc_conf *conf) { + char *new_mount; + size_t len_mount; + /* Offset for the leading '/' since the path_cont + * is absolute inside the container */ + int ret = -1, offset = 1; + + /* +1 for the separating whitespace */ + len_mount = strlen(conf->lxc_shmount.path_host) + 1 + + strlen(conf->lxc_shmount.path_cont) - offset + + sizeof(" none bind,create=dir 0 0") - 1; + /* +1 for the termintating '\0' */ + new_mount = malloc(len_mount + 1); + if (!new_mount) { + SYSERROR("Not enough memory"); + return -ENOMEM; + } + + ret = snprintf(new_mount, len_mount + 1, "%s %s none bind,create=dir 0 0", + conf->lxc_shmount.path_host, conf->lxc_shmount.path_cont + offset); + if (ret < 0 || ret >= len_mount + 1) { + free(new_mount); + return -1; + } + + ret = add_elem_to_mount_list(new_mount, conf); + if (ret < 0) + ERROR("Failed to add new mount \"%s\" to the config", new_mount); + + free(new_mount); + return ret; +} + static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_handler *handler) { int i, r; @@ -763,6 +797,14 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_ha } } + if (flags & LXC_AUTO_SHMOUNTS_MASK) { + int ret = add_shmount_to_list(conf); + if (ret < 0) { + ERROR("Failed to add shmount entry to container config"); + return ret; + } + } + return 0; } diff --git a/src/lxc/confile.c b/src/lxc/confile.c index d0770542a..3ed219ebd 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1761,6 +1761,8 @@ static int set_config_mount_auto(const char *key, const char *value, return -1; for (autoptr = autos;; autoptr = NULL) { + bool is_shmounts = false; + token = strtok_r(autoptr, " \t", &sptr); if (!token) { ret = 0; @@ -1770,6 +1772,12 @@ static int set_config_mount_auto(const char *key, const char *value, for (i = 0; allowed_auto_mounts[i].token; i++) { if (!strcmp(allowed_auto_mounts[i].token, token)) break; + + if (strcmp("shmounts:", allowed_auto_mounts[i].token) == 0 + && strncmp("shmounts:", token, sizeof("shmounts:") - 1) == 0) { + is_shmounts = true; + break; + } } if (!allowed_auto_mounts[i].token) { @@ -1779,6 +1787,14 @@ static int set_config_mount_auto(const char *key, const char *value, lxc_conf->auto_mounts &= ~allowed_auto_mounts[i].mask; lxc_conf->auto_mounts |= allowed_auto_mounts[i].flag; + if (is_shmounts) { + lxc_conf->lxc_shmount.path_host = strdup(token + (sizeof("shmounts:") - 1)); + if (strcmp(lxc_conf->lxc_shmount.path_host, "") == 0) { + ERROR("Invalid shmounts path: empty"); + break; + } + lxc_conf->lxc_shmount.path_cont = strdup("/dev/.lxc-mounts"); + } } free(autos); @@ -1810,6 +1826,10 @@ static int set_config_mount(const char *key, const char *value, return 0; } +int add_elem_to_mount_list(const char *value, struct lxc_conf *lxc_conf) { + return set_config_mount(NULL, value, lxc_conf, NULL); +} + static int set_config_cap_keep(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { diff --git a/src/lxc/confile.h b/src/lxc/confile.h index 0d877c898..894cb79bf 100644 --- a/src/lxc/confile.h +++ b/src/lxc/confile.h @@ -112,4 +112,6 @@ bool clone_update_unexp_ovl_paths(struct lxc_conf *conf, const char *oldpath, extern bool network_new_hwaddrs(struct lxc_conf *conf); +extern int add_elem_to_mount_list(const char *value, struct lxc_conf *lxc_conf); + #endif /* __LXC_CONFILE_H */ From abc0fbf7ce2606c3101378ff4a242dbedbb1d1d2 Mon Sep 17 00:00:00 2001 From: Liza Tretyakova <[email protected]> Date: Wed, 2 May 2018 10:47:15 +0300 Subject: [PATCH 3/6] utils: add shared mount point detection Signed-off-by: Liza Tretyakova <[email protected]> --- src/lxc/utils.c | 33 ++++++++++++++++++++------------- src/lxc/utils.h | 1 + 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index 3d0f8641c..f30bfc76a 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -1133,19 +1133,12 @@ uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval) return hval; } -/* - * Detect whether / is mounted MS_SHARED. The only way I know of to - * check that is through /proc/self/mountinfo. - * I'm only checking for /. If the container rootfs or mount location - * is MS_SHARED, but not '/', then you're out of luck - figuring that - * out would be too much work to be worth it. - */ -int detect_shared_rootfs(void) +bool is_shared_mountpoint(const char *path) { - char buf[LXC_LINELEN], *p; + char buf[LXC_LINELEN]; FILE *f; int i; - char *p2; + char *p, *p2; f = fopen("/proc/self/mountinfo", "r"); if (!f) @@ -1159,16 +1152,30 @@ int detect_shared_rootfs(void) if (!p2) continue; *p2 = '\0'; - if (strcmp(p + 1, "/") == 0) { - /* This is '/'. Is it shared? */ + if (strcmp(p + 1, path) == 0) { + /* This is the path. Is it shared? */ p = strchr(p2 + 1, ' '); if (p && strstr(p, "shared:")) { fclose(f); - return 1; + return true; } } } fclose(f); + return false; +} + +/* + * Detect whether / is mounted MS_SHARED. The only way I know of to + * check that is through /proc/self/mountinfo. + * I'm only checking for /. If the container rootfs or mount location + * is MS_SHARED, but not '/', then you're out of luck - figuring that + * out would be too much work to be worth it. + */ +int detect_shared_rootfs(void) +{ + if(is_shared_mountpoint("/")) + return 1; return 0; } diff --git a/src/lxc/utils.h b/src/lxc/utils.h index b15076cbd..dbab1e183 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -490,6 +490,7 @@ extern bool dir_exists(const char *path); #define FNV1A_64_INIT ((uint64_t)0xcbf29ce484222325ULL) extern uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval); +extern bool is_shared_mountpoint(const char *path); extern int detect_shared_rootfs(void); extern bool detect_ramfs_rootfs(void); extern char *on_path(const char *cmd, const char *rootfs); From 0eb3a8d374b8a4cddf4816844f3c2d4fe9f8ea16 Mon Sep 17 00:00:00 2001 From: Liza Tretyakova <[email protected]> Date: Wed, 2 May 2018 10:58:54 +0300 Subject: [PATCH 4/6] start: add shmount setup on container start Signed-off-by: Liza Tretyakova <[email protected]> --- src/lxc/start.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/lxc/start.c b/src/lxc/start.c index d7f079979..6584ebd13 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1473,6 +1473,70 @@ static inline int do_share_ns(void *arg) return 0; } +static int lxc_setup_shmount(struct lxc_conf *conf) { + size_t len_cont; + char *full_cont_path; + int ret = -1; + + /* Construct the shmount path under the container root */ + /* +1 for slash */ + len_cont = strlen(conf->rootfs.mount) + 1 + strlen(conf->lxc_shmount.path_cont); + /* +1 for the terminating '\0' */ + full_cont_path = malloc(len_cont + 1); + if(!full_cont_path) { + SYSERROR("Not enough memory"); + return -ENOMEM; + } + ret = snprintf(full_cont_path, len_cont + 1, "%s/%s", conf->rootfs.mount, conf->lxc_shmount.path_cont); + if (ret < 0 || ret >= len_cont + 1) { + SYSERROR("Failed to create filename"); + free(full_cont_path); + return -1; + } + + /* Check if shmount point is already set up */ + if (is_shared_mountpoint(conf->lxc_shmount.path_host)) { + INFO("Path \"%s\" is already MS_SHARED. Reusing", conf->lxc_shmount.path_host); + free(full_cont_path); + return 0; + } + + /* Create host and cont mount paths */ + ret = mkdir_p(conf->lxc_shmount.path_host, 0711); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", conf->lxc_shmount.path_host); + free(full_cont_path); + return ret; + } + + ret = mkdir_p(full_cont_path, 0711); + if (ret < 0 && errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", full_cont_path); + free(full_cont_path); + return ret; + } + + /* Prepare host mountpoint */ + ret = mount("tmpfs", conf->lxc_shmount.path_host, "tmpfs", + 0, "size=100k,mode=0711"); + if (ret < 0) { + SYSERROR("Failed to mount \"%s\"", conf->lxc_shmount.path_host); + free(full_cont_path); + return ret; + } + ret = mount(conf->lxc_shmount.path_host, conf->lxc_shmount.path_host, "none", + MS_REC | MS_SHARED, ""); + if (ret < 0) { + SYSERROR("Failed to make shared \"%s\"", conf->lxc_shmount.path_host); + free(full_cont_path); + return ret; + } + + INFO("Made shared mount point \"%s\"", conf->lxc_shmount.path_host); + free(full_cont_path); + return 0; +} + /* lxc_spawn() performs crucial setup tasks and clone()s the new process which * exec()s the requested container binary. * Note that lxc_spawn() runs in the parent namespaces. Any operations performed @@ -1549,6 +1613,21 @@ static int lxc_spawn(struct lxc_handler *handler) } } + if (conf->lxc_shmount.path_host) { + if(!conf->lxc_shmount.path_cont) { + ERROR("Missing the container side path to the shared mount point"); + lxc_sync_fini(handler); + return -1; + } + + ret = lxc_setup_shmount(conf); + if (ret < 0) { + ERROR("Failed to setup shared mount point"); + lxc_sync_fini(handler); + return -1; + } + } + if (!cgroup_init(handler)) { ERROR("Failed initializing cgroup support"); goto out_delete_net; From 1b9fdb0bb4db5f3bbd4bc64a4daacb0346b1cfc3 Mon Sep 17 00:00:00 2001 From: Liza Tretyakova <[email protected]> Date: Wed, 2 May 2018 11:07:58 +0300 Subject: [PATCH 5/6] lxccontainer: add container API function and structs for injecting a mount Signed-off-by: Liza Tretyakova <[email protected]> --- src/lxc/lxccontainer.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lxc/lxccontainer.h | 13 +++++ 2 files changed, 165 insertions(+) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 30efeaff8..803f74cea 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -30,9 +30,11 @@ #include <stdarg.h> #include <stdint.h> #include <stdio.h> +#include <stdlib.h> #include <sys/file.h> #include <sys/mman.h> #include <sys/mount.h> +#include <sys/stat.h> #include <sys/syscall.h> #include <sys/sysmacros.h> #include <sys/types.h> @@ -457,6 +459,25 @@ static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3) \ return ret; \ } +#define WRAP_API_6(rettype, fnname, t1, t2, t3, t4, t5, t6) \ +static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3, \ + t4 a4, t5 a5, t6 a6) \ +{ \ + rettype ret; \ + bool reset_config = false; \ + \ + if (!current_config && c && c->lxc_conf) { \ + current_config = c->lxc_conf; \ + reset_config = true; \ + } \ + \ + ret = do_##fnname(c, a1, a2, a3, a4, a5, a6); \ + if (reset_config) \ + current_config = NULL; \ + \ + return ret; \ +} + WRAP_API(bool, lxcapi_is_defined) static const char *do_lxcapi_state(struct lxc_container *c) @@ -4691,6 +4712,136 @@ static bool do_lxcapi_restore(struct lxc_container *c, char *directory, bool ver WRAP_API_2(bool, lxcapi_restore, char *, bool) +static int do_lxcapi_mount(struct lxc_container *c, + const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, + const void *data, struct lxc_mount *mnt) { + char *template, *suff, *path; + pid_t pid, init_pid; + size_t len; + int ret = -1, fd = -EBADF; + + if (!c || !c->lxc_conf) { + ERROR("Container or configuration is NULL"); + return -EINVAL; + } + + len = strlen(c->lxc_conf->lxc_shmount.path_host) + sizeof("/lxcmountXXXXXX") - 1; + template = malloc(len + 1); + if (!template) { + SYSERROR("Not enough memory"); + return -ENOMEM; + } + ret = snprintf(template, len + 1, "%s/lxcmountXXXXXX", c->lxc_conf->lxc_shmount.path_host); + if (ret < 0 || (size_t)ret >= len + 1) { + SYSERROR("Error writing shmounts tempdir name"); + goto out_err_free; + } + + /* Create a temporary dir under the shared mountpoint */ + template = mkdtemp(template); + if (!template) { + SYSERROR("Could not create shmounts temporary dir"); + goto out_err_free; + } + + /* Do the fork */ + pid = fork(); + if (pid < 0) { + SYSERROR("Could not fork"); + goto out_err_free; + } + + if (pid == 0) { + int exit_status = EXIT_FAILURE; + + /* Do the mount */ + ret = mount(source, template, filesystemtype, mountflags, data); + if (ret < 0) { + SYSERROR("Failed to mount \"%s\" onto \"%s\"", source, template); + goto out_child_err_temp; + } + + init_pid = do_lxcapi_init_pid(c); + if (init_pid < 0) { + ERROR("Failed to obtain container's init pid"); + goto out_child_err_temp; + } + + /* Enter the container namespaces */ + if (am_guest_unpriv() && !lxc_list_empty(&c->lxc_conf->id_map)) { + /* if fully unprivileged */ + if (!switch_to_ns(init_pid, "user")){ + ERROR("Failed to enter user namespace"); + goto out_child_err_temp; + } + } + if (!switch_to_ns(init_pid, "mnt")) { + ERROR("Failed to enter mount namespace"); + goto out_child_err_temp; + } + + suff = strrchr(template, '/'); + if (!suff) + goto out_child_err_temp; + + len = strlen(c->lxc_conf->lxc_shmount.path_cont) + sizeof("/lxcmountXXXXXX") - 1; + path = malloc(len + 1); + if (!path) { + SYSERROR("Not enough memory"); + goto out_child_err_temp; + } + ret = snprintf(path, len + 1, "%s%s", c->lxc_conf->lxc_shmount.path_cont, suff); + if (ret < 0 || (size_t)ret >= len + 1) { + SYSERROR("Error writing container mountpoint name"); + goto out_child_err_path; + } + + ret = mkdir_p(target, 0700); + if (ret < 0) { + ERROR("Failed to create container temp mountpoint"); + goto out_child_err_path; + } + + ret = mount(path, target, NULL, MS_REC | MS_MOVE, NULL); + if (ret < 0) { + SYSERROR("Failed to move the mount from \"%s\" to \"%s\"", path, target); + goto out_child_err_path; + } + + exit_status = EXIT_SUCCESS; + +out_child_err_path: + free(path); + +out_child_err_temp: + free(template); + + _exit(exit_status); + } + + ret = wait_for_pid(pid); + if (ret < 0) { + SYSERROR("Wait for the child with pid %ld failed", (long) pid); + goto out_err_free; + } + + ret = 0; + + (void)umount2(template, MNT_DETACH); + (void)unlink(template); + +out_err_free: + if (fd >= 0) + close(fd); + free(template); + + return ret; +} + +WRAP_API_6(int, lxcapi_mount, const char *, const char *, const char *, + unsigned long, const void *, struct lxc_mount*) + static int lxcapi_attach_run_waitl(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...) { va_list ap; @@ -4839,6 +4990,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->restore = lxcapi_restore; c->migrate = lxcapi_migrate; c->console_log = lxcapi_console_log; + c->mount = lxcapi_mount; return c; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 7bbac2f06..c61a1b840 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -42,6 +42,7 @@ extern "C" { #define LXC_CLONE_MAXFLAGS (1 << 5) /*!< Number of \c LXC_CLONE_* flags */ #define LXC_CREATE_QUIET (1 << 0) /*!< Redirect \c stdin to \c /dev/zero and \c stdout and \c stderr to \c /dev/null */ #define LXC_CREATE_MAXFLAGS (1 << 1) /*!< Number of \c LXC_CREATE* flags */ +#define LXC_MOUNT_API_V1 1 struct bdev_specs; @@ -53,6 +54,10 @@ struct migrate_opts; struct lxc_console_log; +struct lxc_mount { + int version; +}; + /*! * An LXC container. * @@ -846,6 +851,14 @@ struct lxc_container { * \return \c true if the container was rebooted successfully, else \c false. */ bool (*reboot2)(struct lxc_container *c, int timeout); + + /*! + * \brief Mount the host's path `source` onto the container's path `target`. + */ + int (*mount)(struct lxc_container *c, + const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, + const void *data, struct lxc_mount *mnt); }; /*! From 8536cf1e6f4005d327216dfe6342b5f879d75398 Mon Sep 17 00:00:00 2001 From: Liza Tretyakova <[email protected]> Date: Wed, 2 May 2018 11:54:06 +0300 Subject: [PATCH 6/6] tests/mount_injection, tests/Makefile: add mount injection tests Signed-off-by: Liza Tretyakova <[email protected]> --- src/tests/Makefile.am | 5 +- src/tests/mount_injection.c | 304 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 src/tests/mount_injection.c diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index f39c3e047..59f8370e0 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -33,6 +33,7 @@ lxc_test_state_server_SOURCES = state_server.c lxctest.h lxc_test_share_ns_SOURCES = share_ns.c lxctest.h lxc_test_criu_check_feature_SOURCES = criu_check_feature.c lxctest.h lxc_test_raw_clone_SOURCES = lxc_raw_clone.c lxctest.h +lxc_test_mount_injection_SOURCES = mount_injection.c AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ @@ -63,7 +64,8 @@ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \ lxc-test-apparmor lxc-test-utils lxc-test-parse-config-file \ lxc-test-config-jump-table lxc-test-shortlived \ lxc-test-api-reboot lxc-test-state-server lxc-test-share-ns \ - lxc-test-criu-check-feature lxc-test-raw-clone + lxc-test-criu-check-feature lxc-test-raw-clone \ + lxc-test-mount-injection bin_SCRIPTS = if ENABLE_TOOLS @@ -120,6 +122,7 @@ EXTRA_DIST = \ lxc-test-unpriv \ lxc-test-utils.c \ may_control.c \ + mount_injection.c \ parse_config_file.c \ saveconfig.c \ shortlived.c \ diff --git a/src/tests/mount_injection.c b/src/tests/mount_injection.c new file mode 100644 index 000000000..1420e5174 --- /dev/null +++ b/src/tests/mount_injection.c @@ -0,0 +1,304 @@ +/* mount_injection + * + * Copyright © 2018 Elizaveta Tretiakova <[email protected]>. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#define _GNU_SOURCE +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <lxc/lxccontainer.h> +#include <lxc/list.h> +#include <string.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "lxctest.h" +#include "utils.h" + +#define NAME "mount_injection_test-" +#define TEMPLATE P_tmpdir"/mount_injection_XXXXXX" + +struct mountinfo_strings { + const char *mount_root; + const char *mount_point; + const char *fstype; + const char *mount_source; + const char *message; +}; + +static int comp_field(char *line, const char *str, int nfields) +{ + char *p, *p2; + int i, ret; + + if(!line) + return -1; + + if (!str) + return 0; + + for (p = line, i = 0; p && i < nfields; i++) + p = strchr(p + 1, ' '); + if (!p) + return -1; + p2 = strchr(p + 1, ' '); + if (p2) + *p2 = '\0'; + ret = strcmp(p + 1, str); + if (p2) + *p2 = ' '; + return ret; +} + +static int find_in_proc_mounts(void *data) +{ + char buf[LXC_LINELEN]; + FILE *f; + struct mountinfo_strings *strs = (struct mountinfo_strings *) data; + + fprintf(stderr, "%s", strs->message); + + f = fopen("/proc/self/mountinfo", "r"); + if (!f) + return 0; + while (fgets(buf, LXC_LINELEN, f)) { + if (comp_field(buf, strs->mount_root, 3) == 0 && comp_field(buf, strs->mount_point, 4) == 0) { + char *buf2 = strchr(buf, '-'); + if (comp_field(buf2, strs->fstype, 1) == 0 && comp_field(buf2, strs->mount_source, 2) == 0) { + fclose(f); + fprintf(stderr, "OK\n"); + _exit(EXIT_SUCCESS); + } + } + } + fclose(f); + fprintf(stderr, "ERR\n"); + _exit(EXIT_FAILURE); +} + +static int check_containers_mountinfo(struct lxc_container *c, struct mountinfo_strings *d) +{ + pid_t pid; + int ret = -1; + lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; + + ret = c->attach(c, find_in_proc_mounts, d, &attach_options, &pid); + if (ret < 0) { + fprintf(stderr, "Check of the container's mountinfo failed\n"); + return ret; + } + + ret = wait_for_pid(pid); + if (ret < 0) + fprintf(stderr, "Attached function failed"); + + return ret; +} + +/* config_items: NULL-terminated array of config pairs */ +static int perform_container_test(const char *name, const char *config_items[]) +{ + int i; + char *sret; + char template_log[sizeof(TEMPLATE)], template_dir[sizeof(TEMPLATE)], + device_message[sizeof("Check urandom deivce injected into "" - ") - 1 + strlen(name) + 1], + dir_message[sizeof("Check dir "" injected into "" - ") - 1 + sizeof(TEMPLATE) - 1 + strlen(name) + 1]; + struct lxc_container *c; + struct lxc_mount mnt; + struct lxc_log log; + int ret = -1, dev_msg_size = sizeof("Check urandom deivce injected into "" - ") - 1 + strlen(name) + 1, + dir_msg_size = sizeof("Check dir "" injected into "" - ") - 1 + sizeof(TEMPLATE) - 1 + strlen(name) + 1; + struct mountinfo_strings device = { + .mount_root = "/", + .mount_point = "/mnt/mount_injection_test_urandom", + .fstype = "devtmpfs", + .mount_source = "/dev/urandom", + .message = "" + }, dir = { + .mount_root = template_dir, + .mount_point = template_dir, + .fstype = "ext4", + .mount_source = NULL, + .message = "" + }; + + /* Temp paths and messages setup */ + strcpy(template_dir, TEMPLATE); + sret = mkdtemp(template_dir); + if (!sret) { + lxc_error("Failed to create temporary src file for container %s\n", name); + exit(EXIT_FAILURE); + } + + ret = snprintf(device_message, dev_msg_size, "Check urandom deivce injected into %s - ", name); + if (ret < 0 || ret >= dev_msg_size) { + fprintf(stderr, "Failed to create message for dev\n"); + exit(EXIT_FAILURE); + } + device.message = &device_message[0]; + + ret = snprintf(dir_message, dir_msg_size, "Check dir %s injected into %s - ", template_dir, name); + if (ret < 0 || ret >= dir_msg_size) { + fprintf(stderr, "Failed to create message for dir\n"); + exit(EXIT_FAILURE); + } + dir.message = &dir_message[0]; + + /* Setup logging*/ + strcpy(template_log, TEMPLATE); + i = lxc_make_tmpfile(template_log, false); + if (i < 0) { + lxc_error("Failed to create temporary log file for container %s\n", name); + exit(EXIT_FAILURE); + } else { + lxc_debug("Using \"%s\" as temporary log file for container %s\n", template_log, name); + close(i); + } + + log.name = name; + log.file = template_log; + log.level = "TRACE"; + log.prefix = "mount-injection"; + log.quiet = false; + log.lxcpath = NULL; + if (lxc_log_init(&log)) + exit(EXIT_FAILURE); + + /* Container setup */ + c = lxc_container_new(name, NULL); + if (!c) { + fprintf(stderr, "Unable to instantiate container (%s)...\n", name); + goto out; + } + + if (c->is_defined(c)) { + fprintf(stderr, "Container (%s) already exists\n", name); + goto out; + } + + for (int i = 0; config_items[i]; i += 2) { + if (!c->set_config_item(c, config_items[i], config_items[i + 1])) { + fprintf(stderr, "Failed to set \"%s\" config option to \"%s\"\n", config_items[i], config_items[i + 1]); + goto out; + } + } + + if (!c->create(c, "busybox", NULL, NULL, 1, NULL)) { + fprintf(stderr, "Creating the container (%s) failed...\n", name); + goto out; + } + + c->want_daemonize(c, true); + + if (!c->start(c, false, NULL)) { + fprintf(stderr, "Starting the container (%s) failed...\n", name); + goto out; + } + + mnt.version = LXC_MOUNT_API_V1; + + /* Check device mounted */ + ret = c->mount(c, "/dev/urandom", "/mnt/mount_injection_test_urandom", "devtmpfs", 0, NULL, &mnt); + if (ret < 0) { + fprintf(stderr, "Failed to mount \"/dev/urandom\"\n"); + goto out; + } + + ret = check_containers_mountinfo(c, &device); + if (ret < 0) + goto out; + + /* Check dir mounted */ + ret = c->mount(c, template_dir, template_dir, "ext4", MS_BIND, NULL, &mnt); + if (ret < 0) { + fprintf(stderr, "Failed to mount \"%s\"\n", template_dir); + goto out; + } + + ret = check_containers_mountinfo(c, &dir); + if (ret < 0) + goto out; + + if (!c->stop(c)) { + fprintf(stderr, "Stopping the container (%s) failed...\n", name); + goto out; + } + + if (!c->destroy(c)) { + fprintf(stderr, "Destroying the container (%s) failed...\n", name); + goto out; + } + + ret = 0; +out: + lxc_container_put(c); + + if (ret != 0) { + int fd; + + fd = open(template_log, O_RDONLY); + if (fd >= 0) { + char buf[4096]; + ssize_t buflen; + while ((buflen = read(fd, buf, 1024)) > 0) { + buflen = write(STDERR_FILENO, buf, buflen); + if (buflen <= 0) + break; + } + close(fd); + } + } + + unlink(template_log); + unlink(template_dir); + + return ret; +} + +static int do_priv_container_test() +{ + const char *config_items[] = {"lxc.mount.auto", "shmounts:/tmp/mount_injection_test", NULL}; + return perform_container_test(NAME"privileged", config_items); +} + +static int do_unpriv_container_test() +{ + const char *config_items[] = { + "lxc.mount.auto", "shmounts:/tmp/mount_injection_test", + "lxc.init.uid", "100000", + "lxc.init.gid", "100000", + NULL + }; + return perform_container_test(NAME"unprivileged", config_items); +} + +int main(int argc, char *argv[]) +{ + if (do_priv_container_test()) { + fprintf(stderr, "Privileged mount injection test failed\n"); + return -1; + } + if(do_unpriv_container_test()) { + fprintf(stderr, "Unprivileged mount injection test failed\n"); + return -1; + } + return 0; +}
_______________________________________________ lxc-devel mailing list [email protected] http://lists.linuxcontainers.org/listinfo/lxc-devel
