git: ac37e0edf97a - stable/13 - linux(4): Improve recvmsg() readability

From: Dmitry Chagin <dchagin_at_FreeBSD.org>
Date: Fri, 17 Jun 2022 19:41:12 UTC
The branch stable/13 has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=ac37e0edf97ace6a2dfc8271af94bdb096880cc2

commit ac37e0edf97ace6a2dfc8271af94bdb096880cc2
Author:     Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2022-05-28 20:44:02 +0000
Commit:     Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2022-06-17 19:35:34 +0000

    linux(4): Improve recvmsg() readability
    
    To improve recvmsg() readability SCM_ handlers moved to a separate
    functions.
    
    MFC after:              2 weeks
    
    (cherry picked from commit b408788d6b7edf91a6c4b9e2e8d1b34fe5d41525)
---
 sys/compat/linux/linux_socket.c | 154 +++++++++++++++++++++++++---------------
 1 file changed, 95 insertions(+), 59 deletions(-)

diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c
index 71f958b57298..4dee89d1ebd6 100644
--- a/sys/compat/linux/linux_socket.c
+++ b/sys/compat/linux/linux_socket.c
@@ -1532,26 +1532,90 @@ linux_sendmmsg(struct thread *td, struct linux_sendmmsg_args *args)
 	return (error);
 }
 
