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

From: [email protected] (dfilter service)
To: [email protected]
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 <[email protected]>
   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) + \
 _______________________________________________
 [email protected] mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "[email protected]"
 
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-emulation
To unsubscribe, send any mail to "[email protected]"

Reply via email to