Unverified Commit c51c7570 by Stéphane Graber Committed by GitHub

Merge pull request #3340 from brauner/2020-03-30/fixes

cgroups: handle older kernels (e.g. v4.9)
parents 4446e0fa 7d849163
......@@ -30,6 +30,7 @@
#include <sys/types.h>
#include <unistd.h>
#include "af_unix.h"
#include "caps.h"
#include "cgroup.h"
#include "cgroup2_devices.h"
......@@ -2123,20 +2124,96 @@ static int cgroup_attach_leaf(const struct lxc_conf *conf, int unified_fd, pid_t
return log_error_errno(-1, errno, "Failed to attach to unified cgroup");
}
static int cgroup_attach_create_leaf(const struct lxc_conf *conf,
int unified_fd, int *sk_fd)
{
__do_close int sk = *sk_fd, target_fd0 = -EBADF, target_fd1 = -EBADF;
int target_fds[2];
ssize_t ret;
/* Create leaf cgroup. */
ret = mkdirat(unified_fd, ".lxc", 0755);
if (ret < 0 && errno != EEXIST)
return log_error_errno(-1, errno, "Failed to create leaf cgroup \".lxc\"");
target_fd0 = openat(unified_fd, ".lxc/cgroup.procs", O_WRONLY | O_CLOEXEC | O_NOFOLLOW);
if (target_fd0 < 0)
return log_error_errno(-errno, errno, "Failed to open \".lxc/cgroup.procs\"");
target_fds[0] = target_fd0;
target_fd1 = openat(unified_fd, "cgroup.procs", O_WRONLY | O_CLOEXEC | O_NOFOLLOW);
if (target_fd1 < 0)
return log_error_errno(-errno, errno, "Failed to open \".lxc/cgroup.procs\"");
target_fds[1] = target_fd1;
ret = lxc_abstract_unix_send_fds(sk, target_fds, 2, NULL, 0);
if (ret <= 0)
return log_error_errno(-errno, errno, "Failed to send \".lxc/cgroup.procs\" fds %d and %d",
target_fd0, target_fd1);
return log_debug(0, "Sent target cgroup fds %d and %d", target_fd0, target_fd1);
}
static int cgroup_attach_move_into_leaf(const struct lxc_conf *conf,
int *sk_fd, pid_t pid)
{
__do_close int sk = *sk_fd, target_fd0 = -EBADF, target_fd1 = -EBADF;
int target_fds[2];
char pidstr[INTTYPE_TO_STRLEN(int64_t) + 1];
size_t pidstr_len;
ssize_t ret;
ret = lxc_abstract_unix_recv_fds(sk, target_fds, 2, NULL, 0);
if (ret <= 0)
return log_error_errno(-1, errno, "Failed to receive target cgroup fd");
target_fd0 = target_fds[0];
target_fd1 = target_fds[1];
pidstr_len = sprintf(pidstr, INT64_FMT, (int64_t)pid);
ret = lxc_write_nointr(target_fd0, pidstr, pidstr_len);
if (ret > 0 && ret == pidstr_len)
return log_debug(0, "Moved process into target cgroup via fd %d", target_fd0);
ret = lxc_write_nointr(target_fd1, pidstr, pidstr_len);
if (ret > 0 && ret == pidstr_len)
return log_debug(0, "Moved process into target cgroup via fd %d", target_fd1);
return log_debug_errno(-1, errno, "Failed to move process into target cgroup via fd %d and %d",
target_fd0, target_fd1);
}
struct userns_exec_unified_attach_data {
const struct lxc_conf *conf;
int unified_fd;
int sk_pair[2];
pid_t pid;
};
static int cgroup_unified_attach_wrapper(void *data)
static int cgroup_unified_attach_child_wrapper(void *data)
{
struct userns_exec_unified_attach_data *args = data;
if (!args->conf || args->unified_fd < 0 || args->pid <= 0)
if (!args->conf || args->unified_fd < 0 || args->pid <= 0 ||
args->sk_pair[0] < 0 || args->sk_pair[1] < 0)
return ret_errno(EINVAL);
return cgroup_attach_leaf(args->conf, args->unified_fd, args->pid);
close_prot_errno_disarm(args->sk_pair[0]);
return cgroup_attach_create_leaf(args->conf, args->unified_fd,
&args->sk_pair[1]);
}
static int cgroup_unified_attach_parent_wrapper(void *data)
{
struct userns_exec_unified_attach_data *args = data;
if (!args->conf || args->unified_fd < 0 || args->pid <= 0 ||
args->sk_pair[0] < 0 || args->sk_pair[1] < 0)
return ret_errno(EINVAL);
close_prot_errno_disarm(args->sk_pair[1]);
return cgroup_attach_move_into_leaf(args->conf, &args->sk_pair[0],
args->pid);
}
int cgroup_attach(const struct lxc_conf *conf, const char *name,
......@@ -2159,7 +2236,15 @@ int cgroup_attach(const struct lxc_conf *conf, const char *name,
.pid = pid,
};
ret = userns_exec_minimal(conf, cgroup_unified_attach_wrapper, &args);
ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, args.sk_pair);
if (ret < 0)
return -errno;
ret = userns_exec_minimal(conf,
cgroup_unified_attach_parent_wrapper,
&args,
cgroup_unified_attach_child_wrapper,
&args);
} else {
ret = cgroup_attach_leaf(conf, unified_fd, pid);
}
......@@ -2213,7 +2298,15 @@ static int __cg_unified_attach(const struct hierarchy *h,
.pid = pid,
};
ret = userns_exec_minimal(conf, cgroup_unified_attach_wrapper, &args);
ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, args.sk_pair);
if (ret < 0)
return -errno;
ret = userns_exec_minimal(conf,
cgroup_unified_attach_parent_wrapper,
&args,
cgroup_unified_attach_child_wrapper,
&args);
} else {
ret = cgroup_attach_leaf(conf, unified_fd, pid);
}
......
......@@ -4126,7 +4126,9 @@ on_error:
return ret;
}
int userns_exec_minimal(const struct lxc_conf *conf, int (*fn)(void *), void *data)
int userns_exec_minimal(const struct lxc_conf *conf,
int (*fn_parent)(void *), void *fn_parent_data,
int (*fn_child)(void *), void *fn_child_data)
{
call_cleaner(lxc_free_idmap) struct lxc_list *idmap = NULL;
uid_t resuid = LXC_INVALID_UID;
......@@ -4136,7 +4138,7 @@ int userns_exec_minimal(const struct lxc_conf *conf, int (*fn)(void *), void *da
pid_t pid;
int sock_fds[2];
if (!conf || !fn || !data)
if (!conf || !fn_child)
return ret_errno(EINVAL);
idmap = get_minimal_idmap(conf, &resuid, &resgid);
......@@ -4189,7 +4191,7 @@ int userns_exec_minimal(const struct lxc_conf *conf, int (*fn)(void *), void *da
_exit(EXIT_FAILURE);
}
ret = fn(data);
ret = fn_child(fn_child_data);
if (ret) {
SYSERROR("Running function in new user namespace failed");
_exit(EXIT_FAILURE);
......@@ -4232,6 +4234,11 @@ int userns_exec_minimal(const struct lxc_conf *conf, int (*fn)(void *), void *da
goto on_error;
}
if (fn_parent && fn_parent(fn_parent_data)) {
SYSERROR("Running parent function failed");
_exit(EXIT_FAILURE);
}
on_error:
close_prot_errno_disarm(sock_fds[0]);
close_prot_errno_disarm(sock_fds[1]);
......
......@@ -467,6 +467,8 @@ extern int setup_proc_filesystem(struct lxc_list *procs, pid_t pid);
extern int lxc_clear_procs(struct lxc_conf *c, const char *key);
extern int lxc_clear_apparmor_raw(struct lxc_conf *c);
extern int lxc_clear_namespace(struct lxc_conf *c);
extern int userns_exec_minimal(const struct lxc_conf *conf, int (*fn)(void *), void *data);
extern int userns_exec_minimal(const struct lxc_conf *conf,
int (*fn_parent)(void *), void *fn_parent_data,
int (*fn_child)(void *), void *fn_child_data);
#endif /* __LXC_CONF_H */
......@@ -1703,6 +1703,7 @@ static int lxc_spawn(struct lxc_handler *handler)
}
if (!cgroup_ops->payload_enter(cgroup_ops, handler)) {
ERROR("Failed to enter cgroups");
goto out_delete_net;
}
......
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