]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
af_unix: allow caller and callee to negotiate expectations and reality
authorChristian Brauner <christian.brauner@ubuntu.com>
Thu, 25 Feb 2021 10:18:09 +0000 (11:18 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Thu, 25 Feb 2021 15:05:06 +0000 (16:05 +0100)
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/af_unix.c
src/lxc/af_unix.h

index 747e6882050b0be324946453c34d7286d91a9249..f1d36c5dd3d797164212417757ce820d69619e4a 100644 (file)
@@ -113,7 +113,7 @@ int lxc_abstract_unix_connect(const char *path)
 }
 
 int lxc_abstract_unix_send_fds_iov(int fd, const int *sendfds, int num_sendfds,
-                                  struct iovec *iov, size_t iovlen)
+                                  struct iovec *const iov, size_t iovlen)
 {
        __do_free char *cmsgbuf = NULL;
        int ret;
@@ -176,6 +176,12 @@ static ssize_t lxc_abstract_unix_recv_fds_iov(int fd,
        size_t cmsgbufsize = CMSG_SPACE(sizeof(struct ucred)) +
                             CMSG_SPACE(ret_fds->fd_count_max * sizeof(int));
 
+       if (ret_fds->flags & ~UNIX_FDS_ACCEPT_MASK)
+               return ret_errno(EINVAL);
+
+       if (hweight32((ret_fds->flags & ~UNIX_FDS_ACCEPT_NONE)) > 1)
+               return ret_errno(EINVAL);
+
        cmsgbuf = zalloc(cmsgbufsize);
        if (!cmsgbuf)
                return ret_errno(ENOMEM);
@@ -202,7 +208,7 @@ again:
                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
                        __u32 idx;
                        /*
-                        * This causes some compilers to complaing about
+                        * This causes some compilers to complain about
                         * increased alignment requirements but I haven't found
                         * a better way to deal with this yet. Suggestions
                         * welcome!
@@ -225,7 +231,22 @@ again:
                                return syserrno_set(-EFBIG, "Received excessive number of file descriptors");
                        }
 
+                       if (msg.msg_flags & MSG_CTRUNC) {
+                               for (idx = 0; idx < num_raw; idx++)
+                                       close(fds_raw[idx]);
+
+                               return syserrno_set(-EFBIG, "Control message was truncated; closing all fds and rejecting incomplete message");
+                       }
+
                        if (ret_fds->fd_count_max > num_raw) {
+                               if (!(ret_fds->flags & UNIX_FDS_ACCEPT_LESS)) {
+                                       for (idx = 0; idx < num_raw; idx++)
+                                               close(fds_raw[idx]);
+
+                                       return syserrno_set(-EINVAL, "Received fewer file descriptors than we expected %u != %u",
+                                                           ret_fds->fd_count_max, num_raw);
+                               }
+
                                /*
                                 * Make sure any excess entries in the fd array
                                 * are set to -EBADF so our cleanup functions
@@ -234,16 +255,33 @@ again:
                                for (idx = num_raw; idx < ret_fds->fd_count_max; idx++)
                                        ret_fds->fd[idx] = -EBADF;
 
-                               WARN("Received fewer file descriptors than we expected %u != %u", ret_fds->fd_count_max, num_raw);
+                               ret_fds->flags |= UNIX_FDS_RECEIVED_LESS;
                        } else if (ret_fds->fd_count_max < num_raw) {
+                               if (!(ret_fds->flags & UNIX_FDS_ACCEPT_MORE)) {
+                                       for (idx = 0; idx < num_raw; idx++)
+                                               close(fds_raw[idx]);
+
+                                       return syserrno_set(-EINVAL, "Received more file descriptors than we expected %u != %u",
+                                                           ret_fds->fd_count_max, num_raw);
+                               }
+
                                /* Make sure we close any excess fds we received. */
                                for (idx = ret_fds->fd_count_max; idx < num_raw; idx++)
                                        close(fds_raw[idx]);
 
-                               WARN("Received more file descriptors than we expected %u != %u", ret_fds->fd_count_max, num_raw);
-
                                /* Cap the number of received file descriptors. */
                                num_raw = ret_fds->fd_count_max;
+                               ret_fds->flags |= UNIX_FDS_RECEIVED_MORE;
+                       } else {
+                               ret_fds->flags |= UNIX_FDS_RECEIVED_EXACT;
+                       }
+
+                       if (hweight32((ret_fds->flags & ~UNIX_FDS_ACCEPT_MASK)) > 1) {
+                               for (idx = 0; idx < num_raw; idx++)
+                                       close(fds_raw[idx]);
+
+                               return syserrno_set(-EINVAL, "Invalid flag combination; closing to not risk leaking fds %u != %u",
+                                                   ret_fds->fd_count_max, num_raw);
                        }
 
                        memcpy(ret_fds->fd, CMSG_DATA(cmsg), num_raw * sizeof(int));
@@ -252,6 +290,15 @@ again:
                }
        }
 
