aboutsummaryrefslogtreecommitdiff
path: root/linux-user
diff options
context:
space:
mode:
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2007-11-11 17:39:18 +0000
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2007-11-11 17:39:18 +0000
commit5a4a898d81406cdda342ec2bce5ca14d2070ccb3 (patch)
tree32153dc8f364c0a4aec5d52555c467c4fa3eb4f1 /linux-user
parent745cacc7b7e36aeb9bcc034f1de3d92eac1efc72 (diff)
improved cmsg handling - improved shm memory code
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3600 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'linux-user')
-rw-r--r--linux-user/syscall.c114
1 files changed, 72 insertions, 42 deletions
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index a7f41cf99f..402fa06aea 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -669,12 +669,22 @@ static inline abi_long host_to_target_sockaddr(abi_ulong target_addr,
}
/* ??? Should this also swap msgh->name? */
-static inline void target_to_host_cmsg(struct msghdr *msgh,
- struct target_msghdr *target_msgh)
+static inline abi_long target_to_host_cmsg(struct msghdr *msgh,
+ struct target_msghdr *target_msgh)
{
struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
- struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh);
+ abi_long msg_controllen;
+ abi_ulong target_cmsg_addr;
+ struct target_cmsghdr *target_cmsg;
socklen_t space = 0;
+
+ msg_controllen = tswapl(target_msgh->msg_controllen);
+ if (msg_controllen < sizeof (struct target_cmsghdr))
+ goto the_end;
+ target_cmsg_addr = tswapl(target_msgh->msg_control);
+ target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1);
+ if (!target_cmsg)
+ return -TARGET_EFAULT;
while (cmsg && target_cmsg) {
void *data = CMSG_DATA(cmsg);
@@ -709,18 +719,30 @@ static inline void target_to_host_cmsg(struct msghdr *msgh,
cmsg = CMSG_NXTHDR(msgh, cmsg);
target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
}
-
+ unlock_user(target_cmsg, target_cmsg_addr, 0);
+ the_end:
msgh->msg_controllen = space;
+ return 0;
}
/* ??? Should this also swap msgh->name? */
-static inline void host_to_target_cmsg(struct target_msghdr *target_msgh,
- struct msghdr *msgh)
+static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh,
+ struct msghdr *msgh)
{
struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
- struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh);
+ abi_long msg_controllen;
+ abi_ulong target_cmsg_addr;
+ struct target_cmsghdr *target_cmsg;
socklen_t space = 0;
+ msg_controllen = tswapl(target_msgh->msg_controllen);
+ if (msg_controllen < sizeof (struct target_cmsghdr))
+ goto the_end;
+ target_cmsg_addr = tswapl(target_msgh->msg_control);
+ target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0);
+ if (!target_cmsg)
+ return -TARGET_EFAULT;
+
while (cmsg && target_cmsg) {
void *data = CMSG_DATA(cmsg);
void *target_data = TARGET_CMSG_DATA(target_cmsg);
@@ -728,7 +750,7 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh,
int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr));
space += TARGET_CMSG_SPACE(len);
- if (space > tswapl(target_msgh->msg_controllen)) {
+ if (space > msg_controllen) {
space -= TARGET_CMSG_SPACE(len);
gemu_log("Target cmsg overflow\n");
break;
@@ -753,8 +775,10 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh,
cmsg = CMSG_NXTHDR(msgh, cmsg);
target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
}
-
- msgh->msg_controllen = tswapl(space);
+ unlock_user(target_cmsg, target_cmsg_addr, space);
+ the_end:
+ target_msgh->msg_controllen = tswapl(space);
+ return 0;
}
/* do_setsockopt() Must return target values and target errnos. */
@@ -1109,12 +1133,13 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
msg.msg_iov = vec;
if (send) {
- target_to_host_cmsg(&msg, msgp);
- ret = get_errno(sendmsg(fd, &msg, flags));
+ ret = target_to_host_cmsg(&msg, msgp);
+ if (ret == 0)
+ ret = get_errno(sendmsg(fd, &msg, flags));
} else {
ret = get_errno(recvmsg(fd, &msg, flags));
if (!is_error(ret))
- host_to_target_cmsg(msgp, &msg);
+ ret = host_to_target_cmsg(msgp, &msg);
}
unlock_iovec(vec, target_vec, count, !send);
unlock_user_struct(msgp, target_msg, send ? 0 : 1);
@@ -1409,8 +1434,8 @@ static abi_long do_socketcall(int num, abi_ulong vptr)
#define N_SHM_REGIONS 32
static struct shm_region {
- uint32_t start;
- uint32_t size;
+ abi_ulong start;
+ abi_ulong size;
} shm_regions[N_SHM_REGIONS];
struct target_ipc_perm
@@ -1776,7 +1801,6 @@ static abi_long do_ipc(unsigned int call, int first,
{
int version;
abi_long ret = 0;
- unsigned long raddr;
struct shmid_ds shm_info;
int i;
@@ -1831,32 +1855,38 @@ static abi_long do_ipc(unsigned int call, int first,
break;
case IPCOP_shmat:
- /* SHM_* flags are the same on all linux platforms */
- ret = get_errno((long) shmat(first, (void *) ptr, second));
- if (is_error(ret))
- break;
- raddr = ret;
- /* find out the length of the shared memory segment */
-
- ret = get_errno(shmctl(first, IPC_STAT, &shm_info));
- if (is_error(ret)) {
- /* can't get length, bail out */
- shmdt((void *) raddr);
- break;
- }
- page_set_flags(raddr, raddr + shm_info.shm_segsz,
- PAGE_VALID | PAGE_READ |
- ((second & SHM_RDONLY)? 0: PAGE_WRITE));
- for (i = 0; i < N_SHM_REGIONS; ++i) {
- if (shm_regions[i].start == 0) {
- shm_regions[i].start = raddr;
- shm_regions[i].size = shm_info.shm_segsz;
+ {
+ abi_ulong raddr;
+ void *host_addr;
+ /* SHM_* flags are the same on all linux platforms */
+ host_addr = shmat(first, (void *)g2h(ptr), second);
+ if (host_addr == (void *)-1) {
+ ret = get_errno((long)host_addr);
break;
- }
- }
- if (put_user(raddr, third, abi_ulong))
- return -TARGET_EFAULT;
- ret = 0;
+ }
+ raddr = h2g((unsigned long)host_addr);
+ /* find out the length of the shared memory segment */
+
+ ret = get_errno(shmctl(first, IPC_STAT, &shm_info));
+ if (is_error(ret)) {
+ /* can't get length, bail out */
+ shmdt(host_addr);
+ break;
+ }
+ page_set_flags(raddr, raddr + shm_info.shm_segsz,
+ PAGE_VALID | PAGE_READ |
+ ((second & SHM_RDONLY)? 0: PAGE_WRITE));
+ for (i = 0; i < N_SHM_REGIONS; ++i) {
+ if (shm_regions[i].start == 0) {
+ shm_regions[i].start = raddr;
+ shm_regions[i].size = shm_info.shm_segsz;
+ break;
+ }
+ }
+ if (put_user(raddr, third, abi_ulong))
+ return -TARGET_EFAULT;
+ ret = 0;
+ }
break;
case IPCOP_shmdt:
for (i = 0; i < N_SHM_REGIONS; ++i) {
@@ -1866,7 +1896,7 @@ static abi_long do_ipc(unsigned int call, int first,
break;
}
}
- ret = get_errno(shmdt((void *) ptr));
+ ret = get_errno(shmdt((void *)g2h(ptr)));
break;
case IPCOP_shmget: