summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorps <ps@FreeBSD.org>2005-10-31 21:09:56 +0000
committerps <ps@FreeBSD.org>2005-10-31 21:09:56 +0000
commitbd0529b5a0b46f48edc03276b4f87871d130ec17 (patch)
treedac810179b8f34e11bee118b9b53989333f09c5b /sys
parent8cc0b6538da5b4f869b6f3dac62c54102dd37c69 (diff)
downloadFreeBSD-src-bd0529b5a0b46f48edc03276b4f87871d130ec17.zip
FreeBSD-src-bd0529b5a0b46f48edc03276b4f87871d130ec17.tar.gz
Reformat socket control messages on input/output for 32bit compatibility
on 64bit systems. Submitted by: ps, ups Reviewed by: jhb
Diffstat (limited to 'sys')
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c243
-rw-r--r--sys/kern/uipc_syscalls.c19
-rw-r--r--sys/sys/syscallsubr.h2
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);
OpenPOWER on IntegriCloud