/* Copy socket control/CMSG data to user buffer, frees the mbuf */ int copyout_msg_control(struct lwp *l, struct msghdr *mp, struct mbuf *control) { int i, len, error = 0; struct cmsghdr *cmsg; struct mbuf *m; char *q; len = mp->msg_controllen; if (len <= 0 || control == 0) { mp->msg_controllen = 0; free_control_mbuf(l, control, control); return 0; } q = (char *)mp->msg_control; for (m = control; m != NULL; ) { cmsg = mtod(m, struct cmsghdr *); i = m->m_len; if (len < i) { mp->msg_flags |= MSG_CTRUNC; if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) /* Do not truncate me ... */ break; i = len; } error = copyout(mtod(m, void *), q, i); ktrkuser("msgcontrol", mtod(m, void *), i); if (error != 0) { /* We must free all the SCM_RIGHTS */ m = control; break; } m = m->m_next; if (m) i = ALIGN(i); q += i; len -= i; if (len <= 0) break; } free_control_mbuf(l, control, m); mp->msg_controllen = q - (char *)mp->msg_control; return error; }
static int do_sys_recvmsg_so(struct lwp *l, int s, struct socket *so, struct msghdr *mp, struct mbuf **from, struct mbuf **control, register_t *retsize) { struct iovec aiov[UIO_SMALLIOV], *iov = aiov, *tiov, *ktriov = NULL; struct uio auio; size_t len, iovsz; int i, error; ktrkuser("msghdr", mp, sizeof *mp); *from = NULL; if (control != NULL) *control = NULL; iovsz = mp->msg_iovlen * sizeof(struct iovec); if (mp->msg_flags & MSG_IOVUSRSPACE) { if ((unsigned int)mp->msg_iovlen > UIO_SMALLIOV) { if ((unsigned int)mp->msg_iovlen > IOV_MAX) { error = EMSGSIZE; goto out; } iov = kmem_alloc(iovsz, KM_SLEEP); } if (mp->msg_iovlen != 0) { error = copyin(mp->msg_iov, iov, iovsz); if (error) goto out; } auio.uio_iov = iov; } else auio.uio_iov = mp->msg_iov; auio.uio_iovcnt = mp->msg_iovlen; auio.uio_rw = UIO_READ; auio.uio_offset = 0; /* XXX */ auio.uio_resid = 0; KASSERT(l == curlwp); auio.uio_vmspace = l->l_proc->p_vmspace; tiov = auio.uio_iov; for (i = 0; i < mp->msg_iovlen; i++, tiov++) { /* * Reads return ssize_t because -1 is returned on error. * Therefore we must restrict the length to SSIZE_MAX to * avoid garbage return values. */ auio.uio_resid += tiov->iov_len; if (tiov->iov_len > SSIZE_MAX || auio.uio_resid > SSIZE_MAX) { error = EINVAL; goto out; } } if (ktrpoint(KTR_GENIO) && iovsz > 0) { ktriov = kmem_alloc(iovsz, KM_SLEEP); memcpy(ktriov, auio.uio_iov, iovsz); } len = auio.uio_resid; mp->msg_flags &= MSG_USERFLAGS; error = (*so->so_receive)(so, from, &auio, NULL, control, &mp->msg_flags); len -= auio.uio_resid; *retsize = len; if (error != 0 && len != 0 && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) /* Some data transferred */ error = 0; if (ktriov != NULL) { ktrgeniov(s, UIO_READ, ktriov, len, error); kmem_free(ktriov, iovsz); } if (error != 0) { m_freem(*from); *from = NULL; if (control != NULL) { free_control_mbuf(l, *control, *control); *control = NULL; } } out: if (iov != aiov) kmem_free(iov, iovsz); return error; }
/* * This is a brutal clone of compat_43_sys_recvmsg(). */ int compat_43_netbsd32_orecvmsg(struct lwp *l, const struct compat_43_netbsd32_orecvmsg_args *uap, register_t *retval) { /* { syscallarg(int) s; syscallarg(netbsd32_omsghdrp_t) msg; syscallarg(int) flags; } */ struct netbsd32_omsghdr omsg; struct msghdr msg; struct mbuf *from, *control; struct iovec *iov, aiov[UIO_SMALLIOV]; int error; error = copyin(SCARG_P32(uap, msg), &omsg, sizeof (struct omsghdr)); if (error) return (error); if (NETBSD32PTR64(omsg.msg_accrights) == NULL) omsg.msg_accrightslen = 0; /* it was this way in 4.4BSD */ if (omsg.msg_accrightslen > MLEN) return EINVAL; iov = netbsd32_get_iov(NETBSD32PTR64(omsg.msg_iov), omsg.msg_iovlen, aiov, __arraycount(aiov)); if (iov == NULL) return EFAULT; msg.msg_name = NETBSD32PTR64(omsg.msg_name); msg.msg_namelen = omsg.msg_namelen; msg.msg_iovlen = omsg.msg_iovlen; msg.msg_iov = iov; msg.msg_flags = SCARG(uap, flags) & MSG_USERFLAGS; error = do_sys_recvmsg(l, SCARG(uap, s), &msg, &from, NETBSD32PTR64(omsg.msg_accrights) != NULL ? &control : NULL, retval); if (error != 0) return error; /* * If there is any control information and it's SCM_RIGHTS, * pass it back to the program. * XXX: maybe there can be more than one chunk of control data? */ if (NETBSD32PTR64(omsg.msg_accrights) != NULL && control != NULL) { struct cmsghdr *cmsg = mtod(control, void *); if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS && cmsg->cmsg_len < omsg.msg_accrightslen && copyout(CMSG_DATA(cmsg), NETBSD32PTR64(omsg.msg_accrights), cmsg->cmsg_len) == 0) { omsg.msg_accrightslen = cmsg->cmsg_len; free_control_mbuf(l, control, control->m_next); } else { omsg.msg_accrightslen = 0; free_control_mbuf(l, control, control); } } else