]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
commands: introduce LXC_CMD_GET_CGROUP_CTX
authorChristian Brauner <christian.brauner@ubuntu.com>
Tue, 23 Feb 2021 13:00:54 +0000 (14:00 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Tue, 23 Feb 2021 15:15:32 +0000 (16:15 +0100)
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/cgroups/cgroup.h
src/lxc/commands.c
src/lxc/commands.h

index bdc62ff25ddeb34b54ef42732037b6b6fe00c3e6..6c4e85b1afa3c551225d65e1e60a4abef35fe74f 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <stdbool.h>
 #include <stddef.h>
+#include <linux/types.h>
 #include <sys/types.h>
 #include <linux/magic.h>
 
@@ -42,6 +43,17 @@ typedef enum {
 #define DEVICES_CONTROLLER (1U << 0)
 #define FREEZER_CONTROLLER (1U << 1)
 
+/* That's plenty of hierarchies. */
+#define CGROUP_CTX_MAX_FD 20
+// BUILD_BUG_ON(CGROUP_CTX_MAX_FD > KERNEL_SCM_MAX_FD);
+
+struct cgroup_ctx {
+       __s32 cgroup_layout;
+       __u32 utilities;
+       __u32 fd_len;
+       __s32 fd[CGROUP_CTX_MAX_FD];
+} __attribute__((aligned(8)));
+
 /* A descriptor for a mounted hierarchy
  *
  * @controllers
@@ -258,15 +270,36 @@ static inline int cgroup_unified_fd(const struct cgroup_ops *ops)
                               __first, __VA_ARGS__);                  \
        })
 
-static inline ssize_t cgroup_fds(struct cgroup_ops *ops,
-                                int dfds_con[KERNEL_SCM_MAX_FD])
+static void put_cgroup_ctx(struct cgroup_ctx *ctx)
+{
+       if (!IS_ERR_OR_NULL(ctx)) {
+               for (__u32 idx = 0; idx < ctx->fd_len; idx++)
+                       close_prot_errno_disarm(ctx->fd[idx]);
+       }
+}
+define_cleanup_function(struct cgroup_ctx *, put_cgroup_ctx);
+
+static inline int prepare_cgroup_ctx(struct cgroup_ops *ops,
+                                    struct cgroup_ctx *ctx)
 {
-       ssize_t num_dfds = 0;
+       __u32 idx;
+
+       for (idx = 0; ops->hierarchies[idx]; idx++) {
+               if (idx >= CGROUP_CTX_MAX_FD)
+                       return ret_errno(E2BIG);
+
+               ctx->fd[idx] = ops->hierarchies[idx]->dfd_con;
+       }
+
+       if (idx == 0)
+               return ret_errno(ENOENT);
 
-       for (num_dfds = 0; ops->hierarchies[num_dfds]; num_dfds++)
-               dfds_con[num_dfds] = ops->hierarchies[num_dfds]->dfd_con;
+       ctx->fd_len = idx;
+       ctx->cgroup_layout = ops->cgroup_layout;
+       if (ops->unified && ops->unified->dfd_con > 0)
+               ctx->utilities = ops->unified->utilities;
 
-       return num_dfds;
+       return 0;
 }
 
 #endif /* __LXC_CGROUP_H */
index cb26f68317e3257952df2a7454803433142abd7a..f8d4b7ef39d064b563dce4ceff90a9c842bceaab 100644 (file)
@@ -88,7 +88,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
                [LXC_CMD_GET_LIMITING_CGROUP2_FD]       = "get_limiting_cgroup2_fd",
                [LXC_CMD_GET_DEVPTS_FD]                 = "get_devpts_fd",
                [LXC_CMD_GET_SECCOMP_NOTIFY_FD]         = "get_seccomp_notify_fd",
-               [LXC_CMD_GET_CGROUP_FD]                 = "get_cgroup_fd",
+               [LXC_CMD_GET_CGROUP_CTX]                = "get_cgroup_ctx",
        };
 
        if (cmd >= LXC_CMD_MAX)
@@ -97,6 +97,19 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
        return cmdname[cmd];
 }
 
