Print this page
OS-3752 Increase IOV_MAX to at least 1024
OS-3404 lx brand must support sendmsg() with IOV_MAX of 1024
        
*** 19,28 ****
--- 19,30 ----
   * CDDL HEADER END
   */
  
  /*
   * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+  * Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
+  * Copyright 2015, Joyent, Inc.  All rights reserved.
   */
  
  /* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. */
  /*
   * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*** 52,61 ****
--- 54,64 ----
  #include <sys/flock.h>
  #include <sys/modctl.h>
  #include <sys/cmn_err.h>
  #include <sys/vmsystm.h>
  #include <sys/policy.h>
+ #include <sys/limits.h>
  
  #include <sys/socket.h>
  #include <sys/socketvar.h>
  
  #include <sys/isa_defs.h>
*** 84,99 ****
  
  extern void     nl7c_init(void);
  extern int      sockfs_defer_nl7c_init;
  
  /*
-  * Note: DEF_IOV_MAX is defined and used as it is in "fs/vncalls.c"
-  *       as there isn't a formal definition of IOV_MAX ???
-  */
- #define MSG_MAXIOVLEN   16
- 
- /*
   * Kernel component of socket creation.
   *
   * The socket library determines which version number to use.
   * First the library calls this with a NULL devpath. If this fails
   * to find a transport (using solookup) the library will look in /etc/netconfig
--- 87,96 ----
*** 1024,1036 ****
  {
          STRUCT_DECL(nmsghdr, u_lmsg);
          STRUCT_HANDLE(nmsghdr, umsgptr);
          struct nmsghdr lmsg;
          struct uio auio;
!         struct iovec aiov[MSG_MAXIOVLEN];
          int iovcnt;
!         ssize_t len;
          int i;
          int *flagsp;
          model_t model;
  
          dprint(1, ("recvmsg(%d, %p, %d)\n",
--- 1021,1034 ----
  {
          STRUCT_DECL(nmsghdr, u_lmsg);
          STRUCT_HANDLE(nmsghdr, umsgptr);
          struct nmsghdr lmsg;
          struct uio auio;
!         struct iovec buf[IOV_MAX_STACK], *aiov = buf;
!         ssize_t iovsize = 0;
          int iovcnt;
!         ssize_t len, rval;
          int i;
          int *flagsp;
          model_t model;
  
          dprint(1, ("recvmsg(%d, %p, %d)\n",
*** 1069,1117 ****
          lmsg.msg_controllen = STRUCT_FGET(u_lmsg, msg_controllen);
          lmsg.msg_flags = STRUCT_FGET(u_lmsg, msg_flags);
  
          iovcnt = lmsg.msg_iovlen;
  
!         if (iovcnt <= 0 || iovcnt > MSG_MAXIOVLEN) {
                  return (set_errno(EMSGSIZE));
          }
  
  #ifdef _SYSCALL32_IMPL
          /*
           * 32-bit callers need to have their iovec expanded, while ensuring
           * that they can't move more than 2Gbytes of data in a single call.
           */
          if (model == DATAMODEL_ILP32) {
!                 struct iovec32 aiov32[MSG_MAXIOVLEN];
                  ssize32_t count32;
  
!                 if (copyin((struct iovec32 *)lmsg.msg_iov, aiov32,
!                     iovcnt * sizeof (struct iovec32)))
                          return (set_errno(EFAULT));
  
                  count32 = 0;
                  for (i = 0; i < iovcnt; i++) {
                          ssize32_t iovlen32;
  
                          iovlen32 = aiov32[i].iov_len;
                          count32 += iovlen32;
!                         if (iovlen32 < 0 || count32 < 0)
                                  return (set_errno(EINVAL));
                          aiov[i].iov_len = iovlen32;
                          aiov[i].iov_base =
                              (caddr_t)(uintptr_t)aiov32[i].iov_base;
                  }
          } else
  #endif /* _SYSCALL32_IMPL */
          if (copyin(lmsg.msg_iov, aiov, iovcnt * sizeof (struct iovec))) {
                  return (set_errno(EFAULT));
          }
          len = 0;
          for (i = 0; i < iovcnt; i++) {
                  ssize_t iovlen = aiov[i].iov_len;
                  len += iovlen;
                  if (iovlen < 0 || len < 0) {
                          return (set_errno(EINVAL));
                  }
          }
          auio.uio_loffset = 0;
          auio.uio_iov = aiov;
--- 1067,1146 ----
          lmsg.msg_controllen = STRUCT_FGET(u_lmsg, msg_controllen);
          lmsg.msg_flags = STRUCT_FGET(u_lmsg, msg_flags);
  
          iovcnt = lmsg.msg_iovlen;
  
!         if (iovcnt <= 0 || iovcnt > IOV_MAX) {
                  return (set_errno(EMSGSIZE));
          }
  
+         if (iovcnt > IOV_MAX_STACK) {
+                 iovsize = iovcnt * sizeof (struct iovec);
+                 aiov = kmem_alloc(iovsize, KM_SLEEP);
+         }
+ 
  #ifdef _SYSCALL32_IMPL
          /*
           * 32-bit callers need to have their iovec expanded, while ensuring
           * that they can't move more than 2Gbytes of data in a single call.
           */
          if (model == DATAMODEL_ILP32) {
!                 struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
!                 ssize_t iov32size;
                  ssize32_t count32;
  
!                 iov32size = iovcnt * sizeof (struct iovec32);
!                 if (iovsize != 0)
!                         aiov32 = kmem_alloc(iov32size, KM_SLEEP);
! 
!                 if (copyin((struct iovec32 *)lmsg.msg_iov, aiov32, iov32size)) {
!                         if (iovsize != 0) {
!                                 kmem_free(aiov32, iov32size);
!                                 kmem_free(aiov, iovsize);
!                         }
! 
                          return (set_errno(EFAULT));
+                 }
  
                  count32 = 0;
                  for (i = 0; i < iovcnt; i++) {
                          ssize32_t iovlen32;
  
                          iovlen32 = aiov32[i].iov_len;
                          count32 += iovlen32;
!                         if (iovlen32 < 0 || count32 < 0) {
!                                 if (iovsize != 0) {
!                                         kmem_free(aiov32, iov32size);
!                                         kmem_free(aiov, iovsize);
!                                 }
! 
                                  return (set_errno(EINVAL));
+                         }
+ 
                          aiov[i].iov_len = iovlen32;
                          aiov[i].iov_base =
                              (caddr_t)(uintptr_t)aiov32[i].iov_base;
                  }
+ 
+                 if (iovsize != 0)
+                         kmem_free(aiov32, iov32size);
          } else
  #endif /* _SYSCALL32_IMPL */
          if (copyin(lmsg.msg_iov, aiov, iovcnt * sizeof (struct iovec))) {
+                 if (iovsize != 0)
+                         kmem_free(aiov, iovsize);
+ 
                  return (set_errno(EFAULT));
          }
          len = 0;
          for (i = 0; i < iovcnt; i++) {
                  ssize_t iovlen = aiov[i].iov_len;
                  len += iovlen;
                  if (iovlen < 0 || len < 0) {
+                         if (iovsize != 0)
+                                 kmem_free(aiov, iovsize);
+ 
                          return (set_errno(EINVAL));
                  }
          }
          auio.uio_loffset = 0;
          auio.uio_iov = aiov;
*** 1122,1137 ****
  
          if (lmsg.msg_control != NULL &&
              (do_useracc == 0 ||
              useracc(lmsg.msg_control, lmsg.msg_controllen,
              B_WRITE) != 0)) {
                  return (set_errno(EFAULT));
          }
  
!         return (recvit(sock, &lmsg, &auio, flags,
              STRUCT_FADDR(umsgptr, msg_namelen),
!             STRUCT_FADDR(umsgptr, msg_controllen), flagsp));
  }
  
  /*
   * Common send function.
   */
--- 1151,1174 ----
  
          if (lmsg.msg_control != NULL &&
              (do_useracc == 0 ||
              useracc(lmsg.msg_control, lmsg.msg_controllen,
              B_WRITE) != 0)) {
+                 if (iovsize != 0)
+                         kmem_free(aiov, iovsize);
+ 
                  return (set_errno(EFAULT));
          }
  
