Skip to content

Commit

Permalink
Merge pull request #77 from MikaelUrankar/sendrecvmsg
Browse files Browse the repository at this point in the history
Sync sendrecvmsg with linux-user
  • Loading branch information
seanbruno authored and kwitaszczyk committed Dec 3, 2021
1 parent 577da3e commit 1163019
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 167 deletions.
2 changes: 1 addition & 1 deletion bsd-user/bsd-socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ static inline abi_long do_bsd_connect(int sockfd, abi_ulong target_addr,
if ((int)addrlen < 0) {
return -TARGET_EINVAL;
}
addr = alloca(addrlen);
addr = alloca(addrlen+1);

ret = target_to_host_sockaddr(addr, target_addr, addrlen);

Expand Down
214 changes: 152 additions & 62 deletions bsd-user/freebsd/os-socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,64 +28,73 @@ abi_long t2h_freebsd_cmsg(struct msghdr *msgh,
struct target_msghdr *target_msgh)
{
struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
abi_long msg_controllen;
socklen_t msg_controllen;
abi_ulong target_cmsg_addr;
struct target_cmsghdr *target_cmsg;
struct target_cmsghdr *target_cmsg, *target_cmsg_start;
socklen_t space = 0;


msg_controllen = tswapal(target_msgh->msg_controllen);
if (msg_controllen < sizeof(struct target_cmsghdr)) {
msg_controllen = tswap32(target_msgh->msg_controllen);
if (msg_controllen < sizeof(struct target_cmsghdr))
goto the_end;
}
target_cmsg_addr = tswapal(target_msgh->msg_control);
target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1);
if (target_cmsg == 0) {
target_cmsg_start = target_cmsg;
if (!target_cmsg)
return -TARGET_EFAULT;
}

while (cmsg && target_cmsg) {
void *data = CMSG_DATA(cmsg);
void *target_data = TARGET_CMSG_DATA(target_cmsg);
int len = tswapal(target_cmsg->cmsg_len) -
TARGET_CMSG_ALIGN(sizeof(struct target_cmsghdr));

int len = tswap32(target_cmsg->cmsg_len)
- sizeof(struct target_cmsghdr);

space += CMSG_SPACE(len);
if (space > msgh->msg_controllen) {
space -= CMSG_SPACE(len);
/* This is a QEMU bug, since we allocated the payload
* area ourselves (unlike overflow in host-to-target
* conversion, which is just the guest giving us a buffer
* that's too small). It can't happen for the payload types
* we currently support; if it becomes an issue in future
* we would need to improve our allocation strategy to
* something more intelligent than "twice the size of the
* target buffer we're reading from".
*/
gemu_log("Host cmsg overflow\n");
break;
}
cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level);

if (tswap32(target_cmsg->cmsg_level) == TARGET_SOL_SOCKET) {
cmsg->cmsg_level = SOL_SOCKET;
} else {
cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level);
}
cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type);
cmsg->cmsg_len = CMSG_LEN(len);

if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) &&
(cmsg->cmsg_type == SCM_RIGHTS)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
int *fd = (int *)data;
int *target_fd = (int *)target_data;
int i, numfds = len / sizeof(int);

for (i = 0; i < numfds; i++) {
fd[i] = tswap32(target_fd[i]);
__get_user(fd[i], target_fd + i);
}
} else if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) &&
(cmsg->cmsg_type == SCM_TIMESTAMP) &&
(len == sizeof(struct timeval))) {
/* copy struct timeval to host */
struct timeval *tv = (struct timeval *)data;
struct target_freebsd_timeval *target_tv =
(struct target_freebsd_timeval *)target_data;
__get_user(tv->tv_sec, &target_tv->tv_sec);
__get_user(tv->tv_usec, &target_tv->tv_usec);
} else if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_CREDS) {
printf("XXX %s SCM_CREDS\n", __FUNCTION__);
} else {
gemu_log("Unsupported ancillary data: %d/%d\n",
cmsg->cmsg_level, cmsg->cmsg_type);
cmsg->cmsg_level, cmsg->cmsg_type);
memcpy(data, target_data, len);
}

cmsg = CMSG_NXTHDR(msgh, cmsg);
target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg,
target_cmsg_start);
}
unlock_user(target_cmsg, target_cmsg_addr, 0);

the_end:
msgh->msg_controllen = space;
return 0;
Expand All @@ -95,65 +104,146 @@ abi_long h2t_freebsd_cmsg(struct target_msghdr *target_msgh,
struct msghdr *msgh)
{
struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
abi_long msg_controllen;
socklen_t msg_controllen;
abi_ulong target_cmsg_addr;
struct target_cmsghdr *target_cmsg;
struct target_cmsghdr *target_cmsg, *target_cmsg_start;
socklen_t space = 0;

msg_controllen = tswapal(target_msgh->msg_controllen);
msg_controllen = tswap32(target_msgh->msg_controllen);
if (msg_controllen < sizeof(struct target_cmsghdr)) {
goto the_end;
}
target_cmsg_addr = tswapal(target_msgh->msg_control);
target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr,
msg_controllen, 0);
if (target_cmsg == 0) {
target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0);
target_cmsg_start = target_cmsg;
if (!target_cmsg)
return -TARGET_EFAULT;
}

while (cmsg && target_cmsg) {
void *data = CMSG_DATA(cmsg);
void *target_data = TARGET_CMSG_DATA(target_cmsg);
int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));

space += TARGET_CMSG_SPACE(len);
if (space > msg_controllen) {
space -= TARGET_CMSG_SPACE(len);
gemu_log("Target cmsg overflow\n");
int len = cmsg->cmsg_len - sizeof(struct cmsghdr);
int tgt_len, tgt_space;

/* We never copy a half-header but may copy half-data;
* this is Linux's behaviour in put_cmsg(). Note that
* truncation here is a guest problem (which we report
* to the guest via the CTRUNC bit), unlike truncation
* in target_to_host_cmsg, which is a QEMU bug.
*/
if (msg_controllen < sizeof(struct target_cmsghdr)) {
target_msgh->msg_flags |= tswap32(MSG_CTRUNC);
break;
}
target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level);

if (cmsg->cmsg_level == SOL_SOCKET) {
target_cmsg->cmsg_level = tswap32(TARGET_SOL_SOCKET);
} else {
target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level);
}
target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type);
target_cmsg->cmsg_len = tswapal(TARGET_CMSG_LEN(len));
if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) &&
(cmsg->cmsg_type == SCM_RIGHTS)) {
int *fd = (int *)data;
int *target_fd = (int *)target_data;
int i, numfds = len / sizeof(int);

for (i = 0; i < numfds; i++) {
target_fd[i] = tswap32(fd[i]);
/* Payload types which need a different size of payload on
* the target must adjust tgt_len here.
*/
tgt_len = len;
switch (cmsg->cmsg_level) {
case SOL_SOCKET:
switch (cmsg->cmsg_type) {
case SO_TIMESTAMP:
tgt_len = sizeof(struct target_freebsd_timeval);
break;
default:
break;
}
} else if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) &&
(cmsg->cmsg_type == SCM_TIMESTAMP) &&
(len == sizeof(struct timeval))) {
/* copy struct timeval to target */
struct timeval *tv = (struct timeval *)data;
struct target_freebsd_timeval *target_tv =
(struct target_freebsd_timeval *)target_data;
__put_user(tv->tv_sec, &target_tv->tv_sec);
__put_user(tv->tv_usec, &target_tv->tv_usec);
} else {
break;
default:
break;
}

if (msg_controllen < TARGET_CMSG_LEN(tgt_len)) {
target_msgh->msg_flags |= tswap32(MSG_CTRUNC);
tgt_len = msg_controllen - sizeof(struct target_cmsghdr);
}

/* We must now copy-and-convert len bytes of payload
* into tgt_len bytes of destination space. Bear in mind
* that in both source and destination we may be dealing
* with a truncated value!
*/
switch (cmsg->cmsg_level) {
case SOL_SOCKET:
switch (cmsg->cmsg_type) {
case SCM_RIGHTS:
{
int *fd = (int *)data;
int *target_fd = (int *)target_data;
int i, numfds = tgt_len / sizeof(int);

for (i = 0; i < numfds; i++) {
__put_user(fd[i], target_fd + i);
}
break;
}
case SO_TIMESTAMP:
{
struct timeval *tv = (struct timeval *)data;
struct target_freebsd_timeval *target_tv =
(struct target_freebsd_timeval *)target_data;

if (len != sizeof(struct timeval) ||
tgt_len != sizeof(struct target_freebsd_timeval)) {
goto unimplemented;
}

/* copy struct timeval to target */
__put_user(tv->tv_sec, &target_tv->tv_sec);
__put_user(tv->tv_usec, &target_tv->tv_usec);
break;
}
case SCM_CREDS:
{
printf("XXX %s SCM_CREDS\n", __FUNCTION__);
#if 0
struct ucred *cred = (struct ucred *)data;
struct target_ucred *target_cred =
(struct target_ucred *)target_data;

__put_user(cred->pid, &target_cred->pid);
__put_user(cred->uid, &target_cred->uid);
__put_user(cred->gid, &target_cred->gid);
#endif
break;
}
default:
goto unimplemented;
}
break; // switch (cmsg->cmsg_type)
default:
unimplemented:
gemu_log("Unsupported ancillary data: %d/%d\n",
cmsg->cmsg_level, cmsg->cmsg_type);
memcpy(target_data, data, len);
cmsg->cmsg_level, cmsg->cmsg_type);
memcpy(target_data, data, MIN(len, tgt_len));
if (tgt_len > len) {
memset(target_data + len, 0, tgt_len - len);
}
}

target_cmsg->cmsg_len = tswapal(TARGET_CMSG_LEN(tgt_len));
tgt_space = TARGET_CMSG_SPACE(tgt_len);
if (msg_controllen < tgt_space) {
tgt_space = msg_controllen;
}
msg_controllen -= tgt_space;
space += tgt_space;
cmsg = CMSG_NXTHDR(msgh, cmsg);
target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg,
target_cmsg_start);
}
unlock_user(target_cmsg, target_cmsg_addr, space);

the_end:
target_msgh->msg_controllen = tswapal(space);
target_msgh->msg_controllen = tswap32(space);
return 0;
}

Loading

0 comments on commit 1163019

Please sign in to comment.