]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
parse: protect against config updates during parse 2698/head
authorChristian Brauner <christian.brauner@ubuntu.com>
Wed, 17 Oct 2018 16:36:28 +0000 (18:36 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Thu, 18 Oct 2018 15:38:51 +0000 (17:38 +0200)
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/conf.c
src/lxc/file_utils.c
src/lxc/file_utils.h
src/lxc/macro.h
src/lxc/parse.c

index 710625bb0d66a15fafca98ec4c12545fa15a3248..e822e90d30a01f608d72e66c218be6713df4c110 100644 (file)
@@ -3344,9 +3344,8 @@ void remount_all_slave(void)
                }
        }
 
-#define __LXC_SENDFILE_MAX 0x7ffff000 /* maximum number of bytes sendfile can handle */
 again:
-       copied = sendfile(memfd, mntinfo_fd, NULL, __LXC_SENDFILE_MAX);
+       copied = lxc_sendfile_nointr(memfd, mntinfo_fd, NULL, LXC_SENDFILE_MAX);
        if (copied < 0) {
                if (errno == EINTR)
                        goto again;
index 1655e0053c89c04fd45bdf0b92376e7b3152d3f8..485a7843eb69d25f9cf53e1a319748077a17fb73 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/magic.h>
 #include <stdlib.h>
 #include <sys/stat.h>
+#include <sys/sendfile.h>
 #include <sys/types.h>
 
 #include "config.h"
@@ -313,3 +314,19 @@ FILE *fopen_cloexec(const char *path, const char *mode)
        errno = saved_errno;
        return ret;
 }
+
+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;
+}
index 92e29f4e921d1f129814a5b9dab46cfb82480d4d..6361557a0ee66322f56a7d37a224a147890fe418 100644 (file)
@@ -53,5 +53,7 @@ extern bool has_fs_type(const char *path, fs_type_magic magic_val);
 extern bool fhas_fs_type(int fd, fs_type_magic magic_val);
 extern bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val);
 extern FILE *fopen_cloexec(const char *path, const char *mode);
+extern ssize_t lxc_sendfile_nointr(int out_fd, int in_fd, off_t *offset,
+                                  size_t count);
 
 #endif /* __LXC_FILE_UTILS_H */
index 4ce613c52a0acee975034c6bcf8cdc891a1b4243..30aa2a5a59592246f0f107e254ea3b1b0d4e8a61 100644 (file)
@@ -389,4 +389,7 @@ enum {
 #define STRLITERALLEN(x) (sizeof(""x"") - 1)
 #define STRARRAYLEN(x) (sizeof(x) - 1)
 
+/* Maximum number of bytes sendfile() is able to send in one go. */
+#define LXC_SENDFILE_MAX 0x7ffff000
+
 #endif /* __LXC_MACRO_H */
index 2fdb18ec8087aa94352a4660b3a900e9ba903674..ab9aab461f8163d58f3bbabce3dd84c4fd99f497 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/sendfile.h>
 
 #include "config.h"
+#include "file_utils.h"
 #include "log.h"
+#include "macro.h"
 #include "parse.h"
+#include "syscall_wrappers.h"
 #include "utils.h"
 
 lxc_log_define(parse, lxc);
@@ -67,35 +71,67 @@ int lxc_strmunmap(void *addr, size_t length)
 
 int lxc_file_for_each_line_mmap(const char *file, lxc_file_cb callback, void *data)
 {
-       int fd, saved_errno;
-       char *buf, *line;
-       struct stat st;
-       int ret = 0;
+       int saved_errno;
+       ssize_t ret, bytes_sent;
+       char *line;
+       int fd = -1, memfd = -1;
+       char *buf = NULL;
+
+       memfd = memfd_create(".lxc_config_file", MFD_CLOEXEC);
+       if (memfd < 0) {
+               char template[] = P_tmpdir "/.lxc_config_file_XXXXXX";
+
+               if (errno != ENOSYS) {
+                       SYSERROR("Failed to create memory file");
+                       goto on_error;
+               }
+
+               TRACE("Failed to create in-memory file. Falling back to "
+                     "temporary file");
+               memfd = lxc_make_tmpfile(template, true);
+               if (memfd < 0) {
+                       SYSERROR("Failed to create temporary file \"%s\"", template);
+                       goto on_error;
+               }
+       }
 
        fd = open(file, O_RDONLY | O_CLOEXEC);
        if (fd < 0) {
-               SYSERROR("Failed to open config file \"%s\"", file);
+               SYSERROR("Failed to open file \"%s\"", file);
                return -1;
        }
 
-       ret = fstat(fd, &st);
+
+       /* sendfile() handles up to 2GB. No config file should be that big. */
+       bytes_sent = lxc_sendfile_nointr(memfd, fd, NULL, LXC_SENDFILE_MAX);
+       if (bytes_sent < 0) {
+               SYSERROR("Failed to sendfile \"%s\"", file);
+               goto on_error;
+       }
+
+       ret = lxc_write_nointr(memfd, "\0", 1);
        if (ret < 0) {
-               SYSERROR("Failed to stat config file \"%s\"", file);
-               goto on_error_fstat;
+               SYSERROR("Failed to append zero byte");
+               goto on_error;
        }
+       bytes_sent++;
 
-       ret = 0;
-       if (st.st_size == 0)
-               goto on_error_fstat;
+       ret = lseek(memfd, 0, SEEK_SET);
+       if (ret < 0) {
+               SYSERROR("Failed to lseek");
+               goto on_error;
+       }
 
        ret = -1;
-       buf = lxc_strmmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
-                         MAP_PRIVATE | MAP_POPULATE, fd, 0);
+       buf = mmap(NULL, bytes_sent, PROT_READ | PROT_WRITE,
+                  MAP_SHARED | MAP_POPULATE, memfd, 0);
        if (buf == MAP_FAILED) {
-               SYSERROR("Failed to map config file \"%s\"", file);
+               buf = NULL;
+               SYSERROR("Failed to mmap");
                goto on_error;
        }
 
+       ret = 0;
        lxc_iterate_parts(line, buf, "\n\0") {
                ret = callback(line, data);
                if (ret) {
@@ -104,22 +140,22 @@ int lxc_file_for_each_line_mmap(const char *file, lxc_file_cb callback, void *da
                         */
                        if (ret < 0)
                                ERROR("Failed to parse config file \"%s\" at "
-                                     "line \"%s\"",
-                                     file, line);
+                                     "line \"%s\"", file, line);
                        break;
                }
        }
 
 on_error:
-       if (lxc_strmunmap(buf, st.st_size) < 0) {
-               SYSERROR("Failed to unmap config file \"%s\"", file);
+       saved_errno = errno;
+       if (fd >= 0)
+               close(fd);
+       if (memfd >= 0)
+               close(memfd);
+       if (buf && munmap(buf, bytes_sent)) {
+               SYSERROR("Failed to unmap");
                if (ret == 0)
                        ret = -1;
        }
-
-on_error_fstat:
-       saved_errno = errno;
-       close(fd);
        errno = saved_errno;
 
        return ret;