From owner-freebsd-bugs@FreeBSD.ORG Sun Aug 1 04:30:04 2010 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id E4FA5106567A for ; Sun, 1 Aug 2010 04:30:04 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id C26028FC15 for ; Sun, 1 Aug 2010 04:30:04 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.4/8.14.4) with ESMTP id o714U4ck075432 for ; Sun, 1 Aug 2010 04:30:04 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.4/8.14.4/Submit) id o714U4kk075431; Sun, 1 Aug 2010 04:30:04 GMT (envelope-from gnats) Resent-Date: Sun, 1 Aug 2010 04:30:04 GMT Resent-Message-Id: <201008010430.o714U4kk075431@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, John Wehle Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id B8390106566B for ; Sun, 1 Aug 2010 04:20:14 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21]) by mx1.freebsd.org (Postfix) with ESMTP id A77748FC13 for ; Sun, 1 Aug 2010 04:20:14 +0000 (UTC) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.14.3/8.14.3) with ESMTP id o714KEA9062690 for ; Sun, 1 Aug 2010 04:20:14 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.14.3/8.14.3/Submit) id o714KEMn062689; Sun, 1 Aug 2010 04:20:14 GMT (envelope-from nobody) Message-Id: <201008010420.o714KEMn062689@www.freebsd.org> Date: Sun, 1 Aug 2010 04:20:14 GMT From: John Wehle To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Cc: Subject: kern/149168: Linux sendmsg / recvmsg / etc fixes for pulseaudio X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 01 Aug 2010 04:30:05 -0000 >Number: 149168 >Category: kern >Synopsis: Linux sendmsg / recvmsg / etc fixes for pulseaudio >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Sun Aug 01 04:30:04 UTC 2010 >Closed-Date: >Last-Modified: >Originator: John Wehle >Release: 8.1 >Organization: >Environment: FreeBSD wagner.FEITH.COM 8.1-RC2 FreeBSD 8.1-RC2 #0: Thu Jul 29 03:35:35 EDT 2010 root@wagner.FEITH.COM:/usr/obj/usr/src/sys/CUSTOM i386 >Description: The enclosed lightly tested patch extends the Linux emulation so that pulseaudio runs. Specifically tested: a) Fedora 10 paplay (client) talking to FreeBSD 8.1 pulseaudio (server) over both TCP and UNIX domain sockets. b) FreeBSD 8.1 paplay (client) talking to Fedora 10 pulseaudio (server) over both TCP and UNIX domain sockets. c) Fedora 10 paplay (client) talking to Fedora 10 pulseaudio (server) over both TCP and UNIX domain sockets. Changes: 1) Implement NO-OP stubs for capget, capset, prctl PR_GET_KEEPCAPS, and prctl PR_SET_KEEPCAPS so that the pulseaudio server will start. 2) Added SCM_CREDS support to sendmsg and recvmsg. 3) Modify sendmsg to ignore control messages if not using UNIX domain sockets. >How-To-Repeat: Install the Fedora 10 pulseaudio client / server software and try using paplay. >Fix: Patch attached with submission follows: --- ./compat/linux/linux_misc.h.ORIGINAL 2010-06-13 22:09:06.000000000 -0400 +++ ./compat/linux/linux_misc.h 2010-07-31 23:33:18.000000000 -0400 @@ -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. */ --- ./compat/linux/linux_misc.c.ORIGINAL 2010-06-13 22:09:06.000000000 -0400 +++ ./compat/linux/linux_misc.c 2010-07-31 00:09:16.000000000 -0400 @@ -1733,6 +1733,87 @@ 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) + 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) { + 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 || ! args->datap) + 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); + + if (lucd.effective || lucd.permitted || lucd.inheritable) + return (EPERM); + + return (0); +} + int linux_prctl(struct thread *td, struct linux_prctl_args *args) { @@ -1766,6 +1847,11 @@ linux_prctl(struct thread *td, struct li (void *)(register_t)args->arg2, sizeof(pdeath_signal)); break; + case LINUX_PR_GET_KEEPCAPS: + td->td_retval[0] = 0; + break; + case LINUX_PR_SET_KEEPCAPS: + break; case LINUX_PR_SET_NAME: /* * To be on the safe side we need to make sure to not --- ./compat/linux/linux_socket.h.ORIGINAL 2010-06-13 22:09:06.000000000 -0400 +++ ./compat/linux/linux_socket.h 2010-07-28 23:44:10.000000000 -0400 @@ -53,6 +53,7 @@ /* Socket-level control message types */ #define LINUX_SCM_RIGHTS 0x01 +#define LINUX_SCM_CREDENTIALS 0x02 /* Ancilliary data object information macros */ --- ./compat/linux/linux_socket.c.ORIGINAL 2010-07-28 23:45:26.000000000 -0400 +++ ./compat/linux/linux_socket.c 2010-07-31 23:56:08.000000000 -0400 @@ -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); } @@ -472,7 +476,7 @@ 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 skipped */ /* msg_flags skipped */ return (0); } @@ -1092,6 +1096,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,6 +1104,8 @@ 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; @@ -1128,7 +1135,16 @@ linux_sendmsg(struct thread *td, struct if (error) return (error); + control = NULL; + cmsg = NULL; + if (msg.msg_control != 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); @@ -1147,18 +1163,46 @@ 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)) @@ -1166,9 +1210,11 @@ linux_sendmsg(struct thread *td, struct 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; + + if (m_length(control, NULL) == 0) { + m_freem(control); + control = NULL; + } } msg.msg_iov = iov; @@ -1193,9 +1239,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 +1300,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 +1339,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 +1385,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: --- ./i386/linux/linux_proto.h.ORIGINAL 2010-06-13 22:09:06.000000000 -0400 +++ ./i386/linux/linux_proto.h 2010-07-30 23:33:15.000000000 -0400 @@ -586,10 +586,12 @@ struct linux_getcwd_args { char bufsize_l_[PADL_(l_ulong)]; l_ulong bufsize; char bufsize_r_[PADR_(l_ulong)]; }; struct linux_capget_args { - register_t dummy; + char hdrp_l_[PADL_(void *)]; void * hdrp; char hdrp_r_[PADR_(void *)]; + char datap_l_[PADL_(void *)]; void * datap; char datap_r_[PADR_(void *)]; }; struct linux_capset_args { - register_t dummy; + char hdrp_l_[PADL_(void *)]; void * hdrp; char hdrp_r_[PADR_(void *)]; + char datap_l_[PADL_(void *)]; void * datap; char datap_r_[PADR_(void *)]; }; struct linux_sigaltstack_args { char uss_l_[PADL_(l_stack_t *)]; l_stack_t * uss; char uss_r_[PADR_(l_stack_t *)]; --- ./i386/linux/linux_dummy.c.ORIGINAL 2010-06-13 22:09:06.000000000 -0400 +++ ./i386/linux/linux_dummy.c 2010-07-30 23:33:46.000000000 -0400 @@ -57,8 +57,6 @@ DUMMY(vm86); DUMMY(query_module); DUMMY(nfsservctl); DUMMY(rt_sigqueueinfo); -DUMMY(capget); -DUMMY(capset); DUMMY(sendfile); /* different semantics */ DUMMY(setfsuid); DUMMY(setfsgid); >Release-Note: >Audit-Trail: >Unformatted: