conf: copy mountinfo for remount_all_slave()

While a container reads mountinfo from proc fs, the mountinfo can be changed by the kernel anytime. This has caused critical issues on some devices. Signed-off-by: Donghwa Jeong dh48.jeong@samsung.com Reported-by: Donghwa Jeong dh48.jeong@samsung.com Signed-off-by: 's avatarChristian Brauner <christian.brauner@ubuntu.com>
parent cf5b450f
...@@ -24,34 +24,35 @@ ...@@ -24,34 +24,35 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include "config.h" #include "config.h"
#include <arpa/inet.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <grp.h> #include <grp.h>
#include <inttypes.h> #include <inttypes.h>
#include <libgen.h> #include <libgen.h>
#include <linux/loop.h>
#include <net/if.h>
#include <netinet/in.h>
#include <pwd.h> #include <pwd.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/loop.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/stat.h> #include <sys/sendfile.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/sysmacros.h> #include <sys/stat.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <time.h>
#include <unistd.h>
/* makedev() */ /* makedev() */
#ifdef MAJOR_IN_MKDEV #ifdef MAJOR_IN_MKDEV
...@@ -3013,38 +3014,117 @@ void tmp_proc_unmount(struct lxc_conf *lxc_conf) ...@@ -3013,38 +3014,117 @@ void tmp_proc_unmount(struct lxc_conf *lxc_conf)
} }
} }
#define LXC_SENDFILE_MAX 0x7ffff000
static ssize_t lxc_sendfile_nointr(int out_fd, int in_fd, off_t *offset,
size_t count)
{
ssize_t ret;
again:
ret = sendfile(out_fd, in_fd, offset, count);
if (ret < 0) {
if (errno == EINTR)
goto again;
return -1;
}
return ret;
}
/* Walk /proc/mounts and change any shared entries to slave. */
void remount_all_slave(void) void remount_all_slave(void)
{ {
/* walk /proc/mounts and change any shared entries to slave */ int memfd, mntinfo_fd, ret;
FILE *f = fopen("/proc/self/mountinfo", "r"); ssize_t copied;
char *line = NULL; FILE *f;
size_t len = 0; size_t len = 0;
char *line = NULL;
mntinfo_fd = open("/proc/self/mountinfo", O_RDONLY | O_CLOEXEC);
if (mntinfo_fd < 0) {
SYSERROR("Failed to open \"/proc/self/mountinfo\"");
return;
}
memfd = memfd_create(".lxc_mountinfo", MFD_CLOEXEC);
if (memfd < 0) {
char template[] = P_tmpdir "/.lxc_mountinfo_XXXXXX";
if (errno != ENOSYS) {
SYSERROR("Failed to create temporary in-memory file");
close(mntinfo_fd);
return;
}
memfd = lxc_make_tmpfile(template, true);
if (memfd < 0) {
close(mntinfo_fd);
WARN("Failed to create temporary file");
return;
}
}
again:
copied = lxc_sendfile_nointr(memfd, mntinfo_fd, NULL, LXC_SENDFILE_MAX);
if (copied < 0) {
if (errno == EINTR)
goto again;
SYSERROR("Failed to copy \"/proc/self/mountinfo\"");
close(mntinfo_fd);
close(memfd);
return;
}
close(mntinfo_fd);
/* After a successful fdopen() memfd will be closed when calling
* fclose(f). Calling close(memfd) afterwards is undefined.
*/
ret = lseek(memfd, 0, SEEK_SET);
if (ret < 0) {
SYSERROR("Failed to reset file descriptor offset");
close(memfd);
return;
}
f = fdopen(memfd, "r");
if (!f) { if (!f) {
SYSERROR("Failed to open /proc/self/mountinfo to mark all shared"); SYSERROR("Failed to open copy of \"/proc/self/mountinfo\" to mark "
ERROR("Continuing container startup..."); "all shared. Continuing");
close(memfd);
return; return;
} }
while (getline(&line, &len, f) != -1) { while (getline(&line, &len, f) != -1) {
char *target, *opts; int ret;
char *opts, *target;
target = get_field(line, 4); target = get_field(line, 4);
if (!target) if (!target)
continue; continue;
opts = get_field(target, 2); opts = get_field(target, 2);
if (!opts) if (!opts)
continue; continue;
null_endofword(opts); null_endofword(opts);
if (!strstr(opts, "shared")) if (!strstr(opts, "shared"))
continue; continue;
null_endofword(target); null_endofword(target);
if (mount(NULL, target, NULL, MS_SLAVE, NULL)) { ret = mount(NULL, target, NULL, MS_SLAVE, NULL);
SYSERROR("Failed to make %s rslave", target); if (ret < 0) {
SYSERROR("Failed to make \"%s\" MS_SLAVE", target);
ERROR("Continuing..."); ERROR("Continuing...");
continue;
} }
TRACE("Remounted \"%s\" as MS_SLAVE", target);
} }
fclose(f); fclose(f);
free(line); free(line);
TRACE("Remounted all mount table entries as MS_SLAVE");
} }
void lxc_execute_bind_init(struct lxc_conf *conf) void lxc_execute_bind_init(struct lxc_conf *conf)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment