aboutsummaryrefslogtreecommitdiffstats
path: root/main/lxc
diff options
context:
space:
mode:
authorEivind Uggedal <eivind@uggedal.com>2015-09-30 17:34:21 +0000
committerEivind Uggedal <eivind@uggedal.com>2015-09-30 17:35:30 +0000
commit30dbccda05684a58b30f4756d3c45fad8894c896 (patch)
treeaeff5d9883b902347f2c07fe774cbf1386ab662e /main/lxc
parent5b2c05c632c187e0ae835cc7884e75fd5a2c7b9a (diff)
downloadaports-30dbccda05684a58b30f4756d3c45fad8894c896.tar.bz2
aports-30dbccda05684a58b30f4756d3c45fad8894c896.tar.xz
main/lxc: security fix for CVE-2015-1335
Diffstat (limited to 'main/lxc')
-rw-r--r--main/lxc/APKBUILD6
-rw-r--r--main/lxc/CVE-2015-1335.patch636
2 files changed, 641 insertions, 1 deletions
diff --git a/main/lxc/APKBUILD b/main/lxc/APKBUILD
index bbd8a83249..8da83e9c06 100644
--- a/main/lxc/APKBUILD
+++ b/main/lxc/APKBUILD
@@ -4,7 +4,7 @@
pkgname=lxc
pkgver=1.1.3
_mypkgver=${pkgver/_rc/.rc}
-pkgrel=1
+pkgrel=2
pkgdesc="linux containers - tools"
url="http://lxc.sourceforge.net/"
arch="all"
@@ -20,6 +20,7 @@ subpackages="$pkgname-dev $pkgname-doc $pkgname-lvm lua5.2-lxc:_lua52
source="https://github.com/lxc/lxc/archive/lxc-$_mypkgver.tar.gz
0001-Clone-bridge-interface-MTU-setting.patch
version.patch
+ CVE-2015-1335.patch
lxc.initd
"
@@ -91,12 +92,15 @@ dev() {
md5sums="680198729359bd5e58dc14ce617e72b1 lxc-1.1.3.tar.gz
d41088af5dee2e2df7975bb33d5d1537 0001-Clone-bridge-interface-MTU-setting.patch
79e90616b5049a472ccdcb5b1dcdd8b1 version.patch
+7327d4e94f1f23473d82c1fe98706eb2 CVE-2015-1335.patch
1e47412382517ee2adcbca00f07377ca lxc.initd"
sha256sums="b75fb8e376d3313e152f31fb7372b68f33a953a12191a39de4cded04fff94300 lxc-1.1.3.tar.gz
e9a6c2e5f98a5f03bf772333c91efe7dafbc4f8d699dced73859e7d6c98d2d42 0001-Clone-bridge-interface-MTU-setting.patch
b6d85fb23940d2511b3951de56b2532843c0e03ec1613548366361cc0c1a46b9 version.patch
+85af34f968ed1e00de357c2ff6d3234c144f53878a707d1d5f8d864407f23726 CVE-2015-1335.patch
ce4c30c65832c4b79df1c3c1ef6a444247e2866ab24690f1476e7b98b722fac1 lxc.initd"
sha512sums="cafa4fbe4fe23b8c0b98007a79f20899c4a3b98c51d797b9c16e38dfec1eee5a513b58621308fe6551707d38028d9e5bd78ade8822691ffe4a7a369b10a48dda lxc-1.1.3.tar.gz
6c4dc0a53ba8947a37faa14bb81a7bbed7a88fb118513842f5a5a37e9d23fbd2bee4f8f513d3f686a34d373869008ee5ccbd4b9e20615d4b9e4b8139119adce3 0001-Clone-bridge-interface-MTU-setting.patch
e2ffcbf55447291a8434a4f37255c3a6a119bc4116c75d205006aa2b070bf6be28535cf6107bead14bbf64bf9fa415346ab544bd1c15e1add7d1c6380e6b2def version.patch
+d8b0a7827a96a4d5c10e7ebf7ca65f415f6895d3b6371cfc1edda2a9a07ea99b9d3db5d8e5cc542ad88b25e717534755faa9938901183a90c7587d83d3fa81aa CVE-2015-1335.patch
4ff815d354f022854535a0069033f314263623ead48220d732683d5a268f14f221cbfe1aa9515adf1c2e47de71f1b1f1dda374a1f5f15d6f7025d2de91143d9a lxc.initd"
diff --git a/main/lxc/CVE-2015-1335.patch b/main/lxc/CVE-2015-1335.patch
new file mode 100644
index 0000000000..719816e48a
--- /dev/null
+++ b/main/lxc/CVE-2015-1335.patch
@@ -0,0 +1,636 @@
+From 1f9cc4943b640d9355709432a705e5fa6e9ad4df Mon Sep 17 00:00:00 2001
+From: Serge Hallyn <serge.hallyn@ubuntu.com>
+Date: Mon, 31 Aug 2015 12:57:20 -0500
+Subject: [PATCH 1/1] Protect container mounts against symlinks
+
+When a container starts up, lxc sets up the container's inital fstree
+by doing a bunch of mounting, guided by the container configuration
+file. The container config is owned by the admin or user on the host,
+so we do not try to guard against bad entries. However, since the
+mount target is in the container, it's possible that the container admin
+could divert the mount with symbolic links. This could bypass proper
+container startup (i.e. confinement of a root-owned container by the
+restrictive apparmor policy, by diverting the required write to
+/proc/self/attr/current), or bypass the (path-based) apparmor policy
+by diverting, say, /proc to /mnt in the container.
+
+To prevent this,
+
+1. do not allow mounts to paths containing symbolic links
+
+2. do not allow bind mounts from relative paths containing symbolic
+links.
+
+Details:
+
+Define safe_mount which ensures that the container has not inserted any
+symbolic links into any mount targets for mounts to be done during
+container setup.
+
+The host's mount path may contain symbolic links. As it is under the
+control of the administrator, that's ok. So safe_mount begins the check
+for symbolic links after the rootfs->mount, by opening that directory.
+
+It opens each directory along the path using openat() relative to the
+parent directory using O_NOFOLLOW. When the target is reached, it
+mounts onto /proc/self/fd/<targetfd>.
+
+Use safe_mount() in mount_entry(), when mounting container proc,
+and when needed. In particular, safe_mount() need not be used in
+any case where:
+
+1. the mount is done in the container's namespace
+2. the mount is for the container's rootfs
+3. the mount is relative to a tmpfs or proc/sysfs which we have
+ just safe_mount()ed ourselves
+
+Since we were using proc/net as a temporary placeholder for /proc/sys/net
+during container startup, and proc/net is a symbolic link, use proc/tty
+instead.
+
+Update the lxc.container.conf manpage with details about the new
+restrictions.
+
+Finally, add a testcase to test some symbolic link possibilities.
+
+Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
+---
+ doc/lxc.container.conf.sgml.in | 12 +++
+ src/lxc/cgfs.c | 5 +-
+ src/lxc/cgmanager.c | 4 +-
+ src/lxc/conf.c | 29 ++---
+ src/lxc/utils.c | 235 ++++++++++++++++++++++++++++++++++++++++-
+ src/lxc/utils.h | 2 +
+ src/tests/Makefile.am | 2 +
+ src/tests/lxc-test-symlink | 88 +++++++++++++++
+ 8 files changed, 359 insertions(+), 18 deletions(-)
+ create mode 100644 src/tests/lxc-test-symlink
+
+diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
+index 50c6a2a..0a1ec5f 100644
+--- a/doc/lxc.container.conf.sgml.in
++++ b/doc/lxc.container.conf.sgml.in
+@@ -699,6 +699,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ container. This is useful to mount /etc, /var or /home for
+ examples.
+ </para>
++ <para>
++ NOTE - LXC will generally ensure that mount targets and relative
++ bind-mount sources are properly confined under the container
++ root, to avoid attacks involving over-mounting host directories
++ and files. (Symbolic links in absolute mount sources are ignored)
++ However, if the container configuration first mounts a directory which
++ is under the control of the container user, such as /home/joe, into
++ the container at some <filename>path</filename>, and then mounts
++ under <filename>path</filename>, then a TOCTTOU attack would be
++ possible where the container user modifies a symbolic link under
++ his home directory at just the right time.
++ </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+diff --git a/src/lxc/cgfs.c b/src/lxc/cgfs.c
+index fcb3cde..df2e6b2 100644
+--- a/src/lxc/cgfs.c
++++ b/src/lxc/cgfs.c
+@@ -1363,7 +1363,10 @@ static bool cgroupfs_mount_cgroup(void *hdata, const char *root, int type)
+ if (!path)
+ return false;
+ snprintf(path, bufsz, "%s/sys/fs/cgroup", root);
+- r = mount("cgroup_root", path, "tmpfs", MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME, "size=10240k,mode=755");
++ r = safe_mount("cgroup_root", path, "tmpfs",
++ MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME,
++ "size=10240k,mode=755",
++ root);
+ if (r < 0) {
+ SYSERROR("could not mount tmpfs to /sys/fs/cgroup in the container");
+ return false;
+diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c
+index c143bea..779a1d8 100644
+--- a/src/lxc/cgmanager.c
++++ b/src/lxc/cgmanager.c
+@@ -1477,7 +1477,7 @@ static bool cgm_bind_dir(const char *root, const char *dirname)
+ }
+
+ /* mount a tmpfs there so we can create subdirs */
+- if (mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755")) {
++ if (safe_mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755", root)) {
+ SYSERROR("Failed to mount tmpfs at %s", cgpath);
+ return false;
+ }
+@@ -1488,7 +1488,7 @@ static bool cgm_bind_dir(const char *root, const char *dirname)
+ return false;
+ }
+
+- if (mount(dirname, cgpath, "none", MS_BIND, 0)) {
++ if (safe_mount(dirname, cgpath, "none", MS_BIND, 0, root)) {
+ SYSERROR("Failed to bind mount %s to %s", dirname, cgpath);
+ return false;
+ }
+diff --git a/src/lxc/conf.c b/src/lxc/conf.c
+index d37112b..8cff919 100644
+--- a/src/lxc/conf.c
++++ b/src/lxc/conf.c
+@@ -763,10 +763,11 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_ha
+ * 2.6.32...
+ */
+ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "proc", "%r/proc", "proc", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL },
+- { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/sys/net", "%r/proc/net", NULL, MS_BIND, NULL },
++ /* proc/tty is used as a temporary placeholder for proc/sys/net which we'll move back in a few steps */
++ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/sys/net", "%r/proc/tty", NULL, MS_BIND, NULL },
+ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/sys", "%r/proc/sys", NULL, MS_BIND, NULL },
+ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, NULL, "%r/proc/sys", NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL },
+- { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/net", "%r/proc/sys/net", NULL, MS_MOVE, NULL },
++ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/tty", "%r/proc/sys/net", NULL, MS_MOVE, NULL },
+ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, "%r/proc/sysrq-trigger", "%r/proc/sysrq-trigger", NULL, MS_BIND, NULL },
+ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_MIXED, NULL, "%r/proc/sysrq-trigger", NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL },
+ { LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_RW, "proc", "%r/proc", "proc", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL },
+@@ -809,7 +810,7 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_ha
+ }
+ mflags = add_required_remount_flags(source, destination,
+ default_mounts[i].flags);
+- r = mount(source, destination, default_mounts[i].fstype, mflags, default_mounts[i].options);
++ r = safe_mount(source, destination, default_mounts[i].fstype, mflags, default_mounts[i].options, conf->rootfs.path ? conf->rootfs.mount : NULL);
+ saved_errno = errno;
+ if (r < 0 && errno == ENOENT) {
+ INFO("Mount source or target for %s on %s doesn't exist. Skipping.", source, destination);
+@@ -1161,7 +1162,7 @@ static int mount_autodev(const char *name, char *root, const char *lxcpath)
+ return 0;
+ }
+
+- if (mount("none", path, "tmpfs", 0, "size=100000,mode=755")) {
++ if (safe_mount("none", path, "tmpfs", 0, "size=100000,mode=755", root)) {
+ SYSERROR("Failed mounting tmpfs onto %s\n", path);
+ return false;
+ }
+@@ -1246,7 +1247,7 @@ static int fill_autodev(const char *root)
+ return -1;
+ }
+ fclose(pathfile);
+- if (mount(hostpath, path, 0, MS_BIND, NULL) != 0) {
++ if (safe_mount(hostpath, path, 0, MS_BIND, NULL, root) != 0) {
+ SYSERROR("Failed bind mounting device %s from host into container",
+ d->name);
+ return -1;
+@@ -1499,7 +1500,7 @@ static int setup_dev_console(const struct lxc_rootfs *rootfs,
+ return -1;
+ }
+
+- if (mount(console->name, path, "none", MS_BIND, 0)) {
++ if (safe_mount(console->name, path, "none", MS_BIND, 0, rootfs->mount)) {
+ ERROR("failed to mount '%s' on '%s'", console->name, path);
+ return -1;
+ }
+@@ -1554,7 +1555,7 @@ static int setup_ttydir_console(const struct lxc_rootfs *rootfs,
+ return 0;
+ }
+
+- if (mount(console->name, lxcpath, "none", MS_BIND, 0)) {
++ if (safe_mount(console->name, lxcpath, "none", MS_BIND, 0, rootfs->mount)) {
+ ERROR("failed to mount '%s' on '%s'", console->name, lxcpath);
+ return -1;
+ }
+@@ -1704,13 +1705,13 @@ static char *get_field(char *src, int nfields)
+
+ static int mount_entry(const char *fsname, const char *target,
+ const char *fstype, unsigned long mountflags,
+- const char *data, int optional)
++ const char *data, int optional, const char *rootfs)
+ {
+ #ifdef HAVE_STATVFS
+ struct statvfs sb;
+ #endif
+
+- if (mount(fsname, target, fstype, mountflags & ~MS_REMOUNT, data)) {
++ if (safe_mount(fsname, target, fstype, mountflags & ~MS_REMOUNT, data, rootfs)) {
+ if (optional) {
+ INFO("failed to mount '%s' on '%s' (optional): %s", fsname,
+ target, strerror(errno));
+@@ -1757,7 +1758,7 @@ static int mount_entry(const char *fsname, const char *target,
+ #endif
+
+ if (mount(fsname, target, fstype,
+- mountflags | MS_REMOUNT, data)) {
++ mountflags | MS_REMOUNT, data) < 0) {
+ if (optional) {
+ INFO("failed to mount '%s' on '%s' (optional): %s",
+ fsname, target, strerror(errno));
+@@ -1843,7 +1844,7 @@ static inline int mount_entry_on_systemfs(struct mntent *mntent)
+ }
+
+ ret = mount_entry(mntent->mnt_fsname, mntent->mnt_dir,
+- mntent->mnt_type, mntflags, mntdata, optional);
++ mntent->mnt_type, mntflags, mntdata, optional, NULL);
+
+ free(pathdirname);
+ free(mntdata);
+@@ -1930,7 +1931,7 @@ skipabs:
+ }
+
+ ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type,
+- mntflags, mntdata, optional);
++ mntflags, mntdata, optional, rootfs->mount);
+
+ free(mntdata);
+
+@@ -1986,7 +1987,7 @@ static int mount_entry_on_relative_rootfs(struct mntent *mntent,
+ }
+
+ ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type,
+- mntflags, mntdata, optional);
++ mntflags, mntdata, optional, rootfs);
+
+ free(pathdirname);
+ free(mntdata);
+@@ -3646,7 +3647,7 @@ void lxc_execute_bind_init(struct lxc_conf *conf)
+ fclose(pathfile);
+ }
+
+- ret = mount(path, destpath, "none", MS_BIND, NULL);
++ ret = safe_mount(path, destpath, "none", MS_BIND, NULL, conf->rootfs.mount);
+ if (ret < 0)
+ SYSERROR("Failed to bind lxc.init.static into container");
+ INFO("lxc.init.static bound into container at %s", path);
+diff --git a/src/lxc/utils.c b/src/lxc/utils.c
+index 7ced314..70d12d5 100644
+--- a/src/lxc/utils.c
++++ b/src/lxc/utils.c
+@@ -1403,6 +1403,239 @@ int setproctitle(char *title)
+ }
+
+ /*
++ * @path: a pathname where / replaced with '\0'.
++ * @offsetp: pointer to int showing which path segment was last seen.
++ * Updated on return to reflect the next segment.
++ * @fulllen: full original path length.
++ * Returns a pointer to the next path segment, or NULL if done.
++ */
++static char *get_nextpath(char *path, int *offsetp, int fulllen)
++{
++ int offset = *offsetp;
++
++ if (offset >= fulllen)
++ return NULL;
++
++ while (path[offset] != '\0' && offset < fulllen)
++ offset++;
++ while (path[offset] == '\0' && offset < fulllen)
++ offset++;
++
++ *offsetp = offset;
++ return (offset < fulllen) ? &path[offset] : NULL;
++}
++
++/*
++ * Check that @subdir is a subdir of @dir. @len is the length of
++ * @dir (to avoid having to recalculate it).
++ */
++static bool is_subdir(const char *subdir, const char *dir, size_t len)
++{
++ size_t subdirlen = strlen(subdir);
++
++ if (subdirlen < len)
++ return false;
++ if (strncmp(subdir, dir, len) != 0)
++ return false;
++ if (dir[len-1] == '/')
++ return true;
++ if (subdir[len] == '/' || subdirlen == len)
++ return true;
++ return false;
++}
++
++/*
++ * Check if the open fd is a symlink. Return -ELOOP if it is. Return
++ * -ENOENT if we couldn't fstat. Return 0 if the fd is ok.
++ */
++static int check_symlink(int fd)
++{
++ struct stat sb;
++ int ret = fstat(fd, &sb);
++ if (ret < 0)
++ return -ENOENT;
++ if (S_ISLNK(sb.st_mode))
++ return -ELOOP;
++ return 0;
++}
++
++/*
++ * Open a file or directory, provided that it contains no symlinks.
++ *
++ * CAVEAT: This function must not be used for other purposes than container
++ * setup before executing the container's init
++ */
++static int open_if_safe(int dirfd, const char *nextpath)
++{
++ int newfd = openat(dirfd, nextpath, O_RDONLY | O_NOFOLLOW);
++ if (newfd >= 0) // was not a symlink, all good
++ return newfd;
++
++ if (errno == ELOOP)
++ return newfd;
++
++ if (errno == EPERM || errno == EACCES) {
++ /* we're not root (cause we got EPERM) so
++ try opening with O_PATH */
++ newfd = openat(dirfd, nextpath, O_PATH | O_NOFOLLOW);
++ if (newfd >= 0) {
++ /* O_PATH will return an fd for symlinks. We know
++ * nextpath wasn't a symlink at last openat, so if fd
++ * is now a link, then something * fishy is going on
++ */
++ int ret = check_symlink(newfd);
++ if (ret < 0) {
++ close(newfd);
++ newfd = ret;
++ }
++ }
++ }
++
++ return newfd;
++}
++
++/*
++ * Open a path intending for mounting, ensuring that the final path
++ * is inside the container's rootfs.
++ *
++ * CAVEAT: This function must not be used for other purposes than container
++ * setup before executing the container's init
++ *
++ * @target: path to be opened
++ * @prefix_skip: a part of @target in which to ignore symbolic links. This
++ * would be the container's rootfs.
++ *
++ * Return an open fd for the path, or <0 on error.
++ */
++static int open_without_symlink(const char *target, const char *prefix_skip)
++{
++ int curlen = 0, dirfd, fulllen, i;
++ char *dup = NULL;
++
++ fulllen = strlen(target);
++
++ /* make sure prefix-skip makes sense */
++ if (prefix_skip) {
++ curlen = strlen(prefix_skip);
++ if (!is_subdir(target, prefix_skip, curlen)) {
++ ERROR("WHOA there - target '%s' didn't start with prefix '%s'",
++ target, prefix_skip);
++ return -EINVAL;
++ }
++ /*
++ * get_nextpath() expects the curlen argument to be
++ * on a (turned into \0) / or before it, so decrement
++ * curlen to make sure that happens
++ */
++ if (curlen)
++ curlen--;
++ } else {
++ prefix_skip = "/";
++ curlen = 0;
++ }
++
++ /* Make a copy of target which we can hack up, and tokenize it */
++ if ((dup = strdup(target)) == NULL) {
++ SYSERROR("Out of memory checking for symbolic link");
++ return -ENOMEM;
++ }
++ for (i = 0; i < fulllen; i++) {
++ if (dup[i] == '/')
++ dup[i] = '\0';
++ }
++
++ dirfd = open(prefix_skip, O_RDONLY);
++ if (dirfd < 0)
++ goto out;
++ while (1) {
++ int newfd, saved_errno;
++ char *nextpath;
++
++ if ((nextpath = get_nextpath(dup, &curlen, fulllen)) == NULL)
++ goto out;
++ newfd = open_if_safe(dirfd, nextpath);
++ saved_errno = errno;
++ close(dirfd);
++ dirfd = newfd;
++ if (newfd < 0) {
++ errno = saved_errno;
++ if (errno == ELOOP)
++ SYSERROR("%s in %s was a symbolic link!", nextpath, target);
++ else
++ SYSERROR("Error examining %s in %s", nextpath, target);
++ goto out;
++ }
++ }
++
++out:
++ free(dup);
++ return dirfd;
++}
++
++/*
++ * Safely mount a path into a container, ensuring that the mount target
++ * is under the container's @rootfs. (If @rootfs is NULL, then the container
++ * uses the host's /)
++ *
++ * CAVEAT: This function must not be used for other purposes than container
++ * setup before executing the container's init
++ */
++int safe_mount(const char *src, const char *dest, const char *fstype,
++ unsigned long flags, const void *data, const char *rootfs)
++{
++ int srcfd = -1, destfd, ret, saved_errno;
++ char srcbuf[50], destbuf[50]; // only needs enough for /proc/self/fd/<fd>
++ const char *mntsrc = src;
++
++ if (!rootfs)
++ rootfs = "";
++
++ /* todo - allow symlinks for relative paths if 'allowsymlinks' option is passed */
++ if (flags & MS_BIND && src && src[0] != '/') {
++ INFO("this is a relative bind mount");
++ srcfd = open_without_symlink(src, NULL);
++ if (srcfd < 0)
++ return srcfd;
++ ret = snprintf(srcbuf, 50, "/proc/self/fd/%d", srcfd);
++ if (ret < 0 || ret > 50) {
++ close(srcfd);
++ ERROR("Out of memory");
++ return -EINVAL;
++ }
++ mntsrc = srcbuf;
++ }
++
++ destfd = open_without_symlink(dest, rootfs);
++ if (destfd < 0) {
++ if (srcfd != -1)
++ close(srcfd);
++ return destfd;
++ }
++
++ ret = snprintf(destbuf, 50, "/proc/self/fd/%d", destfd);
++ if (ret < 0 || ret > 50) {
++ if (srcfd != -1)
++ close(srcfd);
++ close(destfd);
++ ERROR("Out of memory");
++ return -EINVAL;
++ }
++
++ ret = mount(mntsrc, destbuf, fstype, flags, data);
++ saved_errno = errno;
++ if (srcfd != -1)
++ close(srcfd);
++ close(destfd);
++ if (ret < 0) {
++ errno = saved_errno;
++ SYSERROR("Failed to mount %s onto %s", src, dest);
++ return ret;
++ }
++
++ return 0;
++}
++
++/*
+ * Mount a proc under @rootfs if proc self points to a pid other than
+ * my own. This is needed to have a known-good proc mount for setting
+ * up LSMs both at container startup and attach.
+@@ -1440,7 +1673,7 @@ int mount_proc_if_needed(const char *rootfs)
+ return 0;
+
+ domount:
+- if (mount("proc", path, "proc", 0, NULL))
++ if (safe_mount("proc", path, "proc", 0, NULL, rootfs) < 0)
+ return -1;
+ INFO("Mounted /proc in container for security transition");
+ return 1;
+diff --git a/src/lxc/utils.h b/src/lxc/utils.h
+index ee12dde..059026f 100644
+--- a/src/lxc/utils.h
++++ b/src/lxc/utils.h
+@@ -279,6 +279,8 @@ bool switch_to_ns(pid_t pid, const char *ns);
+ int is_dir(const char *path);
+ char *get_template_path(const char *t);
+ int setproctitle(char *title);
++int safe_mount(const char *src, const char *dest, const char *fstype,
++ unsigned long flags, const void *data, const char *rootfs);
+ int mount_proc_if_needed(const char *rootfs);
+ int null_stdfds(void);
+ #endif /* __LXC_UTILS_H */
+diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
+index 461d869..8af9baa 100644
+--- a/src/tests/Makefile.am
++++ b/src/tests/Makefile.am
+@@ -54,6 +54,7 @@ if DISTRO_UBUNTU
+ bin_SCRIPTS += \
+ lxc-test-apparmor-mount \
+ lxc-test-checkpoint-restore \
++ lxc-test-symlink \
+ lxc-test-ubuntu \
+ lxc-test-unpriv \
+ lxc-test-usernic
+@@ -80,6 +81,7 @@ EXTRA_DIST = \
+ lxc-test-checkpoint-restore \
+ lxc-test-cloneconfig \
+ lxc-test-createconfig \
++ lxc-test-symlink \
+ lxc-test-ubuntu \
+ lxc-test-unpriv \
+ may_control.c \
+diff --git a/src/tests/lxc-test-symlink b/src/tests/lxc-test-symlink
+new file mode 100644
+index 0000000..37320f0
+--- /dev/null
++++ b/src/tests/lxc-test-symlink
+@@ -0,0 +1,88 @@
++#!/bin/bash
++
++set -ex
++
++# lxc: linux Container library
++
++# Authors:
++# Serge Hallyn <serge.hallyn@ubuntu.com>
++#
++# This is a regression test for symbolic links
++
++dirname=`mktemp -d`
++fname=`mktemp`
++fname2=`mktemp`
++
++lxcpath=/var/lib/lxcsym1
++
++cleanup() {
++ lxc-destroy -P $lxcpath -f -n symtest1 || true
++ rm -f $lxcpath
++ rmdir $dirname || true
++ rm -f $fname || true
++ rm -f $fname2 || true
++}
++
++trap cleanup EXIT SIGHUP SIGINT SIGTERM
++
++testrun() {
++ expected=$1
++ run=$2
++ pass="pass"
++ lxc-start -P $lxcpath -n symtest1 -l trace -o $lxcpath/log || pass="fail"
++ [ $pass = "pass" ] && lxc-wait -P $lxcpath -n symtest1 -t 10 -s RUNNING || pass="fail"
++ if [ "$pass" != "$expected" ]; then
++ echo "Test $run: expected $expected but container did not. Start log:"
++ cat $lxcpath/log
++ echo "FAIL: Test $run: expected $expected but container did not."
++ false
++ fi
++ lxc-stop -P $lxcpath -n symtest1 -k || true
++}
++
++# make lxcpath a symlink - this should NOT cause failure
++ln -s /var/lib/lxc $lxcpath
++
++lxc-destroy -P $lxcpath -f -n symtest1 || true
++lxc-create -P $lxcpath -t busybox -n symtest1
++
++cat >> /var/lib/lxc/symtest1/config << EOF
++lxc.mount.entry = $dirname opt/xxx/dir none bind,create=dir
++lxc.mount.entry = $fname opt/xxx/file none bind,create=file
++lxc.mount.entry = $fname2 opt/xxx/file2 none bind
++EOF
++
++# Regular - should succeed
++mkdir -p /var/lib/lxc/symtest1/rootfs/opt/xxx
++touch /var/lib/lxc/symtest1/rootfs/opt/xxx/file2
++testrun pass 1
++
++# symlink - should fail
++rm -rf /var/lib/lxc/symtest1/rootfs/opt/xxx
++mkdir -p /var/lib/lxc/symtest1/rootfs/opt/xxx2
++ln -s /var/lib/lxc/symtest1/rootfs/opt/xxx2 /var/lib/lxc/symtest1/rootfs/opt/xxx
++touch /var/lib/lxc/symtest1/rootfs/opt/xxx/file2
++testrun fail 2
++
++# final final symlink - should fail
++rm -rf $lxcpath/symtest1/rootfs/opt/xxx
++mkdir -p $lxcpath/symtest1/rootfs/opt/xxx
++mkdir -p $lxcpath/symtest1/rootfs/opt/xxx/dir
++touch $lxcpath/symtest1/rootfs/opt/xxx/file
++touch $lxcpath/symtest1/rootfs/opt/xxx/file2src
++ln -s $lxcpath/symtest1/rootfs/opt/xxx/file2src $lxcpath/symtest1/rootfs/opt/xxx/file2
++testrun fail 3
++
++# Ideally we'd also try a loop device, but that won't work in nested containers
++# anyway - TODO
++
++# what about /proc itself
++
++rm -rf $lxcpath/symtest1/rootfs/opt/xxx
++mkdir -p $lxcpath/symtest1/rootfs/opt/xxx
++touch $lxcpath/symtest1/rootfs/opt/xxx/file2
++mv $lxcpath/symtest1/rootfs/proc $lxcpath/symtest1/rootfs/proc1
++ln -s $lxcpath/symtest1/rootfs/proc1 $lxcpath/symtest1/rootfs/proc
++testrun fail 4
++
++echo "all tests passed"
+--
+2.5.0
+