svn commit: r340631 - head/sys/compat/linux

Tijl Coosemans tijl at FreeBSD.org
Mon Nov 19 15:31:55 UTC 2018


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);
 }
 


More information about the svn-src-head mailing list