!         rval = recvit(sock, &lmsg, &auio, flags,
              STRUCT_FADDR(umsgptr, msg_namelen),
!             STRUCT_FADDR(umsgptr, msg_controllen), flagsp);
! 
!         if (iovsize != 0)
!                 kmem_free(aiov, iovsize);
! 
!         return (rval);
  }
  
  /*
   * Common send function.
   */
*** 1265,1277 ****
  sendmsg(int sock, struct nmsghdr *msg, int flags)
  {
          struct nmsghdr lmsg;
          STRUCT_DECL(nmsghdr, u_lmsg);
          struct uio auio;
!         struct iovec aiov[MSG_MAXIOVLEN];
          int iovcnt;
!         ssize_t len;
          int i;
          model_t model;
  
          dprint(1, ("sendmsg(%d, %p, %d)\n", sock, (void *)msg, flags));
  
--- 1302,1315 ----
  sendmsg(int sock, struct nmsghdr *msg, int flags)
  {
          struct nmsghdr lmsg;
          STRUCT_DECL(nmsghdr, u_lmsg);
          struct uio auio;
!         struct iovec buf[IOV_MAX_STACK], *aiov = buf;
!         ssize_t iovsize = 0;
          int iovcnt;
!         ssize_t len, rval;
          int i;
          model_t model;
  
          dprint(1, ("sendmsg(%d, %p, %d)\n", sock, (void *)msg, flags));
  
*** 1310,1366 ****
          lmsg.msg_controllen = STRUCT_FGET(u_lmsg, msg_controllen);
          lmsg.msg_flags = STRUCT_FGET(u_lmsg, msg_flags);
  
          iovcnt = lmsg.msg_iovlen;
  
!         if (iovcnt <= 0 || iovcnt > MSG_MAXIOVLEN) {
                  /*
                   * Unless this is XPG 4.2 we allow iovcnt == 0 to
                   * be compatible with SunOS 4.X and 4.4BSD.
                   */
                  if (iovcnt != 0 || (flags & MSG_XPG4_2))
                          return (set_errno(EMSGSIZE));
          }
  
  #ifdef _SYSCALL32_IMPL
          /*
           * 32-bit callers need to have their iovec expanded, while ensuring
           * that they can't move more than 2Gbytes of data in a single call.
           */
          if (model == DATAMODEL_ILP32) {
!                 struct iovec32 aiov32[MSG_MAXIOVLEN];
                  ssize32_t count32;
  
                  if (iovcnt != 0 &&
!                     copyin((struct iovec32 *)lmsg.msg_iov, aiov32,
!                     iovcnt * sizeof (struct iovec32)))
                          return (set_errno(EFAULT));
  
                  count32 = 0;
                  for (i = 0; i < iovcnt; i++) {
                          ssize32_t iovlen32;
  
                          iovlen32 = aiov32[i].iov_len;
                          count32 += iovlen32;
!                         if (iovlen32 < 0 || count32 < 0)
                                  return (set_errno(EINVAL));
                          aiov[i].iov_len = iovlen32;
                          aiov[i].iov_base =
                              (caddr_t)(uintptr_t)aiov32[i].iov_base;
                  }
          } else
  #endif /* _SYSCALL32_IMPL */
          if (iovcnt != 0 &&
              copyin(lmsg.msg_iov, aiov,
              (unsigned)iovcnt * sizeof (struct iovec))) {
                  return (set_errno(EFAULT));
          }
          len = 0;
          for (i = 0; i < iovcnt; i++) {
                  ssize_t iovlen = aiov[i].iov_len;
                  len += iovlen;
                  if (iovlen < 0 || len < 0) {
                          return (set_errno(EINVAL));
                  }
          }
          auio.uio_loffset = 0;
          auio.uio_iov = aiov;
--- 1348,1435 ----
          lmsg.msg_controllen = STRUCT_FGET(u_lmsg, msg_controllen);
          lmsg.msg_flags = STRUCT_FGET(u_lmsg, msg_flags);
  
          iovcnt = lmsg.msg_iovlen;
  
!         if (iovcnt <= 0 || iovcnt > IOV_MAX) {
                  /*
                   * Unless this is XPG 4.2 we allow iovcnt == 0 to
                   * be compatible with SunOS 4.X and 4.4BSD.
                   */
                  if (iovcnt != 0 || (flags & MSG_XPG4_2))
                          return (set_errno(EMSGSIZE));
          }
  
+         if (iovcnt > IOV_MAX_STACK) {
+                 iovsize = iovcnt * sizeof (struct iovec);
+                 aiov = kmem_alloc(iovsize, KM_SLEEP);
+         }
+ 
  #ifdef _SYSCALL32_IMPL
          /*
           * 32-bit callers need to have their iovec expanded, while ensuring
           * that they can't move more than 2Gbytes of data in a single call.
           */
          if (model == DATAMODEL_ILP32) {
!                 struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
!                 ssize_t iov32size;
                  ssize32_t count32;
  
+                 iov32size = iovcnt * sizeof (struct iovec32);
+                 if (iovsize != 0)
+                         aiov32 = kmem_alloc(iov32size, KM_SLEEP);
+ 
                  if (iovcnt != 0 &&
!                     copyin((struct iovec32 *)lmsg.msg_iov, aiov32, iov32size)) {
!                         if (iovsize != 0) {
!                                 kmem_free(aiov32, iov32size);
!                                 kmem_free(aiov, iovsize);
!                         }
! 
                          return (set_errno(EFAULT));
+                 }
  
                  count32 = 0;
                  for (i = 0; i < iovcnt; i++) {
                          ssize32_t iovlen32;
  
                          iovlen32 = aiov32[i].iov_len;
                          count32 += iovlen32;
!                         if (iovlen32 < 0 || count32 < 0) {
!                                 if (iovsize != 0) {
!                                         kmem_free(aiov32, iov32size);
!                                         kmem_free(aiov, iovsize);
!                                 }
! 
                                  return (set_errno(EINVAL));
+                         }
+ 
                          aiov[i].iov_len = iovlen32;
                          aiov[i].iov_base =
                              (caddr_t)(uintptr_t)aiov32[i].iov_base;
                  }
+ 
+                 if (iovsize != 0)
+                         kmem_free(aiov32, iov32size);
          } else
  #endif /* _SYSCALL32_IMPL */
          if (iovcnt != 0 &&
              copyin(lmsg.msg_iov, aiov,
              (unsigned)iovcnt * sizeof (struct iovec))) {
+                 if (iovsize != 0)
+                         kmem_free(aiov, iovsize);
+ 
                  return (set_errno(EFAULT));
          }
          len = 0;
          for (i = 0; i < iovcnt; i++) {
                  ssize_t iovlen = aiov[i].iov_len;
                  len += iovlen;
                  if (iovlen < 0 || len < 0) {
+                         if (iovsize != 0)
+                                 kmem_free(aiov, iovsize);
+ 
                          return (set_errno(EINVAL));
                  }
          }
          auio.uio_loffset = 0;
          auio.uio_iov = aiov;
*** 1367,1377 ****
          auio.uio_iovcnt = iovcnt;
          auio.uio_resid = len;
          auio.uio_segflg = UIO_USERSPACE;
          auio.uio_limit = 0;
  
!         return (sendit(sock, &lmsg, &auio, flags));
  }
  
  ssize_t
  sendto(int sock, void *buffer, size_t len, int flags,
      struct sockaddr *name, socklen_t namelen)
--- 1436,1451 ----
          auio.uio_iovcnt = iovcnt;
          auio.uio_resid = len;
          auio.uio_segflg = UIO_USERSPACE;
          auio.uio_limit = 0;
  
!         rval = sendit(sock, &lmsg, &auio, flags);
! 
!         if (iovsize != 0)
!                 kmem_free(aiov, iovsize);
! 
!         return (rval);
  }
  
  ssize_t
  sendto(int sock, void *buffer, size_t len, int flags,
      struct sockaddr *name, socklen_t namelen)