The following reply was made to PR kern/149168; it has been noted by GNATS.

From: John Wehle <[email protected]>
To: [email protected]
Cc: [email protected], [email protected]
Subject: Re: kern/149168: [linux] [patch] Linux sendmsg / recvmsg / etc fixes 
for pulseaudio
Date: Tue, 8 Mar 2011 02:32:09 -0500 (EST)

 Enclosed is yet another slightly tweaked and lightly tested version.
 
 I made a bootable amd64 drive which mirrors my existing i386 system
 so I was able to test these changes on both amd64 and i386.
 
 Changes from previous:
 
   1) Include changes to amd64/linux32/linux32_dummy.c.
 
   2) Use PTRIN in LINUX_CMSG_FIRSTHDR and LINUX_CMSG_NXTHDR so
      linux_socket.c compiles without complaints on amd64.
 
   3) Invoke LINUX_CMSG_NXTHDR with the correct variable (basically
      I missed a place in my last round of changes).
 
 Notes:
 
   1) This has been tested on both i386 and amd64 with Fedora 10 paplay
      (client) talking to FreeBSD 8.2 pulseaudio (server) over both TCP
      and UNIX domain sockets.
 
   2) PulseAudio generates the socket name slightly differently between
      FreeBSD and Linux.  When using UNIX domain sockets please set
      PULSE_SERVER prior to invoking the client application.  E.g.:
 
        setenv PULSE_SERVER unix:/tmp/pulse-J1eO0ABCS0DM/native
 
      where /tmp/pulse-J1eO0ABCS0DM/native is the name of the FreeBSD
      PulseAudio socket.  Someone knowledgeable may be able to muck
 
        /usr/compat/linux/etc/pulse/client.conf
 
      so this is not necessary.
 
 -- John
 -----------------------8<----------------------------8<------------------
 --- ./compat/linux/linux_misc.h.ORIGINAL       2010-12-21 12:09:25.000000000 
-0500
 +++ ./compat/linux/linux_misc.h        2011-02-26 22:41:49.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-26 22:41:49.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-03-07 23:40:31.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 */
  
 @@ -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) + \
 --- ./compat/linux/linux_socket.c.ORIGINAL     2010-12-21 12:09:25.000000000 
-0500
 +++ ./compat/linux/linux_socket.c      2011-03-07 23:38:21.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,28 +1163,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 +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-26 22:41:49.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-26 22:41:49.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/linux32_dummy.c.ORIGINAL   2010-12-21 12:09:25.000000000 
-0500
 +++ ./amd64/linux32/linux32_dummy.c    2011-03-07 23:36:02.000000000 -0500
 @@ -54,8 +54,6 @@ DUMMY(sysfs);
  DUMMY(query_module);
  DUMMY(nfsservctl);
  DUMMY(rt_sigqueueinfo);
 -DUMMY(capget);
 -DUMMY(capset);
  DUMMY(sendfile);
  DUMMY(setfsuid);
  DUMMY(setfsgid);
 --- ./amd64/linux32/syscalls.master.ORIGINAL   2010-12-21 12:09:25.000000000 
-0500
 +++ ./amd64/linux32/syscalls.master    2011-02-26 22:41:49.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: [email protected]  |
 |    John Wehle    |     Fax: 1-215-540-5495  |                         |
 -------------------------------------------------------------------------
 
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-emulation
To unsubscribe, send any mail to "[email protected]"

Reply via email to