+       if (ret_fds->fd_count_ret == 0) {
+               ret_fds->flags |= UNIX_FDS_RECEIVED_NONE;
+
+               /* We expected to receive file descriptors. */
+               if ((ret_fds->flags & UNIX_FDS_ACCEPT_MASK) &&
+                   !(ret_fds->flags & UNIX_FDS_ACCEPT_NONE))
+                       return syserrno_set(-EINVAL, "Received no file descriptors");
+       }
+
        return ret;
 }
 
index d8a9ad9ca34f59c6506bcac145fc66134ca15dc9..7b979374348abff3a1512f4412b6ca91038ae0c1 100644 (file)
 #include "macro.h"
 #include "memory_utils.h"
 
+#define KERNEL_SCM_MAX_FD 253
+
+/* Allow the caller to set expectations. */
+
+/*
+ * UNIX_FDS_ACCEPT_EXACT will only succeed if the exact amount of fds has been
+ * received  (unless combined with UNIX_FDS_ACCEPT_NONE).
+ */
+#define UNIX_FDS_ACCEPT_EXACT ((__u32)(1 << 0)) /* default */
+
+/*
+ * UNIX_FDS_ACCEPT_LESS will also succeed if less than the requested number of
+ * fd has been received. If the UNIX_FDS_ACCEPT_NONE flag is not raised than at
+ * least one fd must be received.
+ * */
+#define UNIX_FDS_ACCEPT_LESS ((__u32)(1 << 1))
+
+/*
+ * UNIX_FDS_ACCEPT_MORE will also succeed if more than the requested number of
+ * fds have been received. Any additional fds will be silently closed.  If the
+ * UNIX_FDS_ACCEPT_NONE flag is not raised than at least one fd must be
+ * received.
+ */
+#define UNIX_FDS_ACCEPT_MORE ((__u32)(1 << 2)) /* wipe any extra fds */
+
 /*
- * Technically 253 is the kernel limit but we want to the struct to be a
- * multiple of 8.
+ * UNIX_FDS_ACCEPT_NONE can be specified with any of the above flags and
+ * indicates that the caller will accept no file descriptors to be received.
  */
-#define KERNEL_SCM_MAX_FD 252
+#define UNIX_FDS_ACCEPT_NONE ((__u32)(1 << 3))
 
+/* UNIX_FDS_ACCEPT_MASK is the value of all the above flags or-ed together. */
+#define UNIX_FDS_ACCEPT_MASK (UNIX_FDS_ACCEPT_EXACT | UNIX_FDS_ACCEPT_LESS | UNIX_FDS_ACCEPT_MORE | UNIX_FDS_ACCEPT_NONE)
+
+/* Allow the callee to communicate reality. */
+
+/* UNIX_FDS_RECEIVED_EXACT indicates that the exact number of fds was received. */
+#define UNIX_FDS_RECEIVED_EXACT ((__u32)(1 << 16))
+
+/*
+ * UNIX_FDS_RECEIVED_LESS indicates that less than the requested number of fd
+ * has been received.
+ */
+#define UNIX_FDS_RECEIVED_LESS ((__u32)(1 << 17))
+
+/*
+ * UNIX_FDS_RECEIVED_MORE indicates that more than the requested number of fd
+ * has been received.
+ */
+#define UNIX_FDS_RECEIVED_MORE ((__u32)(1 << 18))
+
+/* UNIX_FDS_RECEIVED_NONE indicates that no fds have been received. */
+#define UNIX_FDS_RECEIVED_NONE ((__u32)(1 << 19))
+
+/**
+ * Defines a generic struct to receive file descriptors from unix sockets.
+ * @fd_count_max : Either the exact or maximum number of file descriptors the
+ *                 caller is willing to accept. Must be smaller than
+ *                 KERNEL_SCM_MAX_FDs; larger values will be rejected.
+ *                 Filled in by the caller.
+ * @fd_count_ret : The actually received number of file descriptors.
+ *                 Filled in by the callee.
+ * @flags        : Flags to negotiate expectations about the number of file
+ *                 descriptors to receive.
+ *                 Filled in by the caller and callee. The caller's flag space
+ *                 is UNIX_FDS_ACCEPT_* other values will be rejected. The
+ *                 caller may only set one of {EXACT, LESS, MORE}. In addition
+ *                 they can raise the NONE flag. Any combination of {EXACT,
+ *                 LESS, MORE} will be rejected.
+ *                 The callee's flag space is UNIX_FDS_RECEIVED_*. Only ever
+ *                 one of those values will be set.
+ * @fd           : Array to store received file descriptors into. Filled by the
+ *                 callee on success. If less file descriptors are received
+ *                 than requested in @fd_count_max the callee will ensure that
+ *                 all additional slots will be set to -EBADF. Nonetheless, the
+ *                 caller should only ever use @fd_count_ret to iterate through
+ *                 @fd after a successful receive.
+ */
 struct unix_fds {
        __u32 fd_count_max;
        __u32 fd_count_ret;
+       __u32 flags;
        __s32 fd[KERNEL_SCM_MAX_FD];
 } __attribute__((aligned(8)));