Commit 5a4a898d81406cdda342ec2bce5ca14d2070ccb3
1 parent
745cacc7
improved cmsg handling - improved shm memory code
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3600 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
72 additions
and
42 deletions
linux-user/syscall.c
... | ... | @@ -669,12 +669,22 @@ static inline abi_long host_to_target_sockaddr(abi_ulong target_addr, |
669 | 669 | } |
670 | 670 | |
671 | 671 | /* ??? Should this also swap msgh->name? */ |
672 | -static inline void target_to_host_cmsg(struct msghdr *msgh, | |
673 | - struct target_msghdr *target_msgh) | |
672 | +static inline abi_long target_to_host_cmsg(struct msghdr *msgh, | |
673 | + struct target_msghdr *target_msgh) | |
674 | 674 | { |
675 | 675 | struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); |
676 | - struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh); | |
676 | + abi_long msg_controllen; | |
677 | + abi_ulong target_cmsg_addr; | |
678 | + struct target_cmsghdr *target_cmsg; | |
677 | 679 | socklen_t space = 0; |
680 | + | |
681 | + msg_controllen = tswapl(target_msgh->msg_controllen); | |
682 | + if (msg_controllen < sizeof (struct target_cmsghdr)) | |
683 | + goto the_end; | |
684 | + target_cmsg_addr = tswapl(target_msgh->msg_control); | |
685 | + target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1); | |
686 | + if (!target_cmsg) | |
687 | + return -TARGET_EFAULT; | |
678 | 688 | |
679 | 689 | while (cmsg && target_cmsg) { |
680 | 690 | void *data = CMSG_DATA(cmsg); |
... | ... | @@ -709,18 +719,30 @@ static inline void target_to_host_cmsg(struct msghdr *msgh, |
709 | 719 | cmsg = CMSG_NXTHDR(msgh, cmsg); |
710 | 720 | target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); |
711 | 721 | } |
712 | - | |
722 | + unlock_user(target_cmsg, target_cmsg_addr, 0); | |
723 | + the_end: | |
713 | 724 | msgh->msg_controllen = space; |
725 | + return 0; | |
714 | 726 | } |
715 | 727 | |
716 | 728 | /* ??? Should this also swap msgh->name? */ |
717 | -static inline void host_to_target_cmsg(struct target_msghdr *target_msgh, | |
718 | - struct msghdr *msgh) | |
729 | +static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, | |
730 | + struct msghdr *msgh) | |
719 | 731 | { |
720 | 732 | struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); |
721 | - struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh); | |
733 | + abi_long msg_controllen; | |
734 | + abi_ulong target_cmsg_addr; | |
735 | + struct target_cmsghdr *target_cmsg; | |
722 | 736 | socklen_t space = 0; |
723 | 737 | |
738 | + msg_controllen = tswapl(target_msgh->msg_controllen); | |
739 | + if (msg_controllen < sizeof (struct target_cmsghdr)) | |
740 | + goto the_end; | |
741 | + target_cmsg_addr = tswapl(target_msgh->msg_control); | |
742 | + target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0); | |
743 | + if (!target_cmsg) | |
744 | + return -TARGET_EFAULT; | |
745 | + | |
724 | 746 | while (cmsg && target_cmsg) { |
725 | 747 | void *data = CMSG_DATA(cmsg); |
726 | 748 | void *target_data = TARGET_CMSG_DATA(target_cmsg); |
... | ... | @@ -728,7 +750,7 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh, |
728 | 750 | int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr)); |
729 | 751 | |
730 | 752 | space += TARGET_CMSG_SPACE(len); |
731 | - if (space > tswapl(target_msgh->msg_controllen)) { | |
753 | + if (space > msg_controllen) { | |
732 | 754 | space -= TARGET_CMSG_SPACE(len); |
733 | 755 | gemu_log("Target cmsg overflow\n"); |
734 | 756 | break; |
... | ... | @@ -753,8 +775,10 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh, |
753 | 775 | cmsg = CMSG_NXTHDR(msgh, cmsg); |
754 | 776 | target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); |
755 | 777 | } |
756 | - | |
757 | - msgh->msg_controllen = tswapl(space); | |
778 | + unlock_user(target_cmsg, target_cmsg_addr, space); | |
779 | + the_end: | |
780 | + target_msgh->msg_controllen = tswapl(space); | |
781 | + return 0; | |
758 | 782 | } |
759 | 783 | |
760 | 784 | /* do_setsockopt() Must return target values and target errnos. */ |
... | ... | @@ -1109,12 +1133,13 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg, |
1109 | 1133 | msg.msg_iov = vec; |
1110 | 1134 | |
1111 | 1135 | if (send) { |
1112 | - target_to_host_cmsg(&msg, msgp); | |
1113 | - ret = get_errno(sendmsg(fd, &msg, flags)); | |
1136 | + ret = target_to_host_cmsg(&msg, msgp); | |
1137 | + if (ret == 0) | |
1138 | + ret = get_errno(sendmsg(fd, &msg, flags)); | |
1114 | 1139 | } else { |
1115 | 1140 | ret = get_errno(recvmsg(fd, &msg, flags)); |
1116 | 1141 | if (!is_error(ret)) |
1117 | - host_to_target_cmsg(msgp, &msg); | |
1142 | + ret = host_to_target_cmsg(msgp, &msg); | |
1118 | 1143 | } |
1119 | 1144 | unlock_iovec(vec, target_vec, count, !send); |
1120 | 1145 | unlock_user_struct(msgp, target_msg, send ? 0 : 1); |
... | ... | @@ -1409,8 +1434,8 @@ static abi_long do_socketcall(int num, abi_ulong vptr) |
1409 | 1434 | #define N_SHM_REGIONS 32 |
1410 | 1435 | |
1411 | 1436 | static struct shm_region { |
1412 | - uint32_t start; | |
1413 | - uint32_t size; | |
1437 | + abi_ulong start; | |
1438 | + abi_ulong size; | |
1414 | 1439 | } shm_regions[N_SHM_REGIONS]; |
1415 | 1440 | |
1416 | 1441 | struct target_ipc_perm |
... | ... | @@ -1776,7 +1801,6 @@ static abi_long do_ipc(unsigned int call, int first, |
1776 | 1801 | { |
1777 | 1802 | int version; |
1778 | 1803 | abi_long ret = 0; |
1779 | - unsigned long raddr; | |
1780 | 1804 | struct shmid_ds shm_info; |
1781 | 1805 | int i; |
1782 | 1806 | |
... | ... | @@ -1831,32 +1855,38 @@ static abi_long do_ipc(unsigned int call, int first, |
1831 | 1855 | break; |
1832 | 1856 | |
1833 | 1857 | case IPCOP_shmat: |
1834 | - /* SHM_* flags are the same on all linux platforms */ | |
1835 | - ret = get_errno((long) shmat(first, (void *) ptr, second)); | |
1836 | - if (is_error(ret)) | |
1837 | - break; | |
1838 | - raddr = ret; | |
1839 | - /* find out the length of the shared memory segment */ | |
1840 | - | |
1841 | - ret = get_errno(shmctl(first, IPC_STAT, &shm_info)); | |
1842 | - if (is_error(ret)) { | |
1843 | - /* can't get length, bail out */ | |
1844 | - shmdt((void *) raddr); | |
1845 | - break; | |
1846 | - } | |
1847 | - page_set_flags(raddr, raddr + shm_info.shm_segsz, | |
1848 | - PAGE_VALID | PAGE_READ | | |
1849 | - ((second & SHM_RDONLY)? 0: PAGE_WRITE)); | |
1850 | - for (i = 0; i < N_SHM_REGIONS; ++i) { | |
1851 | - if (shm_regions[i].start == 0) { | |
1852 | - shm_regions[i].start = raddr; | |
1853 | - shm_regions[i].size = shm_info.shm_segsz; | |
1858 | + { | |
1859 | + abi_ulong raddr; | |
1860 | + void *host_addr; | |
1861 | + /* SHM_* flags are the same on all linux platforms */ | |
1862 | + host_addr = shmat(first, (void *)g2h(ptr), second); | |
1863 | + if (host_addr == (void *)-1) { | |
1864 | + ret = get_errno((long)host_addr); | |
1854 | 1865 | break; |
1855 | - } | |
1856 | - } | |
1857 | - if (put_user(raddr, third, abi_ulong)) | |
1858 | - return -TARGET_EFAULT; | |
1859 | - ret = 0; | |
1866 | + } | |
1867 | + raddr = h2g((unsigned long)host_addr); | |
1868 | + /* find out the length of the shared memory segment */ | |
1869 | + | |
1870 | + ret = get_errno(shmctl(first, IPC_STAT, &shm_info)); | |
1871 | + if (is_error(ret)) { | |
1872 | + /* can't get length, bail out */ | |
1873 | + shmdt(host_addr); | |
1874 | + break; | |
1875 | + } | |
1876 | + page_set_flags(raddr, raddr + shm_info.shm_segsz, | |
1877 | + PAGE_VALID | PAGE_READ | | |
1878 | + ((second & SHM_RDONLY)? 0: PAGE_WRITE)); | |
1879 | + for (i = 0; i < N_SHM_REGIONS; ++i) { | |
1880 | + if (shm_regions[i].start == 0) { | |
1881 | + shm_regions[i].start = raddr; | |
1882 | + shm_regions[i].size = shm_info.shm_segsz; | |
1883 | + break; | |
1884 | + } | |
1885 | + } | |
1886 | + if (put_user(raddr, third, abi_ulong)) | |
1887 | + return -TARGET_EFAULT; | |
1888 | + ret = 0; | |
1889 | + } | |
1860 | 1890 | break; |
1861 | 1891 | case IPCOP_shmdt: |
1862 | 1892 | for (i = 0; i < N_SHM_REGIONS; ++i) { |
... | ... | @@ -1866,7 +1896,7 @@ static abi_long do_ipc(unsigned int call, int first, |
1866 | 1896 | break; |
1867 | 1897 | } |
1868 | 1898 | } |
1869 | - ret = get_errno(shmdt((void *) ptr)); | |
1899 | + ret = get_errno(shmdt((void *)g2h(ptr))); | |
1870 | 1900 | break; |
1871 | 1901 | |
1872 | 1902 | case IPCOP_shmget: | ... | ... |