kern/149168: [linux] [patch] Linux sendmsg / recvmsg / etc
fixes for pulseaudio
John Wehle
john at feith.com
Sun Feb 13 06:00:25 UTC 2011
The following reply was made to PR kern/149168; it has been noted by GNATS.
From: John Wehle <john at feith.com>
To: avg at FreeBSD.org
Cc: rdivacky at FreeBSD.org, bug-followup at FreeBSD.org
Subject: Re: kern/149168: [linux] [patch] Linux sendmsg / recvmsg / etc fixes for pulseaudio
Date: Sun, 13 Feb 2011 01:59:43 -0500 (EST)
Enclosed is a slightly tweaked and lightly tested version.
Changes from previous:
1) Modify linux/syscalls.master in i386 & amd64 instead of mucking a
generated file.
2) For symmetry also ignore msg_controllen in linux_to_bsd_msghdr.
-- John
-----------------------8<----------------------------8<------------------
--- ./compat/linux/linux_misc.h.ORIGINAL 2010-12-21 12:09:25.000000000 -0500
+++ ./compat/linux/linux_misc.h 2011-02-12 22:36:57.000000000 -0500
@@ -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-12-21 12:09:25.000000000 -0500
+++ ./compat/linux/linux_misc.c 2011-02-12 22:36:57.000000000 -0500
@@ -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-12-21 12:09:25.000000000 -0500
+++ ./compat/linux/linux_socket.h 2011-02-12 22:36:57.000000000 -0500
@@ -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-12-21 12:09:25.000000000 -0500
+++ ./compat/linux/linux_socket.c 2011-02-13 00:22:58.000000000 -0500
@@ -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,7 @@ 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 skipped */
bhdr->msg_flags = linux_to_bsd_msg_flags(lhdr->msg_flags);
return (0);
}
@@ -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,15 +1104,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 +1120,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 +1136,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,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_dummy.c.ORIGINAL 2010-12-21 12:09:25.000000000 -0500
+++ ./i386/linux/linux_dummy.c 2011-02-12 22:36:57.000000000 -0500
@@ -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);
--- ./i386/linux/syscalls.master.ORIGINAL 2010-12-21 12:09:25.000000000 -0500
+++ ./i386/linux/syscalls.master 2011-02-12 22:50:39.000000000 -0500
@@ -329,8 +329,8 @@
l_uid16_t uid, l_gid16_t gid); }
183 AUE_GETCWD STD { int linux_getcwd(char *buf, \
l_ulong bufsize); }
-184 AUE_CAPGET STD { int linux_capget(void); }
-185 AUE_CAPSET STD { int linux_capset(void); }
+184 AUE_CAPGET STD { int linux_capget(void *hdrp, void *datap); }
+185 AUE_CAPSET STD { int linux_capset(void *hdrp, const void *datap); }
186 AUE_NULL STD { int linux_sigaltstack(l_stack_t *uss, \
l_stack_t *uoss); }
187 AUE_SENDFILE STD { int linux_sendfile(void); }
--- ./amd64/linux32/syscalls.master.ORIGINAL 2010-12-21 12:09:25.000000000 -0500
+++ ./amd64/linux32/syscalls.master 2011-02-12 22:50:35.000000000 -0500
@@ -327,8 +327,8 @@
l_uid16_t uid, l_gid16_t gid); }
183 AUE_GETCWD STD { int linux_getcwd(char *buf, \
l_ulong bufsize); }
-184 AUE_CAPGET STD { int linux_capget(void); }
-185 AUE_CAPSET STD { int linux_capset(void); }
+184 AUE_CAPGET STD { int linux_capget(void *hdrp, void *datap); }
+185 AUE_CAPSET STD { int linux_capset(void *hdrp, const void *datap); }
186 AUE_NULL STD { int linux_sigaltstack(l_stack_t *uss, \
l_stack_t *uoss); }
187 AUE_SENDFILE STD { int linux_sendfile(void); }
-------------------------------------------------------------------------
| Feith Systems | Voice: 1-215-646-8000 | Email: john at feith.com |
| John Wehle | Fax: 1-215-540-5495 | |
-------------------------------------------------------------------------
More information about the freebsd-emulation
mailing list