Blame SOURCES/0190-socket-util-Introduce-send_one_fd_iov-and-receive_on.patch

a3e2b5
From bc7c550c444210aa8decf98ac0c1dcd051fcc532 Mon Sep 17 00:00:00 2001
a3e2b5
From: Filipe Brandenburger <filbranden@google.com>
a3e2b5
Date: Tue, 24 Jul 2018 18:46:01 -0700
a3e2b5
Subject: [PATCH] socket-util: Introduce send_one_fd_iov() and
a3e2b5
 receive_one_fd_iov()
a3e2b5
a3e2b5
These take a struct iovec to send data together with the passed FD.
a3e2b5
a3e2b5
The receive function returns the FD through an output argument. In case data is
a3e2b5
received, but no FD is passed, the receive function will set the output
a3e2b5
argument to -1 explicitly.
a3e2b5
a3e2b5
Update code in dynamic-user to use the new helpers.
a3e2b5
a3e2b5
(cherry picked from commit d34673ecb825aa9ecf6958b0caab792f5061c56a)
a3e2b5
a3e2b5
Resolves: #1683319
a3e2b5
---
a3e2b5
 src/basic/socket-util.c | 97 ++++++++++++++++++++++++++++++++---------
a3e2b5
 src/basic/socket-util.h | 10 ++++-
a3e2b5
 src/core/dynamic-user.c | 57 ++----------------------
a3e2b5
 3 files changed, 90 insertions(+), 74 deletions(-)
a3e2b5
a3e2b5
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
a3e2b5
index 3f90a81d35..986bc6e67f 100644
a3e2b5
--- a/src/basic/socket-util.c
a3e2b5
+++ b/src/basic/socket-util.c
a3e2b5
@@ -1011,9 +1011,10 @@ int getpeergroups(int fd, gid_t **ret) {
a3e2b5
         return (int) n;
a3e2b5
 }
a3e2b5
 
a3e2b5
-int send_one_fd_sa(
a3e2b5
+ssize_t send_one_fd_iov_sa(
a3e2b5
                 int transport_fd,
a3e2b5
                 int fd,
a3e2b5
+                struct iovec *iov, size_t iovlen,
a3e2b5
                 const struct sockaddr *sa, socklen_t len,
a3e2b5
                 int flags) {
a3e2b5
 
a3e2b5
@@ -1024,28 +1025,58 @@ int send_one_fd_sa(
a3e2b5
         struct msghdr mh = {
a3e2b5
                 .msg_name = (struct sockaddr*) sa,
a3e2b5
                 .msg_namelen = len,
a3e2b5
-                .msg_control = &control,
a3e2b5
-                .msg_controllen = sizeof(control),
a3e2b5
+                .msg_iov = iov,
a3e2b5
+                .msg_iovlen = iovlen,
a3e2b5
         };
a3e2b5
-        struct cmsghdr *cmsg;
a3e2b5
+        ssize_t k;
a3e2b5
 
a3e2b5
         assert(transport_fd >= 0);
a3e2b5
-        assert(fd >= 0);
a3e2b5
 
a3e2b5
-        cmsg = CMSG_FIRSTHDR(&mh);
a3e2b5
-        cmsg->cmsg_level = SOL_SOCKET;
a3e2b5
-        cmsg->cmsg_type = SCM_RIGHTS;
a3e2b5
-        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
a3e2b5
-        memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
a3e2b5
+        /*
a3e2b5
+         * We need either an FD or data to send.
a3e2b5
+         * If there's nothing, return an error.
a3e2b5
+         */
a3e2b5
+        if (fd < 0 && !iov)
a3e2b5
+                return -EINVAL;
a3e2b5
 
a3e2b5
-        mh.msg_controllen = CMSG_SPACE(sizeof(int));
a3e2b5
-        if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0)
a3e2b5
-                return -errno;
a3e2b5
+        if (fd >= 0) {
a3e2b5
+                struct cmsghdr *cmsg;
a3e2b5
 
a3e2b5
-        return 0;
a3e2b5
+                mh.msg_control = &control;
a3e2b5
+                mh.msg_controllen = sizeof(control);
a3e2b5
+
a3e2b5
+                cmsg = CMSG_FIRSTHDR(&mh);
a3e2b5
+                cmsg->cmsg_level = SOL_SOCKET;
a3e2b5
+                cmsg->cmsg_type = SCM_RIGHTS;
a3e2b5
+                cmsg->cmsg_len = CMSG_LEN(sizeof(int));
a3e2b5
+                memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
a3e2b5
+
a3e2b5
+                mh.msg_controllen = CMSG_SPACE(sizeof(int));
a3e2b5
+        }
a3e2b5
+        k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags);
a3e2b5
+        if (k < 0)
a3e2b5
+                return (ssize_t) -errno;
a3e2b5
+
a3e2b5
+        return k;
a3e2b5
 }
a3e2b5
 
