diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/compat/freebsd32/freebsd32_misc.c | 243 | ||||
-rw-r--r-- | sys/kern/uipc_syscalls.c | 19 | ||||
-rw-r--r-- | sys/sys/syscallsubr.h | 2 |
3 files changed, 240 insertions, 24 deletions
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 466af49..417656b 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include <sys/lock.h> #include <sys/malloc.h> #include <sys/file.h> /* Must come after sys/malloc.h */ +#include <sys/mbuf.h> #include <sys/mman.h> #include <sys/module.h> #include <sys/mount.h> @@ -800,32 +801,52 @@ freebsd32_pwritev(struct thread *td, struct freebsd32_pwritev_args *uap) } static int -freebsd32_copyiniov(struct iovec32 *iovp, u_int iovcnt, struct iovec **iov, +freebsd32_copyiniov(struct iovec32 *iovp32, u_int iovcnt, struct iovec **iovp, int error) { struct iovec32 iov32; - int i; - + struct iovec *iov; u_int iovlen; + int i; - *iov = NULL; + *iovp = NULL; if (iovcnt > UIO_MAXIOV) return (error); iovlen = iovcnt * sizeof(struct iovec); - *iov = malloc(iovlen, M_IOV, M_WAITOK); + iov = malloc(iovlen, M_IOV, M_WAITOK); for (i = 0; i < iovcnt; i++) { - error = copyin(&iovp[i], &iov32, sizeof(struct iovec32)); + error = copyin(&iovp32[i], &iov32, sizeof(struct iovec32)); if (error) { - free(*iov, M_IOV); - *iov = NULL; + free(iov, M_IOV); return (error); } - iov[i]->iov_base = PTRIN(iov32.iov_base); - iov[i]->iov_len = iov32.iov_len; + iov[i].iov_base = PTRIN(iov32.iov_base); + iov[i].iov_len = iov32.iov_len; + } + *iovp = iov; + return (0); +} + +static int +freebsd32_copyoutiov(struct iovec *iov, u_int iovcnt, struct iovec32 *iovp, + int error) +{ + struct iovec32 iov32; + int i; + + if (iovcnt > UIO_MAXIOV) + return (error); + for (i = 0; i < iovcnt; i++) { + iov32.iov_base = PTROUT(iov[i].iov_base); + iov32.iov_len = iov[i].iov_len; + error = copyout(&iov32, &iovp[i], sizeof(iov32)); + if (error) + return (error); } return (0); } + struct msghdr32 { u_int32_t msg_name; socklen_t msg_namelen; @@ -853,8 +874,7 @@ freebsd32_copyinmsghdr(struct msghdr32 *msg32, struct msghdr *msg) msg->msg_control = PTRIN(m32.msg_control); msg->msg_controllen = m32.msg_controllen; msg->msg_flags = m32.msg_flags; - return (freebsd32_copyiniov((struct iovec32 *)(uintptr_t)m32.msg_iov, m32.msg_iovlen, &msg->msg_iov, - EMSGSIZE)); + return (0); } static int @@ -874,6 +894,103 @@ freebsd32_copyoutmsghdr(struct msghdr *msg, struct msghdr32 *msg32) return (error); } +#define FREEBSD32_ALIGNBYTES (sizeof(int) - 1) +#define FREEBSD32_ALIGN(p) \ + (((u_long)(p) + FREEBSD32_ALIGNBYTES) & ~FREEBSD32_ALIGNBYTES) +#define FREEBSD32_CMSG_SPACE(l) \ + (FREEBSD32_ALIGN(sizeof(struct cmsghdr)) + FREEBSD32_ALIGN(l)) + +#define FREEBSD32_CMSG_DATA(cmsg) ((unsigned char *)(cmsg) + \ + FREEBSD32_ALIGN(sizeof(struct cmsghdr))) +static int +freebsd32_copy_msg_out(struct msghdr *msg, struct mbuf *control) +{ + struct cmsghdr *cm; + void *data; + socklen_t clen, datalen; + int error; + caddr_t ctlbuf; + int len, maxlen, copylen; + struct mbuf *m; + error = 0; + + len = msg->msg_controllen; + maxlen = msg->msg_controllen; + msg->msg_controllen = 0; + + m = control; + ctlbuf = msg->msg_control; + + while (m && len > 0) { + cm = mtod(m, struct cmsghdr *); + clen = m->m_len; + + while (cm != NULL) { + + if (sizeof(struct cmsghdr) > clen || + cm->cmsg_len > clen) { + error = EINVAL; + break; + } + + data = CMSG_DATA(cm); + datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data; + + /* Adjust message length */ + cm->cmsg_len = FREEBSD32_ALIGN(sizeof(struct cmsghdr)) + + datalen; + + + /* Copy cmsghdr */ + copylen = sizeof(struct cmsghdr); + if (len < copylen) { + msg->msg_flags |= MSG_CTRUNC; + copylen = len; + } + + error = copyout(cm,ctlbuf,copylen); + if (error) + goto exit; + + ctlbuf += FREEBSD32_ALIGN(copylen); + len -= FREEBSD32_ALIGN(copylen); + + if (len <= 0) + break; + + /* Copy data */ + copylen = datalen; + if (len < copylen) { + msg->msg_flags |= MSG_CTRUNC; + copylen = len; + } + + error = copyout(data,ctlbuf,copylen); + if (error) + goto exit; + + ctlbuf += FREEBSD32_ALIGN(copylen); + len -= FREEBSD32_ALIGN(copylen); + + if (CMSG_SPACE(datalen) < clen) { + clen -= CMSG_SPACE(datalen); + cm = (struct cmsghdr *) + ((caddr_t)cm + CMSG_SPACE(datalen)); + } else { + clen = 0; + cm = NULL; + } + } + m = m->m_next; + } + + msg->msg_controllen = (len <= 0) ? maxlen : ctlbuf - (caddr_t)msg->msg_control; + +exit: + return (error); + +} + int freebsd32_recvmsg(td, uap) struct thread *td; @@ -886,8 +1003,10 @@ freebsd32_recvmsg(td, uap) struct msghdr msg; struct msghdr32 m32; struct iovec *uiov, *iov; - int error; + struct mbuf *control = NULL; + struct mbuf **controlp; + int error; error = copyin(uap->msg, &m32, sizeof(m32)); if (error) return (error); @@ -901,16 +1020,71 @@ freebsd32_recvmsg(td, uap) msg.msg_flags = uap->flags; uiov = msg.msg_iov; msg.msg_iov = iov; - error = kern_recvit(td, uap->s, &msg, NULL, UIO_SYSSPACE); + + controlp = (msg.msg_control != NULL) ? &control : NULL; + error = kern_recvit(td, uap->s, &msg, NULL, UIO_USERSPACE, controlp); if (error == 0) { msg.msg_iov = uiov; - error = freebsd32_copyoutmsghdr(&msg, uap->msg); + + if (control != NULL) + error = freebsd32_copy_msg_out(&msg, control); + + if (error == 0) + error = freebsd32_copyoutmsghdr(&msg, uap->msg); + + if (error == 0) + error = freebsd32_copyoutiov(iov, iov->iov_len, + (struct iovec32 *)(uintptr_t)m32.msg_iov, EMSGSIZE); } free(iov, M_IOV); - free(uiov, M_IOV); + + if (control != NULL) + m_freem(control); + + return (error); +} + + +static int +freebsd32_convert_msg_in(struct mbuf **controlp) +{ + struct mbuf *control = *controlp; + struct cmsghdr *cm = mtod(control, struct cmsghdr *); + void *data; + socklen_t clen = control->m_len, datalen; + int error; + + error = 0; + *controlp = NULL; + + while (cm != NULL) { + if (sizeof(struct cmsghdr) > clen || cm->cmsg_len > clen) { + error = EINVAL; + break; + } + + data = FREEBSD32_CMSG_DATA(cm); + datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data; + + *controlp = sbcreatecontrol(data, datalen, cm->cmsg_type, + cm->cmsg_level); + controlp = &(*controlp)->m_next; + + if (FREEBSD32_CMSG_SPACE(datalen) < clen) { + clen -= FREEBSD32_CMSG_SPACE(datalen); + cm = (struct cmsghdr *) + ((caddr_t)cm + FREEBSD32_CMSG_SPACE(datalen)); + } else { + clen = 0; + cm = NULL; + } + } + + m_freem(control); return (error); } + int freebsd32_sendmsg(struct thread *td, struct freebsd32_sendmsg_args *uap) @@ -918,6 +1092,8 @@ freebsd32_sendmsg(struct thread *td, struct msghdr msg; struct msghdr32 m32; struct iovec *iov; + struct mbuf *control = NULL; + struct sockaddr *to = NULL; int error; error = copyin(uap->msg, &m32, sizeof(m32)); @@ -931,8 +1107,38 @@ freebsd32_sendmsg(struct thread *td, if (error) return (error); msg.msg_iov = iov; - error = kern_sendit(td, uap->s, &msg, uap->flags, NULL, UIO_SYSSPACE); + if (msg.msg_name != NULL) { + error = getsockaddr(&to, msg.msg_name, msg.msg_namelen); + if (error) { + to = NULL; + goto out; + } + msg.msg_name = to; + } + + if (msg.msg_control) { + if (msg.msg_controllen < sizeof(struct cmsghdr)) { + error = EINVAL; + goto out; + } + + error = sockargs(&control, msg.msg_control, + msg.msg_controllen, MT_CONTROL); + if (error) + goto out; + + error = freebsd32_convert_msg_in(&control); + if (error) + goto out; + } + + error = kern_sendit(td, uap->s, &msg, uap->flags, control, + UIO_USERSPACE); + +out: free(iov, M_IOV); + if (to) + free(to, M_SONAME); return (error); } @@ -960,7 +1166,8 @@ freebsd32_recvfrom(struct thread *td, aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = uap->flags; - error = kern_recvit(td, uap->s, &msg, (void *)(uintptr_t)uap->fromlenaddr, UIO_USERSPACE); + error = kern_recvit(td, uap->s, &msg, + (void *)(uintptr_t)uap->fromlenaddr, UIO_USERSPACE, NULL); return (error); } diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index d855755..6afc60a 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -923,12 +923,13 @@ sendmsg(td, uap) } int -kern_recvit(td, s, mp, namelenp, segflg) +kern_recvit(td, s, mp, namelenp, segflg, controlp) struct thread *td; int s; struct msghdr *mp; void *namelenp; enum uio_seg segflg; + struct mbuf **controlp; { struct uio auio; struct iovec *iov; @@ -944,6 +945,9 @@ kern_recvit(td, s, mp, namelenp, segflg) struct uio *ktruio = NULL; #endif + if(controlp != NULL) + *controlp = 0; + NET_LOCK_GIANT(); error = getsock(td->td_proc->p_fd, s, &fp); if (error) { @@ -984,7 +988,8 @@ kern_recvit(td, s, mp, namelenp, segflg) #endif len = auio.uio_resid; error = so->so_proto->pr_usrreqs->pru_soreceive(so, &fromsa, &auio, - (struct mbuf **)0, mp->msg_control ? &control : (struct mbuf **)0, + (struct mbuf **)0, + (mp->msg_control || controlp) ? &control : (struct mbuf **)0, &mp->msg_flags); if (error) { if (auio.uio_resid != (int)len && (error == ERESTART || @@ -1027,7 +1032,7 @@ kern_recvit(td, s, mp, namelenp, segflg) goto out; } } - if (mp->msg_control) { + if (mp->msg_control && controlp == NULL) { #ifdef COMPAT_OLDSOCK /* * We assume that old recvmsg calls won't receive access @@ -1078,8 +1083,12 @@ out: NET_UNLOCK_GIANT(); if (fromsa) FREE(fromsa, M_SONAME); - if (control) + + if (error == 0 && controlp != NULL) + *controlp = control; + else if (control) m_freem(control); + return (error); } @@ -1091,7 +1100,7 @@ recvit(td, s, mp, namelenp) void *namelenp; { - return (kern_recvit(td, s, mp, namelenp, UIO_USERSPACE)); + return (kern_recvit(td, s, mp, namelenp, UIO_USERSPACE, NULL)); } /* diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index 734addf..7f173ff 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -112,7 +112,7 @@ int kern_readlink(struct thread *td, char *path, enum uio_seg pathseg, char *buf, enum uio_seg bufseg, int count); int kern_readv(struct thread *td, int fd, struct uio *auio); int kern_recvit(struct thread *td, int s, struct msghdr *mp, void *namelenp, - enum uio_seg segflg); + enum uio_seg segflg, struct mbuf **controlp); int kern_rename(struct thread *td, char *from, char *to, enum uio_seg pathseg); int kern_rmdir(struct thread *td, char *path, enum uio_seg pathseg); |