+static int __transfer_cgroup_ctx_fds(struct unix_fds *fds, struct cgroup_ctx *ctx)
+{
+       /* This shouldn't be able to happen but better safe than sorry. */
+       if (ctx->fd_len != fds->fd_count_ret ||
+           fds->fd_count_ret > CGROUP_CTX_MAX_FD)
+               return syswarn_set(-EINVAL, "Unexpected number of file descriptors received %u != %u",
+                                  ctx->fd_len, fds->fd_count_ret);
+
+       memcpy(ctx->fd, fds->fd, ctx->fd_len * sizeof(__s32));
+       fds->fd_count_ret = 0;
+       return 0;
+}
+
 /*
  * lxc_cmd_rsp_recv: Receive a response to a command
  *
@@ -118,6 +131,7 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
 {
        call_cleaner(put_unix_fds) struct unix_fds *fds = &(struct unix_fds){};
        struct lxc_cmd_rsp *rsp = &cmd->rsp;
+       const char *reqstr = lxc_cmd_str(cmd->req.cmd);
        int ret;
 
        switch (cmd->req.cmd) {
@@ -134,17 +148,17 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
        case LXC_CMD_CONSOLE:
                fds->fd_count_max = 1;
                break;
-       case LXC_CMD_GET_CGROUP_FD:
-               fds->fd_count_max = KERNEL_SCM_MAX_FD;
+       case LXC_CMD_GET_CGROUP_CTX:
+               fds->fd_count_max = CGROUP_CTX_MAX_FD;
                break;
        default:
                fds->fd_count_max = 0;
+               break;
        }
        ret = lxc_abstract_unix_recv_fds(sock, fds, rsp, sizeof(*rsp));
        if (ret < 0)
-               return syserrno(ret, "Failed to receive response for command \"%s\"",
-                               lxc_cmd_str(cmd->req.cmd));
-       TRACE("Command \"%s\" received response", lxc_cmd_str(cmd->req.cmd));
+               return syserrno(ret, "Failed to receive response for command \"%s\"", reqstr);
+       TRACE("Command \"%s\" received response with %u file descriptors", reqstr, fds->fd_count_ret);
 
        if (cmd->req.cmd == LXC_CMD_CONSOLE) {
                struct lxc_cmd_console_rsp_data *rspdata;
@@ -157,9 +171,7 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
 
                rspdata = malloc(sizeof(*rspdata));
                if (!rspdata)
-                       return log_warn_errno(-1,
-                                             ENOMEM, "Failed to receive response for command \"%s\"",
-                                             lxc_cmd_str(cmd->req.cmd));
+                       return syserrno_set(-ENOMEM, "Failed to receive response for command \"%s\"", reqstr);
 
                rspdata->ptxfd = move_fd(fds->fd[0]);
                rspdata->ttynum = PTR_TO_INT(rsp->data);
@@ -177,43 +189,42 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
                __fallthrough;
        case LXC_CMD_GET_SECCOMP_NOTIFY_FD:
                rsp->data = INT_TO_PTR(move_fd(fds->fd[0]));
-               return log_debug(ret, "Finished processing \"%s\"", lxc_cmd_str(cmd->req.cmd));
-       case LXC_CMD_GET_CGROUP_FD:
-               rsp->data = move_ptr(fds);
-               rsp->datalen = sizeof(struct unix_fds);
-               return log_debug(ret, "Finished processing \"%s\"", lxc_cmd_str(cmd->req.cmd));
+               return log_debug(ret, "Finished processing \"%s\"", reqstr);
+       case LXC_CMD_GET_CGROUP_CTX:
+               if (rsp->datalen > sizeof(struct cgroup_ctx))
+                       return syserrno_set(-EINVAL, "Invalid response size from server for \"%s\"", reqstr);
+
+               /* Don't pointlessly allocate. */
+               rsp->data = (void *)cmd->req.data;
                break;
        default:
                break;
        }
 
        if (rsp->datalen == 0)
-               return log_debug(ret,
-                                "Response data length for command \"%s\" is 0",
-                                lxc_cmd_str(cmd->req.cmd));
+               return log_debug(ret, "Response data length for command \"%s\" is 0", reqstr);
 
        if ((rsp->datalen > LXC_CMD_DATA_MAX) &&
            (cmd->req.cmd != LXC_CMD_CONSOLE_LOG))
-               return log_error(-1, "Response data for command \"%s\" is too long: %d bytes > %d",
-                                lxc_cmd_str(cmd->req.cmd), rsp->datalen,
-                                LXC_CMD_DATA_MAX);
+               return syserrno_set(-E2BIG, "Response data for command \"%s\" is too long: %d bytes > %d",
+                                   reqstr, rsp->datalen, LXC_CMD_DATA_MAX);
 