+static int
+recvmsg_scm_rights(struct thread *td, l_uint flags, socklen_t *datalen,
+    void **data, void **udata)
+{
+	int i, fd, fds, *fdp;
+
+	if (flags & LINUX_MSG_CMSG_CLOEXEC) {
+		fds = *datalen / sizeof(int);
+		fdp = *data;
+		for (i = 0; i < fds; i++) {
+			fd = *fdp++;
+			(void)kern_fcntl(td, fd, F_SETFD, FD_CLOEXEC);
+		}
+	}
+	return (0);
+}
+
+static int
+recvmsg_scm_creds(socklen_t *datalen, void **data, void **udata)
+{
+	struct cmsgcred *cmcred;
+	struct l_ucred lu;
+
+	cmcred = *data;
+	lu.pid = cmcred->cmcred_pid;
+	lu.uid = cmcred->cmcred_uid;
+	lu.gid = cmcred->cmcred_gid;
+	memmove(*data, &lu, sizeof(lu));
+	*datalen = sizeof(lu);
+	return (0);
+}
+_Static_assert(sizeof(struct cmsgcred) >= sizeof(struct l_ucred),
+    "scm_creds sizeof l_ucred");
+
+static int
+recvmsg_scm_creds2(socklen_t *datalen, void **data, void **udata)
+{
+	struct sockcred2 *scred;
+	struct l_ucred lu;
+
+	scred = *data;
+	lu.pid = scred->sc_pid;
+	lu.uid = scred->sc_uid;
+	lu.gid = scred->sc_gid;
+	memmove(*data, &lu, sizeof(lu));
+	*datalen = sizeof(lu);
+	return (0);
+}
+_Static_assert(sizeof(struct sockcred2) >= sizeof(struct l_ucred),
+    "scm_creds2 sizeof l_ucred");
+
+static int
+recvmsg_scm_timestamp(socklen_t *datalen, void **data, void **udata)
+{
+	struct timeval *ftmvl;
+	l_timeval *ltv;
+
+	if (*datalen != sizeof(struct timeval))
+		return (EMSGSIZE);
+
+	ftmvl = *data;
+	ltv = malloc(sizeof(*ltv), M_LINUX, M_WAITOK);
+	ltv->tv_sec = ftmvl->tv_sec;
+	ltv->tv_usec = ftmvl->tv_usec;
+	*data = *udata = ltv;
+	*datalen = sizeof(*ltv);
+	return (0);
+}
+
 static int
 linux_recvmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
     l_uint flags, struct msghdr *msg)
 {
 	struct cmsghdr *cm;
-	struct cmsgcred *cmcred;
-	struct sockcred2 *scred;
 	struct l_cmsghdr *linux_cmsg = NULL;
-	struct l_ucred linux_ucred;
 	socklen_t datalen, maxlen, outlen;
 	struct l_msghdr linux_msghdr;
 	struct iovec *iov, *uiov;
 	struct mbuf *control = NULL;
 	struct mbuf **controlp;
-	struct timeval *ftmvl;
 	struct sockaddr *sa;
-	l_timeval ltmvl;
 	caddr_t outbuf;
-	void *data;
-	int error, i, fd, fds, *fdp;
+	void *data, *udata;
+	int error;
 
 	error = copyin(msghdr, &linux_msghdr, sizeof(linux_msghdr));
 	if (error != 0)
@@ -1635,69 +1699,40 @@ linux_recvmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
 
 			data = CMSG_DATA(cm);
 			datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
+			udata = NULL;
 
 			switch (cm->cmsg_type) {
 			case SCM_RIGHTS:
-				if (flags & LINUX_MSG_CMSG_CLOEXEC) {
-					fds = datalen / sizeof(int);
-					fdp = data;
-					for (i = 0; i < fds; i++) {
-						fd = *fdp++;
-						(void)kern_fcntl(td, fd,
-						    F_SETFD, FD_CLOEXEC);
-					}
-				}
+				error = recvmsg_scm_rights(td, flags,
+				    &datalen, &data, &udata);
 				break;
 
 			case SCM_CREDS:
-				/*
-				 * Currently LOCAL_CREDS is never in
-				 * effect for Linux so no need to worry
-				 * about sockcred
-				 */
-				if (datalen != sizeof(*cmcred)) {
-					error = EMSGSIZE;
-					goto bad;
-				}
-				cmcred = (struct cmsgcred *)data;
-				bzero(&linux_ucred, sizeof(linux_ucred));
-				linux_ucred.pid = cmcred->cmcred_pid;
-				linux_ucred.uid = cmcred->cmcred_uid;
-				linux_ucred.gid = cmcred->cmcred_gid;
-				data = &linux_ucred;
-				datalen = sizeof(linux_ucred);
+				error = recvmsg_scm_creds(&datalen,
+				    &data, &udata);
 				break;
 
 			case SCM_CREDS2:
-				scred = data;
-				bzero(&linux_ucred, sizeof(linux_ucred));
-				linux_ucred.pid = scred->sc_pid;
-				linux_ucred.uid = scred->sc_uid;
-				linux_ucred.gid = scred->sc_gid;
-				data = &linux_ucred;
-				datalen = sizeof(linux_ucred);
+				error = recvmsg_scm_creds2(&datalen,
+				    &data, &udata);
 				break;
 
 			case SCM_TIMESTAMP:
-				if (datalen != sizeof(struct timeval)) {
-					error = EMSGSIZE;
-					goto bad;
-				}
-				ftmvl = (struct timeval *)data;
-				ltmvl.tv_sec = ftmvl->tv_sec;
-				ltmvl.tv_usec = ftmvl->tv_usec;
-				data = &ltmvl;
-				datalen = sizeof(ltmvl);
+				error = recvmsg_scm_timestamp(&datalen,
+				    &data, &udata);
 				break;
 			}
+			if (error != 0)
+				goto bad;
 
 			if (outlen + LINUX_CMSG_LEN(datalen) > maxlen) {
 				if (outlen == 0) {
 					error = EMSGSIZE;
-					goto bad;
+					goto err;
 				} else {
 					linux_msghdr.msg_flags |= LINUX_MSG_CTRUNC;
 					m_dispose_extcontrolm(control);
+					free(udata, M_LINUX);
 					goto out;
 				}
 			}
@@ -1705,18 +1740,19 @@ linux_recvmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
 			linux_cmsg->cmsg_len = LINUX_CMSG_LEN(datalen);
 
 			error = copyout(linux_cmsg, outbuf, L_CMSG_HDRSZ);
+			if (error == 0) {
+				outbuf += L_CMSG_HDRSZ;
+				error = copyout(data, outbuf, datalen);
+				if (error == 0) {
+					outbuf += LINUX_CMSG_ALIGN(datalen);
+					outlen += LINUX_CMSG_LEN(datalen);
+					cm = CMSG_NXTHDR(msg, cm);
+				}
+			}
+err:
+			free(udata, M_LINUX);
 			if (error != 0)
 				goto bad;
-			outbuf += L_CMSG_HDRSZ;
-
-			error = copyout(data, outbuf, datalen);
-			if (error != 0)
-				goto bad;
-
-			outbuf += LINUX_CMSG_ALIGN(datalen);
-			outlen += LINUX_CMSG_LEN(datalen);
-
-			cm = CMSG_NXTHDR(msg, cm);
 		}
 		linux_msghdr.msg_controllen = outlen;
 	}