Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 9 Apr 2011 09:33:36 +0000 (UTC)
From:      Andriy Gapon <avg@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org
Subject:   svn commit: r220474 - stable/7/sys/compat/linux
Message-ID:  <201104090933.p399XaxL007848@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: avg
Date: Sat Apr  9 09:33:36 2011
New Revision: 220474
URL: http://svn.freebsd.org/changeset/base/220474

Log:
  MFC r220031: linux compat: improve and fix sendmsg/recvmsg compatibility
  
  PR:		kern/149168

Modified:
  stable/7/sys/compat/linux/linux_misc.c
  stable/7/sys/compat/linux/linux_misc.h
  stable/7/sys/compat/linux/linux_socket.c
  stable/7/sys/compat/linux/linux_socket.h
Directory Properties:
  stable/7/sys/   (props changed)
  stable/7/sys/cddl/contrib/opensolaris/   (props changed)
  stable/7/sys/contrib/dev/acpica/   (props changed)
  stable/7/sys/contrib/pf/   (props changed)

Modified: stable/7/sys/compat/linux/linux_misc.c
==============================================================================
--- stable/7/sys/compat/linux/linux_misc.c	Sat Apr  9 09:27:11 2011	(r220473)
+++ stable/7/sys/compat/linux/linux_misc.c	Sat Apr  9 09:33:36 2011	(r220474)
@@ -1633,6 +1633,100 @@ linux_exit_group(struct thread *td, stru
 	return (0);
 }
 
+#define _LINUX_CAPABILITY_VERSION  0x19980330
+
+struct l_user_cap_header {
+	l_int	version;
+	l_int	pid;
+};
+
+struct l_user_cap_data {
+	l_int	effective;
+	l_int	permitted;
+	l_int	inheritable;
+};
+
+int
+linux_capget(struct thread *td, struct linux_capget_args *args)
+{
+	struct l_user_cap_header luch;
+	struct l_user_cap_data lucd;
+	int error;
+
+	if (args->hdrp == NULL)
+		return (EFAULT);
+
+	error = copyin(args->hdrp, &luch, sizeof(luch));
+	if (error != 0)
+		return (error);
+
+	if (luch.version != _LINUX_CAPABILITY_VERSION) {
+		luch.version = _LINUX_CAPABILITY_VERSION;
+		error = copyout(&luch, args->hdrp, sizeof(luch));
+		if (error)
+			return (error);
+		return (EINVAL);
+	}
+
+	if (luch.pid)
+		return (EPERM);
+
+	if (args->datap) {
+		/*
+		 * The current implementation doesn't support setting
+		 * a capability (it's essentially a stub) so indicate
+		 * that no capabilities are currently set or available
+		 * to request.
+		 */
+		bzero (&lucd, sizeof(lucd));
+		error = copyout(&lucd, args->datap, sizeof(lucd));
+	}
+
+	return (error);
+}
+
+int
+linux_capset(struct thread *td, struct linux_capset_args *args)
+{
+	struct l_user_cap_header luch;
+	struct l_user_cap_data lucd;
+	int error;
+
+	if (args->hdrp == NULL || args->datap == NULL)
+		return (EFAULT);
+
+	error = copyin(args->hdrp, &luch, sizeof(luch));
+	if (error != 0)
+		return (error);
+
+	if (luch.version != _LINUX_CAPABILITY_VERSION) {
+		luch.version = _LINUX_CAPABILITY_VERSION;
+		error = copyout(&luch, args->hdrp, sizeof(luch));
+		if (error)
+			return (error);
+		return (EINVAL);
+	}
+
+	if (luch.pid)
+		return (EPERM);
+
+	error = copyin(args->datap, &lucd, sizeof(lucd));
+	if (error != 0)
+		return (error);
+
+	/* We currently don't support setting any capabilities. */
+	if (lucd.effective || lucd.permitted || lucd.inheritable) {
+		linux_msg(td,
+			  "capset effective=0x%x, permitted=0x%x, "
+			  "inheritable=0x%x is not implemented",
+			  (int)lucd.effective, (int)lucd.permitted,
+			  (int)lucd.inheritable);
+		return (EPERM);
+	}
+
+	return (0);
+}
+
 int
 linux_prctl(struct thread *td, struct linux_prctl_args *args)
 {
@@ -1666,6 +1760,21 @@ linux_prctl(struct thread *td, struct li
 		    (void *)(register_t)args->arg2,
 		    sizeof(pdeath_signal));
 		break;
+	case LINUX_PR_GET_KEEPCAPS:
+		/*
+		 * Indicate that we always clear the effective and
+		 * permitted capability sets when the user id becomes
+		 * non-zero (actually the capability sets are simply
+		 * always zero in the current implementation).
+		 */
+		td->td_retval[0] = 0;
+		break;
+	case LINUX_PR_SET_KEEPCAPS:
+		/*
+		 * Ignore requests to keep the effective and permitted
+		 * capability sets when the user id becomes non-zero.
+		 */
+		break;
 	case LINUX_PR_SET_NAME:
 		/*
 		 * To be on the safe side we need to make sure to not

Modified: stable/7/sys/compat/linux/linux_misc.h
==============================================================================
--- stable/7/sys/compat/linux/linux_misc.h	Sat Apr  9 09:27:11 2011	(r220473)
+++ stable/7/sys/compat/linux/linux_misc.h	Sat Apr  9 09:33:36 2011	(r220474)
@@ -37,6 +37,8 @@
 					 * Second arg is a ptr to return the
 					 * signal.
 					 */
+#define	LINUX_PR_GET_KEEPCAPS	7	/* Get drop capabilities on setuid */
+#define	LINUX_PR_SET_KEEPCAPS	8	/* Set drop capabilities on setuid */
 #define	LINUX_PR_SET_NAME	15	/* Set process name. */
 #define	LINUX_PR_GET_NAME	16	/* Get process name. */
 

Modified: stable/7/sys/compat/linux/linux_socket.c
==============================================================================
--- stable/7/sys/compat/linux/linux_socket.c	Sat Apr  9 09:27:11 2011	(r220473)
+++ stable/7/sys/compat/linux/linux_socket.c	Sat Apr  9 09:33:36 2011	(r220474)
@@ -431,6 +431,8 @@ linux_to_bsd_cmsg_type(int cmsg_type)
 	switch (cmsg_type) {
 	case LINUX_SCM_RIGHTS:
 		return (SCM_RIGHTS);
+	case LINUX_SCM_CREDENTIALS:
+		return (SCM_CREDS);
 	}
 	return (-1);
 }