-       if (cmd->req.cmd == LXC_CMD_CONSOLE_LOG) {
-               rsp->data = malloc(rsp->datalen + 1);
-               ((char *)rsp->data)[rsp->datalen] = '\0';
-       } else {
+       if (cmd->req.cmd == LXC_CMD_CONSOLE_LOG)
+               rsp->data = zalloc(rsp->datalen + 1);
+       else if (cmd->req.cmd != LXC_CMD_GET_CGROUP_CTX)
                rsp->data = malloc(rsp->datalen);
-       }
        if (!rsp->data)
-               return log_error_errno(-1,
-                                      ENOMEM, "Failed to allocate response buffer for command \"%s\"",
-                                      lxc_cmd_str(cmd->req.cmd));
+               return syserrno_set(-ENOMEM, "Failed to allocate response buffer for command \"%s\"", reqstr);
 
        ret = lxc_recv_nointr(sock, rsp->data, rsp->datalen, 0);
        if (ret != rsp->datalen)
-               return log_error_errno(-1,
-                                      errno, "Failed to receive response data for command \"%s\"",
-                                      lxc_cmd_str(cmd->req.cmd));
+               return syserrno(-errno, "Failed to receive response data for command \"%s\"", reqstr);
+
+       if (cmd->req.cmd == LXC_CMD_GET_CGROUP_CTX) {
+               ret = __transfer_cgroup_ctx_fds(fds, rsp->data);
+               if (ret < 0)
+                       return syserrno(ret, "Failed to transfer file descriptors for \"%s\"", reqstr);
+       }
 
        return ret;
 }
@@ -266,15 +277,30 @@ static inline int rsp_one_fd(int fd, int fd_send, struct lxc_cmd_rsp *rsp)
        return LXC_CMD_REAP_CLIENT_FD;
 }
 
-static inline int rsp_many_fds(int fd, struct unix_fds *fds, struct lxc_cmd_rsp *rsp)
+static inline int rsp_many_fds(int fd, __u32 fds_len,
+                              const __s32 fds[KERNEL_SCM_MAX_FD],
+                              struct lxc_cmd_rsp *rsp)
 {
-       int ret;
+       ssize_t ret;
 
-       ret = lxc_abstract_unix_send_fds(fd, fds->fd, fds->fd_count_max,
-                                        rsp, sizeof(*rsp));
+       if (fds_len > KERNEL_SCM_MAX_FD) {
+               rsp->ret = -E2BIG;
+               return lxc_cmd_rsp_send_reap(fd, rsp);
+       } else if (fds_len == 0) {
+               rsp->ret = -ENOENT;
+               return lxc_cmd_rsp_send_reap(fd, rsp);
+       }
+
+       ret = lxc_abstract_unix_send_fds(fd, fds, fds_len, rsp, sizeof(*rsp));
        if (ret < 0)
                return ret;
 
+       if (rsp->data && rsp->datalen > 0) {
+               ret = lxc_send_nointr(fd, rsp->data, rsp->datalen, MSG_NOSIGNAL);
+               if (ret < 0 || ret != (ssize_t)rsp->datalen)
+                       return syswarn(-errno, "Failed to send command response %zd", ret);
+       }
+
        return LXC_CMD_REAP_CLIENT_FD;
 }
 
@@ -573,16 +599,18 @@ static int lxc_cmd_get_seccomp_notify_fd_callback(int fd, struct lxc_cmd_req *re
 #endif
 }
 
