[lxc-devel] [lxc/master] add openat2() and safe_mount_beneath

brauner on Github lxc-bot at linuxcontainers.org
Sun Aug 9 13:41:28 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200809/fab62a9f/attachment.bin>
-------------- next part --------------
From 66ac24bff1df0b5107fcd7521aaf5cc299d0059d Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 9 Aug 2020 12:48:02 +0200
Subject: [PATCH 1/3] syscalls: add openat2()

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 configure.ac               |  3 +-
 src/lxc/conf.c             |  2 +-
 src/lxc/syscall_numbers.h  | 20 +++++++++++++
 src/lxc/syscall_wrappers.h | 57 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 80 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 70099e3ad0..f58487f5d0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -622,7 +622,8 @@ AC_CHECK_HEADER([ifaddrs.h],
 AC_HEADER_MAJOR
 
 # Check for some syscalls functions
-AC_CHECK_FUNCS([setns pivot_root sethostname unshare rand_r confstr faccessat gettid memfd_create move_mount open_tree execveat clone3 fsopen fspick fsconfig fsmount])
+AC_CHECK_FUNCS([setns pivot_root sethostname unshare rand_r confstr faccessat gettid memfd_create move_mount open_tree execveat clone3 fsopen fspick fsconfig fsmount, openat2])
+AC_CHECK_TYPES([struct open_how], [], [], [[#include <linux/openat2.h>]])
 AC_CHECK_TYPES([struct clone_args], [], [], [[#include <linux/sched.h>]])
 AC_CHECK_MEMBERS([struct clone_args.set_tid],[],[],[[#include <linux/sched.h>]])
 AC_CHECK_MEMBERS([struct clone_args.cgroup],[],[],[[#include <linux/sched.h>]])
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 36a2309a43..2e513d1cc7 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -1077,7 +1077,7 @@ static int mount_autodev(const char *name, const struct lxc_rootfs *rootfs,
 		goto reset_umask;
 	}
 
-	ret = safe_mount("none", path, "tmpfs", 0, mount_options,
+	ret = safe_mount("none", "dev", "tmpfs", 0, mount_options,
 			 rootfs->path ? rootfs->mount : NULL );
 	if (ret < 0) {
 		SYSERROR("Failed to mount tmpfs on \"%s\"", path);
diff --git a/src/lxc/syscall_numbers.h b/src/lxc/syscall_numbers.h
index 72e4ffe460..8305173d6b 100644
--- a/src/lxc/syscall_numbers.h
+++ b/src/lxc/syscall_numbers.h
@@ -640,4 +640,24 @@
 	#endif
 #endif
 
+#ifndef __NR_openat2
+	#if defined __alpha__
+		#define __NR_openat2 547
+	#elif defined _MIPS_SIM
+		#if _MIPS_SIM == _MIPS_SIM_ABI32	/* o32 */
+			#define __NR_openat2 4437
+		#endif
+		#if _MIPS_SIM == _MIPS_SIM_NABI32	/* n32 */
+			#define __NR_openat2 6437
+		#endif
+		#if _MIPS_SIM == _MIPS_SIM_ABI64	/* n64 */
+			#define __NR_openat2 5437
+		#endif
+	#elif defined __ia64__
+		#define __NR_openat2 (437 + 1024)
+	#else
+		#define __NR_openat2 437
+	#endif
+#endif
+
 #endif /* __LXC_SYSCALL_NUMBERS_H */
diff --git a/src/lxc/syscall_wrappers.h b/src/lxc/syscall_wrappers.h
index 6aaa437226..1c1af73f1f 100644
--- a/src/lxc/syscall_wrappers.h
+++ b/src/lxc/syscall_wrappers.h
@@ -203,4 +203,61 @@ static inline int fsmount_lxc(int fs_fd, unsigned int flags, unsigned int attr_f
 extern int fsmount(int fs_fd, unsigned int flags, unsigned int attr_flags);
 #endif
 
+/*
+ * Arguments for how openat2(2) should open the target path. If only @flags and
+ * @mode are non-zero, then openat2(2) operates very similarly to openat(2).
+ *
+ * However, unlike openat(2), unknown or invalid bits in @flags result in
+ * -EINVAL rather than being silently ignored. @mode must be zero unless one of
+ * {O_CREAT, O_TMPFILE} are set.
+ *
+ * @flags: O_* flags.
+ * @mode: O_CREAT/O_TMPFILE file mode.
+ * @resolve: RESOLVE_* flags.
+ */
+struct lxc_open_how {
+	__u64 flags;
+	__u64 mode;
+	__u64 resolve;
+};
+
+/* how->resolve flags for openat2(2). */
+#ifndef RESOLVE_NO_XDEV
+#define RESOLVE_NO_XDEV		0x01 /* Block mount-point crossings
+					(includes bind-mounts). */
+#endif
+
+#ifndef RESOLVE_NO_MAGICLINKS
+#define RESOLVE_NO_MAGICLINKS	0x02 /* Block traversal through procfs-style
+					"magic-links". */
+#endif
+
+#ifndef RESOLVE_NO_SYMLINKS
+#define RESOLVE_NO_SYMLINKS	0x04 /* Block traversal through all symlinks
+					(implies OEXT_NO_MAGICLINKS) */
+#endif
+
+#ifndef RESOLVE_BENEATH
+#define RESOLVE_BENEATH		0x08 /* Block "lexical" trickery like
+					"..", symlinks, and absolute
+					paths which escape the dirfd. */
+#endif
+
+#ifndef RESOLVE_IN_ROOT
+#define RESOLVE_IN_ROOT		0x10 /* Make all jumps to "/" and ".."
+					be scoped inside the dirfd
+					(similar to chroot(2)). */
+#endif
+
+#ifndef HAVE_OPENAT2
+static inline int openat2(int dfd, const char *filename, struct lxc_open_how *how, size_t size)
+{
+	/* When struct open_how is updated we should update lxc as well. */
+#ifndef HAVE_STRUCT_OPEN_HOW
+	BUILD_BUG_ON(sizeof(struct lxc_open_how) != sizeof(struct open_how));
+#endif
+	return syscall(__NR_openat2, dfd, filename, (struct open_how *)how, size);
+}
+#endif /* HAVE_OPENAT2 */
+
 #endif /* __LXC_SYSCALL_WRAPPER_H */

From 839314f139190966ec020808c37e48d582815e91 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 9 Aug 2020 15:24:26 +0200
Subject: [PATCH 2/3] utils: add safe_mount_beneath() based on openat2()

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/utils.c | 38 ++++++++++++++++++++++++++++++++++++++
 src/lxc/utils.h |  3 +++
 2 files changed, 41 insertions(+)

diff --git a/src/lxc/utils.c b/src/lxc/utils.c
index 39de189afc..4efed645ff 100644
--- a/src/lxc/utils.c
+++ b/src/lxc/utils.c
@@ -1079,6 +1079,44 @@ static int open_without_symlink(const char *target, const char *prefix_skip)
 	return dirfd;
 }
 
+int safe_mount_beneath(const char *beneath, const char *src, const char *dst, const char *fstype,
+		       unsigned int flags, const void *data)
+{
+	__do_close int beneath_fd = -EBADF, source_fd = -EBADF, target_fd = -EBADF;
+	const char *path = beneath ? beneath : "/";
+	struct lxc_open_how how = {
+		.flags		= O_RDONLY | O_CLOEXEC | O_PATH,
+		.resolve	= RESOLVE_NO_XDEV | RESOLVE_NO_SYMLINKS | RESOLVE_NO_MAGICLINKS | RESOLVE_BENEATH,
+	};
+	int ret;
+	char src_buf[LXC_PROC_PID_FD_LEN], tgt_buf[LXC_PROC_PID_FD_LEN];
+
+	beneath_fd = openat(-1, beneath, O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_PATH);
+	if (beneath_fd < 0)
+		return log_error_errno(-errno, errno, "Failed to open %s", path);
+
+	if ((flags & MS_BIND) && src && src[0] != '/') {
+		source_fd = openat2(beneath_fd, src, &how, sizeof(how));
+		if (source_fd < 0)
+			return -errno;
+		snprintf(src_buf, sizeof(src_buf), "/proc/self/fd/%d", source_fd);
+	} else {
+		src_buf[0] = '\0';
+	}
+
+	target_fd = openat2(beneath_fd, dst, &how, sizeof(how));
+	if (target_fd < 0)
+		return -errno;
+	snprintf(tgt_buf, sizeof(tgt_buf), "/proc/self/fd/%d", target_fd);
+
+	if (!is_empty_string(src_buf))
+		ret = mount(src_buf, tgt_buf, fstype, flags, data);
+	else
+		ret = mount(src, tgt_buf, fstype, flags, data);
+
+	return ret;
+}
+
 /*
  * 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
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index 3f9a409150..b0de9d9abc 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -246,4 +246,7 @@ static inline bool gid_valid(gid_t gid)
 
 __hidden extern bool multiply_overflow(int64_t base, uint64_t mult, int64_t *res);
 
+extern int safe_mount_beneath(const char *beneath, const char *src, const char *dst,
+			      const char *fstype, unsigned int flags, const void *data);
+
 #endif /* __LXC_UTILS_H */

From 6c2262f903166a7817b45bb84e76b4f6e4fa10be Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 9 Aug 2020 15:37:31 +0200
Subject: [PATCH 3/3] conf: switch mount_autodev() to new safe_mount_beneath()
 helper

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/conf.c | 45 +++++++++++++++++++++++++--------------------
 1 file changed, 25 insertions(+), 20 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 2e513d1cc7..5fcfb0afa3 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -1051,50 +1051,55 @@ static int lxc_create_ttys(struct lxc_handler *handler)
 static int mount_autodev(const char *name, const struct lxc_rootfs *rootfs,
 			 int autodevtmpfssize, const char *lxcpath)
 {
-	__do_free char *path = NULL;
+	__do_close int root_mntpt_fd = -EBADF;
+	const char *path = rootfs->path ? rootfs->mount : NULL;
 	int ret;
-	size_t clen;
 	mode_t cur_mask;
         char mount_options[128];
 
 	INFO("Preparing \"/dev\"");
 
-	/* $(rootfs->mount) + "/dev/pts" + '\0' */
-	clen = (rootfs->path ? strlen(rootfs->mount) : 0) + 9;
-	path = must_realloc(NULL, clen);
 	sprintf(mount_options, "size=%d,mode=755", (autodevtmpfssize != 0) ? autodevtmpfssize : 500000);
 	DEBUG("Using mount options: %s", mount_options);
 
-	ret = snprintf(path, clen, "%s/dev", rootfs->path ? rootfs->mount : "");
-	if (ret < 0 || (size_t)ret >= clen)
-		return -1;
+	root_mntpt_fd = openat(-1, path, O_RDONLY | O_CLOEXEC | O_PATH | O_DIRECTORY);
+	if (root_mntpt_fd < 0)
+		return log_error_errno(-errno, errno, "Failed to open \"%s\"", path);
 
 	cur_mask = umask(S_IXUSR | S_IXGRP | S_IXOTH);
-	ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+	ret = mkdirat(root_mntpt_fd, "dev" , S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
 	if (ret < 0 && errno != EEXIST) {
 		SYSERROR("Failed to create \"/dev\" directory");
 		ret = -errno;
 		goto reset_umask;
 	}
 
-	ret = safe_mount("none", "dev", "tmpfs", 0, mount_options,
-			 rootfs->path ? rootfs->mount : NULL );
+	ret = safe_mount_beneath(path, "none", "dev", "tmpfs", 0, mount_options);
 	if (ret < 0) {
-		SYSERROR("Failed to mount tmpfs on \"%s\"", path);
-		goto reset_umask;
-	}
-	TRACE("Mounted tmpfs on \"%s\"", path);
+		__do_free char *fallback_path = NULL;
 
-	ret = snprintf(path, clen, "%s/dev/pts", rootfs->path ? rootfs->mount : "");
-	if (ret < 0 || (size_t)ret >= clen) {
-		ret = -1;
-		goto reset_umask;
+		if (errno != ENOSYS) {
+			SYSERROR("Failed to mount tmpfs on \"%s\"", path);
+			goto reset_umask;
+		}
+
+		if (path) {
+			fallback_path = must_make_path(path, "/dev", NULL);
+			ret = safe_mount("none", fallback_path, "tmpfs", 0, mount_options, path);
+		} else {
+			ret = safe_mount("none", "dev", "tmpfs", 0, mount_options, NULL);
+		}
+		if (ret < 0) {
+			SYSERROR("Failed to mount tmpfs on \"%s\"", path);
+			goto reset_umask;
+		}
 	}
+	TRACE("Mounted tmpfs on \"%s\"", path);
 
 	/* If we are running on a devtmpfs mapping, dev/pts may already exist.
 	 * If not, then create it and exit if that fails...
 	 */
-	ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+	ret = mkdirat(root_mntpt_fd, "dev/pts", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
 	if (ret < 0 && errno != EEXIST) {
 		SYSERROR("Failed to create directory \"%s\"", path);
 		ret = -errno;


More information about the lxc-devel mailing list