@@ -442,6 +444,8 @@ bsd_to_linux_cmsg_type(int cmsg_type)
 	switch (cmsg_type) {
 	case SCM_RIGHTS:
 		return (LINUX_SCM_RIGHTS);
+	case SCM_CREDS:
+		return (LINUX_SCM_CREDENTIALS);
 	}
 	return (-1);
 }
@@ -459,7 +463,16 @@ linux_to_bsd_msghdr(struct msghdr *bhdr,
 	bhdr->msg_iov		= PTRIN(lhdr->msg_iov);
 	bhdr->msg_iovlen	= lhdr->msg_iovlen;
 	bhdr->msg_control	= PTRIN(lhdr->msg_control);
-	bhdr->msg_controllen	= lhdr->msg_controllen;
+
+	/*
+	 * msg_controllen is skipped since BSD and LINUX control messages
+	 * are potentially different sizes (e.g. the cred structure used
+	 * by SCM_CREDS is different between the two operating system).
+	 *
+	 * The caller can set it (if necessary) after converting all the
+	 * control messages.
+	 */
+
 	bhdr->msg_flags		= linux_to_bsd_msg_flags(lhdr->msg_flags);
 	return (0);
 }
@@ -472,7 +485,16 @@ bsd_to_linux_msghdr(const struct msghdr 
 	lhdr->msg_iov		= PTROUT(bhdr->msg_iov);
 	lhdr->msg_iovlen	= bhdr->msg_iovlen;
 	lhdr->msg_control	= PTROUT(bhdr->msg_control);
-	lhdr->msg_controllen	= bhdr->msg_controllen;
+
+	/*
+	 * msg_controllen is skipped since BSD and LINUX control messages
+	 * are potentially different sizes (e.g. the cred structure used
+	 * by SCM_CREDS is different between the two operating system).
+	 *
+	 * The caller can set it (if necessary) after converting all the
+	 * control messages.
+	 */
+
 	/* msg_flags skipped */
 	return (0);
 }
@@ -1034,6 +1056,7 @@ static int
 linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
 {
 	struct cmsghdr *cmsg;
+	struct cmsgcred cmcred;
 	struct mbuf *control;
 	struct msghdr msg;
 	struct l_cmsghdr linux_cmsg;
@@ -1041,15 +1064,14 @@ linux_sendmsg(struct thread *td, struct 
 	struct l_msghdr linux_msg;
 	struct iovec *iov;
 	socklen_t datalen;
+	struct sockaddr *sa;
+	sa_family_t sa_family;
 	void *data;
 	int error;
 
 	error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg));
 	if (error)
 		return (error);
