Author: tijl
Date: Mon Nov 19 15:31:54 2018
New Revision: 340631
URL: https://svnweb.freebsd.org/changeset/base/340631

Log:
  Do proper copyin of control message data in the Linux sendmsg syscall.
  
  Instead of calling m_append with a user address, allocate an mbuf cluster
  and copy data into it using copyin.  For the SCM_CREDS case, instead of
  zeroing a stack variable and appending that to the mbuf, zero part of the
  mbuf cluster directly.  One mbuf cluster is also the size limit used by
  the FreeBSD sendmsg syscall (uipc_syscalls.c:sockargs()).
  
  PR:           217901
  Reviewed by:  kib
  MFC after:    3 days

Modified:
  head/sys/compat/linux/linux_socket.c

Modified: head/sys/compat/linux/linux_socket.c
==============================================================================
--- head/sys/compat/linux/linux_socket.c        Mon Nov 19 15:29:40 2018        
(r340630)
+++ head/sys/compat/linux/linux_socket.c        Mon Nov 19 15:31:54 2018        
(r340631)
@@ -1085,7 +1085,6 @@ linux_sendmsg_common(struct thread *td, l_int s, struc
     l_uint flags)
 {
        struct cmsghdr *cmsg;
-       struct cmsgcred cmcred;
        struct mbuf *control;
        struct msghdr msg;
        struct l_cmsghdr linux_cmsg;
@@ -1096,6 +1095,7 @@ linux_sendmsg_common(struct thread *td, l_int s, struc
        struct sockaddr *sa;
        sa_family_t sa_family;
        void *data;
+       l_size_t len;
        int error;
 
        error = copyin(msghdr, &linux_msg, sizeof(linux_msg));
@@ -1126,7 +1126,6 @@ linux_sendmsg_common(struct thread *td, l_int s, struc
                return (error);
 
        control = NULL;
-       cmsg = NULL;
 
        if ((ptr_cmsg = LINUX_CMSG_FIRSTHDR(&linux_msg)) != NULL) {
                error = kern_getsockname(td, s, &sa, &datalen);
@@ -1136,8 +1135,10 @@ linux_sendmsg_common(struct thread *td, l_int s, struc
                free(sa, M_SONAME);
 
                error = ENOBUFS;
-               cmsg = malloc(CMSG_HDRSZ, M_LINUX, M_WAITOK|M_ZERO);
                control = m_get(M_WAITOK, MT_CONTROL);
+               MCLGET(control, M_WAITOK);
+               data = mtod(control, void *);
+               datalen = 0;
 
                do {
                        error = copyin(ptr_cmsg, &linux_cmsg,
@@ -1149,10 +1150,14 @@ linux_sendmsg_common(struct thread *td, l_int s, struc
                        if (linux_cmsg.cmsg_len < sizeof(struct l_cmsghdr))
                                goto bad;
 
+                       if (datalen + CMSG_HDRSZ > MCLBYTES)
+                               goto bad;
+
                        /*
                         * Now we support only SCM_RIGHTS and SCM_CRED,
                         * so return EINVAL in any other cmsg_type
                         */
+                       cmsg = data;
                        cmsg->cmsg_type =
                            linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type);
                        cmsg->cmsg_level =
@@ -1170,35 +1175,34 @@ linux_sendmsg_common(struct thread *td, l_int s, struc
                        if (sa_family != AF_UNIX)
                                continue;
 
-                       data = LINUX_CMSG_DATA(ptr_cmsg);
-                       datalen = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
+                       if (cmsg->cmsg_type == SCM_CREDS) {
+                               len = sizeof(struct cmsgcred);
+                               if (datalen + CMSG_SPACE(len) > MCLBYTES)
+                                       goto bad;
 
-                       switch (cmsg->cmsg_type)
-                       {
-                       case SCM_RIGHTS:
-                               break;
-
-                       case SCM_CREDS:
-                               data = &cmcred;
-                               datalen = sizeof(cmcred);
-
                                /*
                                 * The lower levels will fill in the structure
                                 */
-                               bzero(data, datalen);
-                               break;
+                               memset(CMSG_DATA(data), 0, len);
+                       } else {
+                               len = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
+                               if (datalen + CMSG_SPACE(len) < datalen ||
+                                   datalen + CMSG_SPACE(len) > MCLBYTES)
+                                       goto bad;
+
+                               error = copyin(LINUX_CMSG_DATA(ptr_cmsg),
+                                   CMSG_DATA(data), len);
+                               if (error != 0)
+                                       goto bad;
                        }
 
-                       cmsg->cmsg_len = CMSG_LEN(datalen);
-
-                       error = ENOBUFS;
-                       if (!m_append(control, CMSG_HDRSZ, (c_caddr_t)cmsg))
-                               goto bad;
-                       if (!m_append(control, datalen, (c_caddr_t)data))
-                               goto bad;
+                       cmsg->cmsg_len = CMSG_LEN(len);
+                       data = (char *)data + CMSG_SPACE(len);
+                       datalen += CMSG_SPACE(len);
                } while ((ptr_cmsg = LINUX_CMSG_NXTHDR(&linux_msg, ptr_cmsg)));
 
-               if (m_length(control, NULL) == 0) {
+               control->m_len = datalen;
+               if (datalen == 0) {
                        m_freem(control);
                        control = NULL;
                }
@@ -1212,8 +1216,6 @@ linux_sendmsg_common(struct thread *td, l_int s, struc
 bad:
        m_freem(control);
        free(iov, M_IOV);
-       if (cmsg)
-               free(cmsg, M_LINUX);
        return (error);
 }
 
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to