+ msghdrp = (caddr_t)&msg64;
+ size_of_msghdr = sizeof (msg64);
+ } else {
+ msghdrp = (caddr_t)&msg32;
+ size_of_msghdr = sizeof (msg32);
+ }
+ error = copyin(uap->msg, msghdrp, size_of_msghdr);
+ if (error) {
+ KERNEL_DEBUG(DBG_FNC_RECVMSG | DBG_FUNC_END, error, 0, 0, 0, 0);
+ return (error);
+ }
+
+ /* only need to copy if user process is not 64-bit */
+ if (IS_64BIT_PROCESS(p)) {
+ user_msg.msg_flags = msg64.msg_flags;
+ user_msg.msg_controllen = msg64.msg_controllen;
+ user_msg.msg_control = msg64.msg_control;
+ user_msg.msg_iovlen = msg64.msg_iovlen;
+ user_msg.msg_iov = msg64.msg_iov;
+ user_msg.msg_namelen = msg64.msg_namelen;
+ user_msg.msg_name = msg64.msg_name;
+ } else {
+ user_msg.msg_flags = msg32.msg_flags;
+ user_msg.msg_controllen = msg32.msg_controllen;
+ user_msg.msg_control = msg32.msg_control;
+ user_msg.msg_iovlen = msg32.msg_iovlen;
+ user_msg.msg_iov = msg32.msg_iov;
+ user_msg.msg_namelen = msg32.msg_namelen;
+ user_msg.msg_name = msg32.msg_name;
+ }
+
+ if (user_msg.msg_iovlen <= 0 || user_msg.msg_iovlen > UIO_MAXIOV) {
+ KERNEL_DEBUG(DBG_FNC_RECVMSG | DBG_FUNC_END, EMSGSIZE,
+ 0, 0, 0, 0);
+ return (EMSGSIZE);
+ }
+
+ user_msg.msg_flags = uap->flags;
+
+ /* allocate a uio large enough to hold the number of iovecs passed */
+ auio = uio_create(user_msg.msg_iovlen, 0,
+ (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32),
+ UIO_READ);
+ if (auio == NULL) {
+ error = ENOMEM;
+ goto done;
+ }
+
+ /*
+ * get location of iovecs within the uio. then copyin the iovecs from
+ * user space.
+ */
+ iovp = uio_iovsaddr(auio);
+ if (iovp == NULL) {
+ error = ENOMEM;
+ goto done;
+ }
+ uiov = user_msg.msg_iov;
+ user_msg.msg_iov = CAST_USER_ADDR_T(iovp);
+ error = copyin_user_iovec_array(uiov,
+ IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32,
+ user_msg.msg_iovlen, iovp);
+ if (error)
+ goto done;
+
+ /* finish setup of uio_t */
+ error = uio_calculateresid(auio);
+ if (error) {
+ goto done;
+ }
+
+ error = recvit(p, uap->s, &user_msg, auio, 0, retval);
+ if (!error) {
+ user_msg.msg_iov = uiov;
+ if (IS_64BIT_PROCESS(p)) {
+ msg64.msg_flags = user_msg.msg_flags;
+ msg64.msg_controllen = user_msg.msg_controllen;
+ msg64.msg_control = user_msg.msg_control;
+ msg64.msg_iovlen = user_msg.msg_iovlen;
+ msg64.msg_iov = user_msg.msg_iov;
+ msg64.msg_namelen = user_msg.msg_namelen;
+ msg64.msg_name = user_msg.msg_name;
+ } else {
+ msg32.msg_flags = user_msg.msg_flags;
+ msg32.msg_controllen = user_msg.msg_controllen;
+ msg32.msg_control = user_msg.msg_control;
+ msg32.msg_iovlen = user_msg.msg_iovlen;
+ msg32.msg_iov = user_msg.msg_iov;
+ msg32.msg_namelen = user_msg.msg_namelen;
+ msg32.msg_name = user_msg.msg_name;
+ }
+ error = copyout(msghdrp, uap->msg, size_of_msghdr);
+ }
+done:
+ if (auio != NULL) {
+ uio_free(auio);
+ }
+ KERNEL_DEBUG(DBG_FNC_RECVMSG | DBG_FUNC_END, error, 0, 0, 0, 0);
+ return (error);
+}
+
+int
+recvmsg_x(struct proc *p, struct recvmsg_x_args *uap, user_ssize_t *retval)
+{
+ int error = EOPNOTSUPP;
+ struct user_msghdr_x *user_msg_x = NULL;
+ struct recv_msg_elem *recv_msg_array = NULL;
+ struct socket *so;
+ user_ssize_t len_before = 0, len_after;
+ int need_drop = 0;
+ size_t size_of_msghdr;
+ void *umsgp = NULL;
+ u_int i;
+ u_int uiocnt;
+
+ KERNEL_DEBUG(DBG_FNC_RECVMSG_X | DBG_FUNC_START, 0, 0, 0, 0, 0);
+
+ error = file_socket(uap->s, &so);
+ if (error) {
+ goto out;
+ }
+ need_drop = 1;
+ if (so == NULL) {
+ error = EBADF;
+ goto out;
+ }
+ /*
+ * Input parameter range check
+ */
+ if (uap->cnt == 0 || uap->cnt > UIO_MAXIOV) {
+ error = EINVAL;
+ goto out;
+ }
+ if (uap->cnt > somaxrecvmsgx)
+ uap->cnt = somaxrecvmsgx;
+
+ user_msg_x = _MALLOC(uap->cnt * sizeof(struct user_msghdr_x),
+ M_TEMP, M_WAITOK | M_ZERO);
+ if (user_msg_x == NULL) {
+ DBG_PRINTF("%s _MALLOC() user_msg_x failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ recv_msg_array = alloc_recv_msg_array(uap->cnt);
+ if (recv_msg_array == NULL) {
+ DBG_PRINTF("%s alloc_recv_msg_array() failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ size_of_msghdr = IS_64BIT_PROCESS(p) ?
+ sizeof(struct user64_msghdr_x) : sizeof(struct user32_msghdr_x);
+
+ umsgp = _MALLOC(uap->cnt * size_of_msghdr, M_TEMP, M_WAITOK | M_ZERO);
+ if (umsgp == NULL) {
+ DBG_PRINTF("%s _MALLOC() umsgp failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ error = copyin(uap->msgp, umsgp, uap->cnt * size_of_msghdr);
+ if (error) {
+ DBG_PRINTF("%s copyin() failed\n", __func__);
+ goto out;
+ }
+ error = internalize_recv_msghdr_array(umsgp,
+ IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32,
+ UIO_READ, uap->cnt, user_msg_x, recv_msg_array);
+ if (error) {
+ DBG_PRINTF("%s copyin_user_msghdr_array() failed\n", __func__);
+ goto out;
+ }
+ /*
+ * Make sure the size of each message iovec and
+ * the aggregate size of all the iovec is valid
+ */
+ if (recv_msg_array_is_valid(recv_msg_array, uap->cnt) == 0) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Sanity check on passed arguments
+ */
+ for (i = 0; i < uap->cnt; i++) {
+ struct user_msghdr_x *mp = user_msg_x + i;
+
+ if (mp->msg_flags != 0) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+#if CONFIG_MACF_SOCKET_SUBSET
+ /*
+ * We check the state without holding the socket lock;
+ * if a race condition occurs, it would simply result
+ * in an extra call to the MAC check function.
+ */
+ if (!(so->so_state & SS_DEFUNCT) &&
+ !(so->so_state & SS_ISCONNECTED) &&
+ !(so->so_proto->pr_flags & PR_CONNREQUIRED) &&
+ (error = mac_socket_check_receive(kauth_cred_get(), so)) != 0)
+ goto out;
+#endif /* MAC_SOCKET_SUBSET */
+
+ len_before = recv_msg_array_resid(recv_msg_array, uap->cnt);
+
+ if (so->so_proto->pr_usrreqs->pru_soreceive_list !=
+ pru_soreceive_list_notsupp &&
+ somaxrecvmsgx == 0) {
+ error = so->so_proto->pr_usrreqs->pru_soreceive_list(so,
+ recv_msg_array, uap->cnt, &uap->flags);