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], [email protected]
Subject: Re: kern/149168: [linux] [patch] Linux sendmsg / recvmsg / etc fixes 
for pulseaudio
Date: Mon, 21 Mar 2011 01:27:41 -0400 (EDT)

 Enclosed is yet another slightly tweaked and lightly tested version.
 
 Changes from previous:
 
   1) Address some style issues.
 
   2) Add additional comments.
 
 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.
 
 -- John
 -----------------------8<----------------------------8<------------------
 --- ./compat/linux/linux_misc.h.ORIGINAL       2010-12-21 12:09:25.000000000 
-0500
 +++ ./compat/linux/linux_misc.h        2011-03-07 23:54:17.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-03-18 00:02:48.000000000 -0400
 @@ -1733,6 +1733,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)
  {
 @@ -1766,6 +1860,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
 --- ./compat/linux/linux_socket.h.ORIGINAL     2010-12-21 12:09:25.000000000 
-0500
 +++ ./compat/linux/linux_socket.h      2011-03-07 23:54:17.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-17 23:35:37.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);
  }
 @@ -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:
 --- ./i386/linux/linux_dummy.c.ORIGINAL        2010-12-21 12:09:25.000000000 
-0500
 +++ ./i386/linux/linux_dummy.c 2011-03-07 23:54:17.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-03-17 23:46:17.000000000 -0400
 @@ -329,8 +329,10 @@
                                    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(struct l_user_cap_header 
*hdrp, \
 +                                  struct l_user_cap_data *datap); }
 +185   AUE_CAPSET      STD     { int linux_capset(struct l_user_cap_header 
*hdrp, \
 +                                  struct l_user_cap_data *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:54:17.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-03-17 23:47:06.000000000 -0400
 @@ -327,8 +327,10 @@
                                    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(struct l_user_cap_header 
*hdrp, \
 +                                  struct l_user_cap_data *datap); }
 +185   AUE_CAPSET      STD     { int linux_capset(struct l_user_cap_header 
*hdrp, \
 +                                  struct l_user_cap_data *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  |                         |
 -------------------------------------------------------------------------
 -------------------------------------------------------------------------
 |   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