[lxc-devel] [PATCH] [RFC] lxc: don't call pivot_root if / is on a ramfs

Serge Hallyn serge.hallyn at ubuntu.com
Sun Oct 5 03:30:04 UTC 2014


Quoting Andrey Vagin (avagin at openvz.org):
> From: Andrey Vagin <avagin at gmail.com>
> 
> pivot_root can't be called if / is on a ramfs. Currently chroot is
> called before pivot_root. In this case the standard well-known
> 'chroot escape' technique allows to escape a container.
> 
> I think the best way to handle this situation is to make following actions:
> * clean all mounts, which should not be visible in CT
> * move CT's rootfs into /

Hi, Andrey,

You're definately right that there was a problem with the
pivot_root after chroot.  And with a pure c program I was
able to reproduce it on my 14.10 laptop as well, so apparently
as you said chroot does something extra in 14.10.

However, as it turns out the kernel source file
Documentation/filesystems/ramfs-rootfs-initramfs.txt
gives guidance on the right thing to do.  We just need to
chdir(tmproot);  mount --move tmproot /;  chroot /;  and then
we can pivot_root and cannot subsequently escape.

I've tested http://people.canonical.com/~serge/cis3.c (also appended below)
to in fact DTRT.

So I'd prefer to simply add the MS_MOVE before the chroot.

Do you see any problem with doing so?

-serge

=======================================================================================

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sched.h>
#include <sys/mount.h>
#include <linux/sched.h>
#include <linux/fs.h>

int main(int argc, char *argv[])
{
	struct stat sb;

	if (geteuid()) {
		printf("Run me as root\n");
		exit(1);
	}

	if (argc < 2) {
		printf("Usage: %s [rootfs]\n", argv[0]);
		exit(1);
	}

	if (stat(argv[1], &sb) < 0) {
		printf("Cannot access %s\n", argv[1]);
		exit(1);
	}

	if (unshare(CLONE_NEWNS) < 0) {
		perror("unshare");
		exit(1);
	}

	if (mount("", "/mnt", "tmpfs", 0, NULL) < 0) {
		perror("mount tmpfs 3\n");
		exit(1);
	}

	if (mkdir("/mnt/root", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
		perror("mkdir /mnt/root\n");
		exit(1);
	}

	if (mount(argv[1], "/mnt/root", NULL, MS_BIND, 0) < 0) {
		perror("bind mount rootfs");
		exit(1);
	}

	if (chdir("/mnt") < 0) {
		perror("chdir /mnt");
		exit(1);
	}

	if (mount(".", "/", NULL, MS_MOVE, 0) < 0) {
		perror("move mount");
		exit(1);
	}

	if (chroot(".") < 0) {
		perror("chroot /mnt");
		exit(1);
	}

	if (chdir("/root") < 0) {
		perror("chdir /root");
		exit(1);
	}

	if (chdir(".") < 0) {
		perror("chdir");
		exit(1);
	}

	if (pivot_root(".", "mnt") < 0) {
		perror("pivot_root");
		exit(1);
	}


	if (umount2("mnt", MNT_DETACH) < 0) {
		perror("umount2");
		exit(1);
	}

	printf("Running /chrootbreak\n");
	execl("/chrootbreak", "/chrootbreak", NULL);
	printf("Failed to run chrootbreak, running /bin/sh\n");
	execl("/bin/sh", "/bin/sh", NULL);
	exit(1);
}



More information about the lxc-devel mailing list