Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 26 Mar 2011 11:10:16 GMT
From:      dfilter@FreeBSD.ORG (dfilter service)
To:        freebsd-emulation@FreeBSD.org
Subject:   Re: kern/149168: commit references a PR
Message-ID:  <201103261110.p2QBAGfu036125@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR kern/149168; it has been noted by GNATS.

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/149168: commit references a PR
Date: Sat, 26 Mar 2011 11:06:08 +0000 (UTC)

 Author: avg
 Date: Sat Mar 26 11:05:53 2011
 New Revision: 220031
 URL: http://svn.freebsd.org/changeset/base/220031
 
 Log:
   linux compat: improve and fix sendmsg/recvmsg compatibility
   
   - implement baseic stubs for capget, capset, prctl PR_GET_KEEPCAPS
     and prctl PR_SET_KEEPCAPS.
   - add SCM_CREDS support to sendmsg and recvmsg
   - modify sendmsg to ignore control messages if not using UNIX
     domain sockets
   
   This should allow linux pulse audio daemon and client work on FreeBSD
   and interoperate with native counter-parts modulo the differences in
   pulseaudio versions.
   
   PR:		kern/149168
   Submitted by:	John Wehle <john@feith.com>
   Reviewed by:	netchild
   MFC after:	2 weeks
 
 Modified:
   head/sys/compat/linux/linux_misc.c
   head/sys/compat/linux/linux_misc.h
   head/sys/compat/linux/linux_socket.c
   head/sys/compat/linux/linux_socket.h
 
 Modified: head/sys/compat/linux/linux_misc.c
 ==============================================================================
 --- head/sys/compat/linux/linux_misc.c	Sat Mar 26 10:59:24 2011	(r220030)
 +++ head/sys/compat/linux/linux_misc.c	Sat Mar 26 11:05:53 2011	(r220031)
 @@ -1679,6 +1679,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)
  {
 @@ -1712,6 +1806,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: head/sys/compat/linux/linux_misc.h
 ==============================================================================
 --- head/sys/compat/linux/linux_misc.h	Sat Mar 26 10:59:24 2011	(r220030)
 +++ head/sys/compat/linux/linux_misc.h	Sat Mar 26 11:05:53 2011	(r220031)
 @@ -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: head/sys/compat/linux/linux_socket.c
 ==============================================================================
 --- head/sys/compat/linux/linux_socket.c	Sat Mar 26 10:59:24 2011	(r220030)
 +++ head/sys/compat/linux/linux_socket.c	Sat Mar 26 11:05:53 2011	(r220031)
 @@ -433,6 +433,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);
  }
 @@ -444,6 +446,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);
  }
 @@ -1092,6 +1114,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;
 @@ -1099,15 +1122,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
 @@ -1116,8 +1138,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,
 @@ -1128,13 +1154,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,
 @@ -1147,28 +1181,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;
 @@ -1193,9 +1257,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;
 @@ -1252,39 +1318,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;
 @@ -1295,11 +1357,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)
 @@ -1312,18 +1403,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: head/sys/compat/linux/linux_socket.h
 ==============================================================================
 --- head/sys/compat/linux/linux_socket.h	Sat Mar 26 10:59:24 2011	(r220030)
 +++ head/sys/compat/linux/linux_socket.h	Sat Mar 26 11:05:53 2011	(r220031)
 @@ -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) + \
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 



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