-int lxc_cmd_get_cgroup_fd(const char *name, const char *lxcpath,
-                         const char *controller, bool batch,
-                         struct unix_fds *ret_fds)
+int lxc_cmd_get_cgroup_ctx(const char *name, const char *lxcpath,
+               const char *controller, bool batch,
+               size_t size_ret_ctx, struct cgroup_ctx *ret_ctx)
 {
-       int ret, stopped;
        struct lxc_cmd_rr cmd = {
                .req = {
-                       .cmd = LXC_CMD_GET_CGROUP_FD,
+                       .cmd            = LXC_CMD_GET_CGROUP_CTX,
+                       .datalen        = size_ret_ctx,
+                       .data           = ret_ctx,
                },
        };
+       int ret, stopped;
 
        if (batch && !is_empty_string(controller))
                return ret_errno(EINVAL);
@@ -594,29 +622,34 @@ int lxc_cmd_get_cgroup_fd(const char *name, const char *lxcpath,
        if (cmd.rsp.ret < 0)
                return log_debug_errno(-EBADF, errno, "Failed to receive cgroup fds");
 
-       *ret_fds = *(struct unix_fds *)cmd.rsp.data;
-
        return 0;
 }
 
-static int lxc_cmd_get_cgroup_fd_callback(int fd, struct lxc_cmd_req *req,
-                                         struct lxc_handler *handler,
-                                         struct lxc_epoll_descr *descr)
+static int lxc_cmd_get_cgroup_ctx_callback(int fd, struct lxc_cmd_req *req,
+                                          struct lxc_handler *handler,
+                                          struct lxc_epoll_descr *descr)
 {
        struct lxc_cmd_rsp rsp = {
-               .ret = 0,
+           .ret = EINVAL,
        };
        struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
-       struct unix_fds *fds = {};
+       struct cgroup_ctx ctx_server = {};
        int ret;
 
-       fds->fd_count_max = cgroup_fds(cgroup_ops, fds->fd);
-       ret = lxc_abstract_unix_send_fds(fd, fds->fd, fds->fd_count_max,
-                                        &rsp, sizeof(rsp));
+       ret = copy_struct_from_client(sizeof(struct cgroup_ctx), &ctx_server,
+                                     req->datalen, req->data);
        if (ret < 0)
-               return log_error(ret, "Failed to send cgroup fds");
+               return lxc_cmd_rsp_send_reap(fd, &rsp);
+
+       ret = prepare_cgroup_ctx(cgroup_ops, &ctx_server);
+       if (ret < 0) {
+               rsp.ret = ret;
+               return lxc_cmd_rsp_send_reap(fd, &rsp);
+       }
 
-       return log_trace(LXC_CMD_REAP_CLIENT_FD, "Sent cgroup fds");
+       rsp.data = &ctx_server;
+       rsp.datalen = min(sizeof(struct cgroup_ctx), (size_t)req->datalen);
+       return rsp_many_fds(fd, ctx_server.fd_len, ctx_server.fd, &rsp);
 }
 
 /*
@@ -1614,7 +1647,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
                [LXC_CMD_GET_LIMITING_CGROUP2_FD]       = lxc_cmd_get_limiting_cgroup2_fd_callback,
                [LXC_CMD_GET_DEVPTS_FD]                 = lxc_cmd_get_devpts_fd_callback,
                [LXC_CMD_GET_SECCOMP_NOTIFY_FD]         = lxc_cmd_get_seccomp_notify_fd_callback,
-               [LXC_CMD_GET_CGROUP_FD]                 = lxc_cmd_get_cgroup_fd_callback,
+               [LXC_CMD_GET_CGROUP_CTX]                = lxc_cmd_get_cgroup_ctx_callback,
        };
 
        if (req->cmd >= LXC_CMD_MAX)
index 2ce5de61f503ef13f88c4f9f5f1b95bf01848622..63ba169334dc6187998324069efc0f5ded0464a5 100644 (file)
@@ -8,6 +8,7 @@
 #include <unistd.h>
 
 #include "compiler.h"
+#include "cgroups/cgroup.h"
 #include "lxccontainer.h"
 #include "macro.h"
 #include "state.h"
@@ -43,7 +44,7 @@ typedef enum {
        LXC_CMD_GET_LIMITING_CGROUP2_FD         = 20,
        LXC_CMD_GET_DEVPTS_FD                   = 21,
        LXC_CMD_GET_SECCOMP_NOTIFY_FD           = 22,
-       LXC_CMD_GET_CGROUP_FD                   = 23,
+       LXC_CMD_GET_CGROUP_CTX                  = 23,
        LXC_CMD_MAX,
 } lxc_cmd_t;
 
@@ -123,9 +124,11 @@ __hidden extern int lxc_try_cmd(const char *name, const char *lxcpath);
 __hidden extern int lxc_cmd_console_log(const char *name, const char *lxcpath,
                                        struct lxc_console_log *log);
 __hidden extern int lxc_cmd_get_seccomp_notify_fd(const char *name, const char *lxcpath);
-__hidden extern int lxc_cmd_get_cgroup_fd(const char *name, const char *lxcpath,
-                                         const char *controller, bool batch,
-                                         struct unix_fds *ret_fds);
+__hidden extern int lxc_cmd_get_cgroup_ctx(const char *name, const char *lxcpath,
+                                          const char *controller, bool batch,
+                                          size_t size_ret_ctx,
+                                          struct cgroup_ctx *ret_ctx)
+    __access_r(6, 5);
 __hidden extern int lxc_cmd_seccomp_notify_add_listener(const char *name, const char *lxcpath, int fd,
                                                        /* unused */ unsigned int command,
                                                        /* unused */ unsigned int flags);