a3e2b5
-int receive_one_fd(int transport_fd, int flags) {
a3e2b5
+int send_one_fd_sa(
a3e2b5
+                int transport_fd,
a3e2b5
+                int fd,
a3e2b5
+                const struct sockaddr *sa, socklen_t len,
a3e2b5
+                int flags) {
a3e2b5
+
a3e2b5
+        assert(fd >= 0);
a3e2b5
+
a3e2b5
+        return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags);
a3e2b5
+}
a3e2b5
+
a3e2b5
+ssize_t receive_one_fd_iov(
a3e2b5
+                int transport_fd,
a3e2b5
+                struct iovec *iov, size_t iovlen,
a3e2b5
+                int flags,
a3e2b5
+                int *ret_fd) {
a3e2b5
+
a3e2b5
         union {
a3e2b5
                 struct cmsghdr cmsghdr;
a3e2b5
                 uint8_t buf[CMSG_SPACE(sizeof(int))];
a3e2b5
@@ -1053,10 +1084,14 @@ int receive_one_fd(int transport_fd, int flags) {
a3e2b5
         struct msghdr mh = {
a3e2b5
                 .msg_control = &control,
a3e2b5
                 .msg_controllen = sizeof(control),
a3e2b5
+                .msg_iov = iov,
a3e2b5
+                .msg_iovlen = iovlen,
a3e2b5
         };
a3e2b5
         struct cmsghdr *cmsg, *found = NULL;
a3e2b5
+        ssize_t k;
a3e2b5
 
a3e2b5
         assert(transport_fd >= 0);
a3e2b5
+        assert(ret_fd);
a3e2b5
 
a3e2b5
         /*
a3e2b5
          * Receive a single FD via @transport_fd. We don't care for
a3e2b5
@@ -1066,8 +1101,9 @@ int receive_one_fd(int transport_fd, int flags) {
a3e2b5
          * combination with send_one_fd().
a3e2b5
          */
a3e2b5
 
a3e2b5
-        if (recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags) < 0)
a3e2b5
-                return -errno;
a3e2b5
+        k = recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
a3e2b5
+        if (k < 0)
a3e2b5
+                return (ssize_t) -errno;
a3e2b5
 
a3e2b5
         CMSG_FOREACH(cmsg, &mh) {
a3e2b5
                 if (cmsg->cmsg_level == SOL_SOCKET &&
a3e2b5
@@ -1079,12 +1115,33 @@ int receive_one_fd(int transport_fd, int flags) {
a3e2b5
                 }
a3e2b5
         }
a3e2b5
 
a3e2b5
-        if (!found) {
a3e2b5
+        if (!found)
a3e2b5
                 cmsg_close_all(&mh);
a3e2b5
+
a3e2b5
+        /* If didn't receive an FD or any data, return an error. */
a3e2b5
+        if (k == 0 && !found)
a3e2b5
                 return -EIO;
a3e2b5
-        }
a3e2b5
 
a3e2b5
-        return *(int*) CMSG_DATA(found);
a3e2b5
+        if (found)
a3e2b5
+                *ret_fd = *(int*) CMSG_DATA(found);
a3e2b5
+        else
a3e2b5
+                *ret_fd = -1;
a3e2b5
+
a3e2b5
+        return k;
a3e2b5
+}
a3e2b5
+
a3e2b5
+int receive_one_fd(int transport_fd, int flags) {
a3e2b5
+        int fd;
a3e2b5
+        ssize_t k;
a3e2b5
+
a3e2b5
+        k = receive_one_fd_iov(transport_fd, NULL, 0, flags, &fd;;
a3e2b5
+        if (k == 0)
a3e2b5
+                return fd;
a3e2b5
+
a3e2b5
+        /* k must be negative, since receive_one_fd_iov() only returns
a3e2b5
+         * a positive value if data was received through the iov. */
a3e2b5
+        assert(k < 0);
a3e2b5
+        return (int) k;
a3e2b5
 }
a3e2b5
 
a3e2b5
 ssize_t next_datagram_size_fd(int fd) {
a3e2b5
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
a3e2b5
index 8e23cf2dbd..82781a0de1 100644
a3e2b5
--- a/src/basic/socket-util.h
a3e2b5
+++ b/src/basic/socket-util.h
a3e2b5
@@ -130,11 +130,19 @@ int getpeercred(int fd, struct ucred *ucred);
a3e2b5
 int getpeersec(int fd, char **ret);
a3e2b5
 int getpeergroups(int fd, gid_t **ret);
a3e2b5
 
a3e2b5
+ssize_t send_one_fd_iov_sa(
a3e2b5
+                int transport_fd,
a3e2b5
+                int fd,
a3e2b5
+                struct iovec *iov, size_t iovlen,
a3e2b5
+                const struct sockaddr *sa, socklen_t len,
a3e2b5
+                int flags);
a3e2b5
 int send_one_fd_sa(int transport_fd,
a3e2b5
                    int fd,
a3e2b5
                    const struct sockaddr *sa, socklen_t len,
a3e2b5
                    int flags);
a3e2b5
-#define send_one_fd(transport_fd, fd, flags) send_one_fd_sa(transport_fd, fd, NULL, 0, flags)
a3e2b5
+#define send_one_fd_iov(transport_fd, fd, iov, iovlen, flags) send_one_fd_iov_sa(transport_fd, fd, iov, iovlen, NULL, 0, flags)
a3e2b5
+#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags)
a3e2b5
+ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd);
a3e2b5
 int receive_one_fd(int transport_fd, int flags);
a3e2b5
 
a3e2b5
 ssize_t next_datagram_size_fd(int fd);
a3e2b5
diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c
a3e2b5
index 7c5111ddf6..021fd93a76 100644
a3e2b5
--- a/src/core/dynamic-user.c
a3e2b5
+++ b/src/core/dynamic-user.c
a3e2b5
@@ -312,20 +312,8 @@ static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {
a3e2b5
 static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
a3e2b5
         uid_t uid = UID_INVALID;
a3e2b5
         struct iovec iov = IOVEC_INIT(&uid, sizeof(uid));
a3e2b5
-        union {
a3e2b5
-                struct cmsghdr cmsghdr;
a3e2b5
-                uint8_t buf[CMSG_SPACE(sizeof(int))];
a3e2b5
-        } control = {};
a3e2b5
-        struct msghdr mh = {
a3e2b5
-                .msg_control = &control,
a3e2b5
-                .msg_controllen = sizeof(control),
a3e2b5
-                .msg_iov = &iov,
a3e2b5
-                .msg_iovlen = 1,
a3e2b5
-        };
a3e2b5
-        struct cmsghdr *cmsg;
a3e2b5
-
a3e2b5
+        int lock_fd;
a3e2b5
         ssize_t k;
a3e2b5
-        int lock_fd = -1;
a3e2b5
 
a3e2b5
         assert(d);
a3e2b5
         assert(ret_uid);
a3e2b5
@@ -334,15 +322,9 @@ static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
a3e2b5
         /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with the lock
a3e2b5
          * on the socket taken. */
a3e2b5
 
a3e2b5
-        k = recvmsg(d->storage_socket[0], &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
a3e2b5
+        k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
a3e2b5
         if (k < 0)
a3e2b5
-                return -errno;
a3e2b5
-
a3e2b5
-        cmsg = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
a3e2b5
-        if (cmsg)
a3e2b5
-                lock_fd = *(int*) CMSG_DATA(cmsg);
a3e2b5
-        else
a3e2b5
-                cmsg_close_all(&mh); /* just in case... */
a3e2b5
+                return (int) k;
a3e2b5
 
a3e2b5
         *ret_uid = uid;
a3e2b5
         *ret_lock_fd = lock_fd;
a3e2b5
@@ -352,42 +334,11 @@ static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
a3e2b5
 
a3e2b5
 static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
a3e2b5
         struct iovec iov = IOVEC_INIT(&uid, sizeof(uid));
a3e2b5
-        union {
a3e2b5
-                struct cmsghdr cmsghdr;
a3e2b5
-                uint8_t buf[CMSG_SPACE(sizeof(int))];
a3e2b5
-        } control = {};
a3e2b5
-        struct msghdr mh = {
a3e2b5
-                .msg_control = &control,
a3e2b5
-                .msg_controllen = sizeof(control),
a3e2b5
-                .msg_iov = &iov,
a3e2b5
-                .msg_iovlen = 1,
a3e2b5
-        };
a3e2b5
-        ssize_t k;
a3e2b5
 
a3e2b5
         assert(d);
a3e2b5
 
a3e2b5
         /* Store the UID and lock_fd in the storage socket. This should be called with the socket pair lock taken. */
a3e2b5
-
a3e2b5
-        if (lock_fd >= 0) {
a3e2b5
-                struct cmsghdr *cmsg;
a3e2b5
-
a3e2b5
-                cmsg = CMSG_FIRSTHDR(&mh);
a3e2b5
-                cmsg->cmsg_level = SOL_SOCKET;
a3e2b5
-                cmsg->cmsg_type = SCM_RIGHTS;
a3e2b5
-                cmsg->cmsg_len = CMSG_LEN(sizeof(int));
a3e2b5
-                memcpy(CMSG_DATA(cmsg), &lock_fd, sizeof(int));
a3e2b5
-
a3e2b5
-                mh.msg_controllen = CMSG_SPACE(sizeof(int));
a3e2b5
-        } else {
a3e2b5
-                mh.msg_control = NULL;
a3e2b5
-                mh.msg_controllen = 0;
a3e2b5
-        }
a3e2b5
-
a3e2b5
-        k = sendmsg(d->storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
a3e2b5
-        if (k < 0)
a3e2b5
-                return -errno;
a3e2b5
-
a3e2b5
-        return 0;
a3e2b5
+        return send_one_fd_iov(d->storage_socket[1], lock_fd, &iov, 1, MSG_DONTWAIT);
a3e2b5
 }
a3e2b5
 
a3e2b5
 static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {