Commit 5a4a898d81406cdda342ec2bce5ca14d2070ccb3

Authored by bellard
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,12 +669,22 @@ static inline abi_long host_to_target_sockaddr(abi_ulong target_addr,
669 } 669 }
670 670
671 /* ??? Should this also swap msgh->name? */ 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 struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); 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 socklen_t space = 0; 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 while (cmsg && target_cmsg) { 689 while (cmsg && target_cmsg) {
680 void *data = CMSG_DATA(cmsg); 690 void *data = CMSG_DATA(cmsg);
@@ -709,18 +719,30 @@ static inline void target_to_host_cmsg(struct msghdr *msgh, @@ -709,18 +719,30 @@ static inline void target_to_host_cmsg(struct msghdr *msgh,
709 cmsg = CMSG_NXTHDR(msgh, cmsg); 719 cmsg = CMSG_NXTHDR(msgh, cmsg);
710 target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); 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 msgh->msg_controllen = space; 724 msgh->msg_controllen = space;
  725 + return 0;
714 } 726 }
715 727
716 /* ??? Should this also swap msgh->name? */ 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 struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); 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 socklen_t space = 0; 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 while (cmsg && target_cmsg) { 746 while (cmsg && target_cmsg) {
725 void *data = CMSG_DATA(cmsg); 747 void *data = CMSG_DATA(cmsg);
726 void *target_data = TARGET_CMSG_DATA(target_cmsg); 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,7 +750,7 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh,
728 int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr)); 750 int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr));
729 751
730 space += TARGET_CMSG_SPACE(len); 752 space += TARGET_CMSG_SPACE(len);
731 - if (space > tswapl(target_msgh->msg_controllen)) { 753 + if (space > msg_controllen) {
732 space -= TARGET_CMSG_SPACE(len); 754 space -= TARGET_CMSG_SPACE(len);
733 gemu_log("Target cmsg overflow\n"); 755 gemu_log("Target cmsg overflow\n");
734 break; 756 break;
@@ -753,8 +775,10 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh, @@ -753,8 +775,10 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh,
753 cmsg = CMSG_NXTHDR(msgh, cmsg); 775 cmsg = CMSG_NXTHDR(msgh, cmsg);
754 target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); 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 /* do_setsockopt() Must return target values and target errnos. */ 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,12 +1133,13 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
1109 msg.msg_iov = vec; 1133 msg.msg_iov = vec;
1110 1134
1111 if (send) { 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 } else { 1139 } else {
1115 ret = get_errno(recvmsg(fd, &msg, flags)); 1140 ret = get_errno(recvmsg(fd, &msg, flags));
1116 if (!is_error(ret)) 1141 if (!is_error(ret))
1117 - host_to_target_cmsg(msgp, &msg); 1142 + ret = host_to_target_cmsg(msgp, &msg);
1118 } 1143 }
1119 unlock_iovec(vec, target_vec, count, !send); 1144 unlock_iovec(vec, target_vec, count, !send);
1120 unlock_user_struct(msgp, target_msg, send ? 0 : 1); 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,8 +1434,8 @@ static abi_long do_socketcall(int num, abi_ulong vptr)
1409 #define N_SHM_REGIONS 32 1434 #define N_SHM_REGIONS 32
1410 1435
1411 static struct shm_region { 1436 static struct shm_region {
1412 - uint32_t start;  
1413 - uint32_t size; 1437 + abi_ulong start;
  1438 + abi_ulong size;
1414 } shm_regions[N_SHM_REGIONS]; 1439 } shm_regions[N_SHM_REGIONS];
1415 1440
1416 struct target_ipc_perm 1441 struct target_ipc_perm
@@ -1776,7 +1801,6 @@ static abi_long do_ipc(unsigned int call, int first, @@ -1776,7 +1801,6 @@ static abi_long do_ipc(unsigned int call, int first,
1776 { 1801 {
1777 int version; 1802 int version;
1778 abi_long ret = 0; 1803 abi_long ret = 0;
1779 - unsigned long raddr;  
1780 struct shmid_ds shm_info; 1804 struct shmid_ds shm_info;
1781 int i; 1805 int i;
1782 1806
@@ -1831,32 +1855,38 @@ static abi_long do_ipc(unsigned int call, int first, @@ -1831,32 +1855,38 @@ static abi_long do_ipc(unsigned int call, int first,
1831 break; 1855 break;
1832 1856
1833 case IPCOP_shmat: 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 break; 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 break; 1890 break;
1861 case IPCOP_shmdt: 1891 case IPCOP_shmdt:
1862 for (i = 0; i < N_SHM_REGIONS; ++i) { 1892 for (i = 0; i < N_SHM_REGIONS; ++i) {
@@ -1866,7 +1896,7 @@ static abi_long do_ipc(unsigned int call, int first, @@ -1866,7 +1896,7 @@ static abi_long do_ipc(unsigned int call, int first,
1866 break; 1896 break;
1867 } 1897 }
1868 } 1898 }
1869 - ret = get_errno(shmdt((void *) ptr)); 1899 + ret = get_errno(shmdt((void *)g2h(ptr)));
1870 break; 1900 break;
1871 1901
1872 case IPCOP_shmget: 1902 case IPCOP_shmget: