Merge pull request #2698 from brauner/2018-10-17/config_parsing

parse: protect against config updates during parse
This commit is contained in:
Wolfgang Bumiller 2018-10-18 19:38:38 +02:00 committed by GitHub
commit b37348d8f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 29 deletions

View File

@ -3344,9 +3344,8 @@ void remount_all_slave(void)
} }
} }
#define __LXC_SENDFILE_MAX 0x7ffff000 /* maximum number of bytes sendfile can handle */
again: 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 (copied < 0) {
if (errno == EINTR) if (errno == EINTR)
goto again; goto again;

View File

@ -25,6 +25,7 @@
#include <linux/magic.h> #include <linux/magic.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/sendfile.h>
#include <sys/types.h> #include <sys/types.h>
#include "config.h" #include "config.h"
@ -313,3 +314,19 @@ FILE *fopen_cloexec(const char *path, const char *mode)
errno = saved_errno; errno = saved_errno;
return ret; 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;
}

View 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 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 bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val);
extern FILE *fopen_cloexec(const char *path, const char *mode); 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 */ #endif /* __LXC_FILE_UTILS_H */

View File

@ -389,4 +389,7 @@ enum {
#define STRLITERALLEN(x) (sizeof(""x"") - 1) #define STRLITERALLEN(x) (sizeof(""x"") - 1)
#define STRARRAYLEN(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 */ #endif /* __LXC_MACRO_H */

View File

@ -30,10 +30,14 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/sendfile.h>
#include "config.h" #include "config.h"
#include "file_utils.h"
#include "log.h" #include "log.h"
#include "macro.h"
#include "parse.h" #include "parse.h"
#include "syscall_wrappers.h"
#include "utils.h" #include "utils.h"
lxc_log_define(parse, lxc); 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 lxc_file_for_each_line_mmap(const char *file, lxc_file_cb callback, void *data)
{ {
int fd, saved_errno; int saved_errno;
char *buf, *line; ssize_t ret, bytes_sent;
struct stat st; char *line;
int ret = 0; int fd = -1, memfd = -1;
char *buf = NULL;
fd = open(file, O_RDONLY | O_CLOEXEC); memfd = memfd_create(".lxc_config_file", MFD_CLOEXEC);
if (fd < 0) { if (memfd < 0) {
SYSERROR("Failed to open config file \"%s\"", file); char template[] = P_tmpdir "/.lxc_config_file_XXXXXX";
return -1;
}
ret = fstat(fd, &st); if (errno != ENOSYS) {
if (ret < 0) { SYSERROR("Failed to create memory file");
SYSERROR("Failed to stat config file \"%s\"", file);
goto on_error_fstat;
}
ret = 0;
if (st.st_size == 0)
goto on_error_fstat;
ret = -1;
buf = lxc_strmmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_POPULATE, fd, 0);
if (buf == MAP_FAILED) {
SYSERROR("Failed to map config file \"%s\"", file);
goto on_error; 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 file \"%s\"", file);
return -1;
}
/* 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 append zero byte");
goto on_error;
}
bytes_sent++;
ret = lseek(memfd, 0, SEEK_SET);
if (ret < 0) {
SYSERROR("Failed to lseek");
goto on_error;
}
ret = -1;
buf = mmap(NULL, bytes_sent, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE, memfd, 0);
if (buf == MAP_FAILED) {
buf = NULL;
SYSERROR("Failed to mmap");
goto on_error;
}
ret = 0;
lxc_iterate_parts(line, buf, "\n\0") { lxc_iterate_parts(line, buf, "\n\0") {
ret = callback(line, data); ret = callback(line, data);
if (ret) { 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) if (ret < 0)
ERROR("Failed to parse config file \"%s\" at " ERROR("Failed to parse config file \"%s\" at "
"line \"%s\"", "line \"%s\"", file, line);
file, line);
break; break;
} }
} }
on_error: on_error:
if (lxc_strmunmap(buf, st.st_size) < 0) { saved_errno = errno;
SYSERROR("Failed to unmap config file \"%s\"", file); if (fd >= 0)
close(fd);
if (memfd >= 0)
close(memfd);
if (buf && munmap(buf, bytes_sent)) {
SYSERROR("Failed to unmap");
if (ret == 0) if (ret == 0)
ret = -1; ret = -1;
} }
on_error_fstat:
saved_errno = errno;
close(fd);
errno = saved_errno; errno = saved_errno;
return ret; return ret;