-	error = linux_to_bsd_msghdr(&msg, &linux_msg);
-	if (error)
-		return (error);
 
 	/*
 	 * Some Linux applications (ping) define a non-NULL control data
@@ -1058,8 +1080,12 @@ linux_sendmsg(struct thread *td, struct 
 	 * order to handle this case.  This should be checked, but allows the
 	 * Linux ping to work.
 	 */
-	if (msg.msg_control != NULL && msg.msg_controllen == 0)
-		msg.msg_control = NULL;
+	if (PTRIN(linux_msg.msg_control) != NULL && linux_msg.msg_controllen == 0)
+		linux_msg.msg_control = PTROUT(NULL);
+
+	error = linux_to_bsd_msghdr(&msg, &linux_msg);
+	if (error)
+		return (error);
 
 #ifdef COMPAT_LINUX32
 	error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
@@ -1070,13 +1096,21 @@ linux_sendmsg(struct thread *td, struct 
 	if (error)
 		return (error);
 
-	if (msg.msg_control != NULL) {
+	control = NULL;
+	cmsg = NULL;
+
+	if ((ptr_cmsg = LINUX_CMSG_FIRSTHDR(&linux_msg)) != NULL) {
+		error = kern_getsockname(td, args->s, &sa, &datalen);
+		if (error)
+			goto bad;
+		sa_family = sa->sa_family;
+		free(sa, M_SONAME);
+
 		error = ENOBUFS;
 		cmsg = malloc(CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO);
 		control = m_get(M_WAIT, MT_CONTROL);
 		if (control == NULL)
 			goto bad;
-		ptr_cmsg = LINUX_CMSG_FIRSTHDR(&msg);
 
 		do {
 			error = copyin(ptr_cmsg, &linux_cmsg,
@@ -1089,28 +1123,58 @@ linux_sendmsg(struct thread *td, struct 
 				goto bad;
 
 			/*
-			 * Now we support only SCM_RIGHTS, so return EINVAL
-			 * in any other cmsg_type
+			 * Now we support only SCM_RIGHTS and SCM_CRED,
+			 * so return EINVAL in any other cmsg_type
 			 */
-			if ((cmsg->cmsg_type =
-			    linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type)) == -1)
-				goto bad;
+			cmsg->cmsg_type =
+			    linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type);
 			cmsg->cmsg_level =
 			    linux_to_bsd_sockopt_level(linux_cmsg.cmsg_level);
+			if (cmsg->cmsg_type == -1
+			    || cmsg->cmsg_level != SOL_SOCKET)
+				goto bad;
 
+			/*
+			 * Some applications (e.g. pulseaudio) attempt to
+			 * send ancillary data even if the underlying protocol
+			 * doesn't support it which is not allowed in the
+			 * FreeBSD system call interface.
+			 */
+			if (sa_family != AF_UNIX)
+				continue;
+
+			data = LINUX_CMSG_DATA(ptr_cmsg);
 			datalen = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
+
+			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;
+			}
+
 			cmsg->cmsg_len = CMSG_LEN(datalen);
-			data = LINUX_CMSG_DATA(ptr_cmsg);
 
 			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;
-		} while ((ptr_cmsg = LINUX_CMSG_NXTHDR(&msg, ptr_cmsg)));
-	} else {
-		control = NULL;
-		cmsg = NULL;
+		} while ((ptr_cmsg = LINUX_CMSG_NXTHDR(&linux_msg, ptr_cmsg)));
+
+		if (m_length(control, NULL) == 0) {
+			m_freem(control);
+			control = NULL;
+		}
 	}
 
 	msg.msg_iov = iov;
