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:
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;

View 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;
}

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 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 */

View 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 */

View File

@ -30,10 +30,14 @@
#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);
if (ret < 0) {
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);
/* 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") {
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;