[V2] rexec: handle legacy kernels

parent fed8112d
...@@ -31,7 +31,9 @@ ...@@ -31,7 +31,9 @@
#include "config.h" #include "config.h"
#include "file_utils.h" #include "file_utils.h"
#include "macro.h" #include "macro.h"
#include "memory_utils.h"
#include "string_utils.h" #include "string_utils.h"
#include "utils.h"
int lxc_write_to_file(const char *filename, const void *buf, size_t count, int lxc_write_to_file(const char *filename, const void *buf, size_t count,
bool add_newline, mode_t mode) bool add_newline, mode_t mode)
...@@ -218,7 +220,8 @@ int lxc_count_file_lines(const char *fn) ...@@ -218,7 +220,8 @@ int lxc_count_file_lines(const char *fn)
int lxc_make_tmpfile(char *template, bool rm) int lxc_make_tmpfile(char *template, bool rm)
{ {
int fd, ret; __do_close_prot_errno int fd = -EBADF;
int ret;
mode_t msk; mode_t msk;
msk = umask(0022); msk = umask(0022);
...@@ -227,16 +230,17 @@ int lxc_make_tmpfile(char *template, bool rm) ...@@ -227,16 +230,17 @@ int lxc_make_tmpfile(char *template, bool rm)
if (fd < 0) if (fd < 0)
return -1; return -1;
if (lxc_set_cloexec(fd))
return -1;
if (!rm) if (!rm)
return fd; return steal_fd(fd);
ret = unlink(template); ret = unlink(template);
if (ret < 0) { if (ret < 0)
close(fd);
return -1; return -1;
}
return fd; return steal_fd(fd);
} }
bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val) bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val)
...@@ -366,3 +370,33 @@ on_error: ...@@ -366,3 +370,33 @@ on_error:
return NULL; return NULL;
} }
int fd_to_fd(int from, int to)
{
for (;;) {
uint8_t buf[PATH_MAX];
uint8_t *p = buf;
ssize_t bytes_to_write;
ssize_t bytes_read;
bytes_read = lxc_read_nointr(from, buf, sizeof buf);
if (bytes_read < 0)
return -1;
if (bytes_read == 0)
break;
bytes_to_write = (size_t)bytes_read;
do {
ssize_t bytes_written;
bytes_written = lxc_write_nointr(to, p, bytes_to_write);
if (bytes_written < 0)
return -1;
bytes_to_write -= bytes_written;
p += bytes_written;
} while (bytes_to_write > 0);
}
return 0;
}
...@@ -57,5 +57,6 @@ extern FILE *fopen_cloexec(const char *path, const char *mode); ...@@ -57,5 +57,6 @@ 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, extern ssize_t lxc_sendfile_nointr(int out_fd, int in_fd, off_t *offset,
size_t count); size_t count);
extern char *file_to_buf(char *path, size_t *length); extern char *file_to_buf(char *path, size_t *length);
extern int fd_to_fd(int from, int to);
#endif /* __LXC_FILE_UTILS_H */ #endif /* __LXC_FILE_UTILS_H */
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include "macro.h"
static inline void __auto_free__(void *p) static inline void __auto_free__(void *p)
{ {
free(*(void **)p); free(*(void **)p);
...@@ -45,13 +47,17 @@ static inline void __auto_closedir__(DIR **d) ...@@ -45,13 +47,17 @@ static inline void __auto_closedir__(DIR **d)
closedir(*d); closedir(*d);
} }
#define close_prot_errno_disarm(fd) \
if (fd >= 0) { \
int _e_ = errno; \
close(fd); \
errno = _e_; \
fd = -EBADF; \
}
static inline void __auto_close__(int *fd) static inline void __auto_close__(int *fd)
{ {
if (*fd >= 0) { close_prot_errno_disarm(*fd);
int e = errno;
close(*fd);
errno = e;
}
} }
#define __do_close_prot_errno __attribute__((__cleanup__(__auto_close__))) #define __do_close_prot_errno __attribute__((__cleanup__(__auto_close__)))
......
...@@ -84,42 +84,95 @@ static int parse_argv(char ***argv) ...@@ -84,42 +84,95 @@ static int parse_argv(char ***argv)
static int is_memfd(void) static int is_memfd(void)
{ {
__do_close_prot_errno int fd = -EBADF; __do_close_prot_errno int fd = -EBADF;
int saved_errno, seals; int seals;
fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC); fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
if (fd < 0) if (fd < 0)
return -ENOTRECOVERABLE; return -ENOTRECOVERABLE;
seals = fcntl(fd, F_GET_SEALS); seals = fcntl(fd, F_GET_SEALS);
if (seals < 0) if (seals < 0) {
struct stat s = {0};
if (fstat(fd, &s) == 0)
return (s.st_nlink == 0);
return -EINVAL; return -EINVAL;
}
return seals == LXC_MEMFD_REXEC_SEALS; return seals == LXC_MEMFD_REXEC_SEALS;
} }
static void lxc_rexec_as_memfd(char **argv, char **envp, const char *memfd_name) static void lxc_rexec_as_memfd(char **argv, char **envp, const char *memfd_name)
{ {
__do_close_prot_errno int fd = -EBADF, memfd = -EBADF; __do_close_prot_errno int fd = -EBADF, memfd = -EBADF, tmpfd = -EBADF;
int saved_errno; int ret;
ssize_t bytes_sent;
memfd = memfd_create(memfd_name, MFD_ALLOW_SEALING | MFD_CLOEXEC); memfd = memfd_create(memfd_name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
if (memfd < 0) if (memfd < 0) {
char template[PATH_MAX];
ret = snprintf(template, sizeof(template),
P_tmpdir "/.%s_XXXXXX", memfd_name);
if (ret < 0 || (size_t)ret >= sizeof(template))
return;
tmpfd = lxc_make_tmpfile(template, true);
if (tmpfd < 0)
return;
ret = fchmod(tmpfd, 0700);
if (ret)
return; return;
}
fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC); fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
if (fd < 0) if (fd < 0)
return; return;
/* sendfile() handles up to 2GB. */ /* sendfile() handles up to 2GB. */
bytes_sent = lxc_sendfile_nointr(memfd, fd, NULL, LXC_SENDFILE_MAX); if (memfd >= 0) {
if (bytes_sent < 0) ssize_t bytes_sent = 0;
struct stat st = {0};
ret = fstat(fd, &st);
if (ret)
return; return;
if (fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS)) while (bytes_sent < st.st_size) {
ssize_t sent;
sent = lxc_sendfile_nointr(memfd, fd, NULL,
st.st_size - bytes_sent);
if (sent < 0)
return; return;
bytes_sent += sent;
}
} else if (fd_to_fd(fd, tmpfd)) {
return;
}
close_prot_errno_disarm(fd);
if (memfd >= 0 && fcntl(memfd, F_ADD_SEALS, LXC_MEMFD_REXEC_SEALS))
return;
if (memfd >= 0) {
fexecve(memfd, argv, envp); fexecve(memfd, argv, envp);
} else {
__do_close_prot_errno int execfd = -EBADF;
char procfd[LXC_PROC_PID_FD_LEN];
ret = snprintf(procfd, sizeof(procfd), "/proc/self/fd/%d", tmpfd);
if (ret < 0 || (size_t)ret >= sizeof(procfd))
return;
execfd = open(procfd, O_PATH | O_CLOEXEC);
close_prot_errno_disarm(tmpfd);
if (execfd < 0)
return;
fexecve(execfd, argv, envp);
}
} }
/* /*
......
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