@@ -1135,9 +1199,11 @@ static int
 linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
 {
 	struct cmsghdr *cm;
+	struct cmsgcred *cmcred;
 	struct msghdr msg;
 	struct l_cmsghdr *linux_cmsg = NULL;
-	socklen_t datalen, outlen, clen;
+	struct l_ucred linux_ucred;
+	socklen_t datalen, outlen;
 	struct l_msghdr linux_msg;
 	struct iovec *iov, *uiov;
 	struct mbuf *control = NULL;
@@ -1194,39 +1260,35 @@ linux_recvmsg(struct thread *td, struct 
 			goto bad;
 	}
 
-	if (control) {
+	outbuf = PTRIN(linux_msg.msg_control);
+	outlen = 0;
 
+	if (control) {
 		linux_cmsg = malloc(L_CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO);
-		outbuf = PTRIN(linux_msg.msg_control);
-		cm = mtod(control, struct cmsghdr *);
-		outlen = 0;
-		clen = control->m_len;
 
-		while (cm != NULL) {
+		msg.msg_control = mtod(control, struct cmsghdr *);
+		msg.msg_controllen = control->m_len;
+
+		cm = CMSG_FIRSTHDR(&msg);
 
-			if ((linux_cmsg->cmsg_type =
-			    bsd_to_linux_cmsg_type(cm->cmsg_type)) == -1)
+		while (cm != NULL) {
+			linux_cmsg->cmsg_type =
+			    bsd_to_linux_cmsg_type(cm->cmsg_type);
+			linux_cmsg->cmsg_level =
+			    bsd_to_linux_sockopt_level(cm->cmsg_level);
+			if (linux_cmsg->cmsg_type == -1
+			    || cm->cmsg_level != SOL_SOCKET)
 			{
 				error = EINVAL;
 				goto bad;
 			}
+
 			data = CMSG_DATA(cm);
 			datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
 
-			switch (linux_cmsg->cmsg_type)
+			switch (cm->cmsg_type)
 			{
-			case LINUX_SCM_RIGHTS:
-				if (outlen + LINUX_CMSG_LEN(datalen) >
-				    linux_msg.msg_controllen) {
-					if (outlen == 0) {
-						error = EMSGSIZE;
-						goto bad;
-					} else {
-						linux_msg.msg_flags |=
-						    LINUX_MSG_CTRUNC;
-						goto out;
-					}
-				}
+			case SCM_RIGHTS:
 				if (args->flags & LINUX_MSG_CMSG_CLOEXEC) {
 					fds = datalen / sizeof(int);
 					fdp = data;
@@ -1237,11 +1299,40 @@ linux_recvmsg(struct thread *td, struct 
 					}
 				}
 				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);
+				break;
+			}
+
+			if (outlen + LINUX_CMSG_LEN(datalen) >
+			    linux_msg.msg_controllen) {
+				if (outlen == 0) {
+					error = EMSGSIZE;
+					goto bad;
+				} else {
+					linux_msg.msg_flags |=
+					    LINUX_MSG_CTRUNC;
+					goto out;
+				}
 			}
 
 			linux_cmsg->cmsg_len = LINUX_CMSG_LEN(datalen);
-			linux_cmsg->cmsg_level =
-			    bsd_to_linux_sockopt_level(cm->cmsg_level);
 
 			error = copyout(linux_cmsg, outbuf, L_CMSG_HDRSZ);
 			if (error)
@@ -1254,18 +1345,13 @@ linux_recvmsg(struct thread *td, struct 
 
 			outbuf += LINUX_CMSG_ALIGN(datalen);
 			outlen += LINUX_CMSG_LEN(datalen);
-			linux_msg.msg_controllen = outlen;
 
-			if (CMSG_SPACE(datalen) < clen) {
-				clen -= CMSG_SPACE(datalen);
-				cm = (struct cmsghdr *)
-				    ((caddr_t)cm + CMSG_SPACE(datalen));
-			} else
-				cm = NULL;
+			cm = CMSG_NXTHDR(&msg, cm);
 		}
 	}
 
 out:
+	linux_msg.msg_controllen = outlen;
 	error = copyout(&linux_msg, PTRIN(args->msg), sizeof(linux_msg));
 
 bad:

Modified: stable/7/sys/compat/linux/linux_socket.h
==============================================================================
--- stable/7/sys/compat/linux/linux_socket.h	Sat Apr  9 09:27:11 2011	(r220473)
+++ stable/7/sys/compat/linux/linux_socket.h	Sat Apr  9 09:33:36 2011	(r220474)
@@ -53,6 +53,7 @@
 /* Socket-level control message types */
 
 #define LINUX_SCM_RIGHTS	0x01
+#define LINUX_SCM_CREDENTIALS   0x02
 
 /* Ancilliary data object information macros */
 
@@ -66,13 +67,14 @@
 #define LINUX_CMSG_FIRSTHDR(msg) \
 				((msg)->msg_controllen >= \
 				    sizeof(struct l_cmsghdr) ? \
-				    (struct l_cmsghdr *)((msg)->msg_control) : \
+				    (struct l_cmsghdr *) \
+				        PTRIN((msg)->msg_control) : \
 				    (struct l_cmsghdr *)(NULL))
 #define LINUX_CMSG_NXTHDR(msg, cmsg) \
 				((((char *)(cmsg) + \
 				    LINUX_CMSG_ALIGN((cmsg)->cmsg_len) + \
 				    sizeof(*(cmsg))) > \
-				    (((char *)(msg)->msg_control) + \
+				    (((char *)PTRIN((msg)->msg_control)) + \
 				    (msg)->msg_controllen)) ? \
 				    (struct l_cmsghdr *) NULL : \
 				    (struct l_cmsghdr *)((char *)(cmsg) + \



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201104090933.p399XaxL007848>