|
| 1 | +From Dan Gohman < [email protected]> |
| 2 | +Subject: [PATCH] Remove the `FD_SETSIZE` limitation in `select` |
| 3 | + |
| 4 | +The `fd_set` type is limited to a fixed `FD_SETSIZE` number of file |
| 5 | +descriptors, however Linux's `select has no such limitation. Change |
| 6 | +the `select` implementation to using manual bit-vector logic to better |
| 7 | +implement the Linux semantics. |
| 8 | + |
| 9 | +diff -ur a/linux-user/syscall.c b/linux-user/syscall.c |
| 10 | +--- a/linux-user/syscall.c |
| 11 | ++++ b/linux-user/syscall.c |
| 12 | +@@ -664,8 +664,9 @@ |
| 13 | + char **, argv, char **, envp, int, flags) |
| 14 | + #if defined(TARGET_NR_select) || defined(TARGET_NR__newselect) || \ |
| 15 | + defined(TARGET_NR_pselect6) || defined(TARGET_NR_pselect6_time64) |
| 16 | +-safe_syscall6(int, pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, \ |
| 17 | +- fd_set *, exceptfds, struct timespec *, timeout, void *, sig) |
| 18 | ++safe_syscall6(int, pselect6, int, nfds, unsigned long *, readfds, \ |
| 19 | ++ unsigned long *, writefds, unsigned long *, exceptfds, \ |
| 20 | ++ struct timespec *, timeout, void *, sig) |
| 21 | + #endif |
| 22 | + #if defined(TARGET_NR_ppoll) || defined(TARGET_NR_ppoll_time64) |
| 23 | + safe_syscall5(int, ppoll, struct pollfd *, ufds, unsigned int, nfds, |
| 24 | +@@ -861,7 +862,7 @@ |
| 25 | + |
| 26 | + #if defined(TARGET_NR_select) || defined(TARGET_NR__newselect) || \ |
| 27 | + defined(TARGET_NR_pselect6) || defined(TARGET_NR_pselect6_time64) |
| 28 | +-static inline abi_long copy_from_user_fdset(fd_set *fds, |
| 29 | ++static inline abi_long copy_from_user_fdset(unsigned long *fds, |
| 30 | + abi_ulong target_fds_addr, |
| 31 | + int n) |
| 32 | + { |
| 33 | +@@ -875,7 +876,8 @@ |
| 34 | + 1))) |
| 35 | + return -TARGET_EFAULT; |
| 36 | + |
| 37 | +- FD_ZERO(fds); |
| 38 | ++ memset(fds, 0, DIV_ROUND_UP(n, sizeof(unsigned long) * 8) * |
| 39 | ++ sizeof(unsigned long)); |
| 40 | + k = 0; |
| 41 | + for (i = 0; i < nw; i++) { |
| 42 | + /* grab the abi_ulong */ |
| 43 | +@@ -883,7 +885,8 @@ |
| 44 | + for (j = 0; j < TARGET_ABI_BITS; j++) { |
| 45 | + /* check the bit inside the abi_ulong */ |
| 46 | + if ((b >> j) & 1) |
| 47 | +- FD_SET(k, fds); |
| 48 | ++ fds[k / (sizeof(unsigned long) * 8)] |= |
| 49 | ++ 1ul << (k % (sizeof(unsigned long) * 8)); |
| 50 | + k++; |
| 51 | + } |
| 52 | + } |
| 53 | +@@ -893,7 +896,8 @@ |
| 54 | + return 0; |
| 55 | + } |
| 56 | + |
| 57 | +-static inline abi_ulong copy_from_user_fdset_ptr(fd_set *fds, fd_set **fds_ptr, |
| 58 | ++static inline abi_ulong copy_from_user_fdset_ptr(unsigned long *fds, |
| 59 | ++ unsigned long **fds_ptr, |
| 60 | + abi_ulong target_fds_addr, |
| 61 | + int n) |
| 62 | + { |
| 63 | +@@ -908,7 +912,7 @@ |
| 64 | + } |
| 65 | + |
| 66 | + static inline abi_long copy_to_user_fdset(abi_ulong target_fds_addr, |
| 67 | +- const fd_set *fds, |
| 68 | ++ const unsigned long *fds, |
| 69 | + int n) |
| 70 | + { |
| 71 | + int i, nw, j, k; |
| 72 | +@@ -926,7 +930,10 @@ |
| 73 | + for (i = 0; i < nw; i++) { |
| 74 | + v = 0; |
| 75 | + for (j = 0; j < TARGET_ABI_BITS; j++) { |
| 76 | +- v |= ((abi_ulong)(FD_ISSET(k, fds) != 0) << j); |
| 77 | ++ bool set = |
| 78 | ++ (fds[k / (sizeof(unsigned long) * 8)] & |
| 79 | ++ (1ul << (k % (sizeof(unsigned long) * 8)))) != 0; |
| 80 | ++ v |= ((abi_ulong)set << j); |
| 81 | + k++; |
| 82 | + } |
| 83 | + __put_user(v, &target_fds[i]); |
| 84 | +@@ -1295,28 +1302,40 @@ |
| 85 | + abi_ulong rfd_addr, abi_ulong wfd_addr, |
| 86 | + abi_ulong efd_addr, abi_ulong target_tv_addr) |
| 87 | + { |
| 88 | +- fd_set rfds, wfds, efds; |
| 89 | +- fd_set *rfds_ptr, *wfds_ptr, *efds_ptr; |
| 90 | ++ unsigned long *rfds, *wfds, *efds; |
| 91 | ++ unsigned long *rfds_ptr, *wfds_ptr, *efds_ptr; |
| 92 | + struct timeval tv; |
| 93 | + struct timespec ts, *ts_ptr; |
| 94 | + abi_long ret; |
| 95 | + |
| 96 | +- ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); |
| 97 | ++ rfds = malloc(DIV_ROUND_UP(n, sizeof(unsigned long) * 8) * |
| 98 | ++ sizeof(unsigned long)); |
| 99 | ++ wfds = malloc(DIV_ROUND_UP(n, sizeof(unsigned long) * 8) * |
| 100 | ++ sizeof(unsigned long)); |
| 101 | ++ efds = malloc(DIV_ROUND_UP(n, sizeof(unsigned long) * 8) * |
| 102 | ++ sizeof(unsigned long)); |
| 103 | ++ |
| 104 | ++ ret = copy_from_user_fdset_ptr(rfds, &rfds_ptr, rfd_addr, n); |
| 105 | + if (ret) { |
| 106 | ++ free(rfds); free(wfds); free(efds); |
| 107 | + return ret; |
| 108 | + } |
| 109 | +- ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n); |
| 110 | ++ ret = copy_from_user_fdset_ptr(wfds, &wfds_ptr, wfd_addr, n); |
| 111 | + if (ret) { |
| 112 | ++ free(rfds); free(wfds); free(efds); |
| 113 | + return ret; |
| 114 | + } |
| 115 | +- ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n); |
| 116 | ++ ret = copy_from_user_fdset_ptr(efds, &efds_ptr, efd_addr, n); |
| 117 | + if (ret) { |
| 118 | ++ free(rfds); free(wfds); free(efds); |
| 119 | + return ret; |
| 120 | + } |
| 121 | + |
| 122 | + if (target_tv_addr) { |
| 123 | +- if (copy_from_user_timeval(&tv, target_tv_addr)) |
| 124 | ++ if (copy_from_user_timeval(&tv, target_tv_addr)) { |
| 125 | ++ free(rfds); free(wfds); free(efds); |
| 126 | + return -TARGET_EFAULT; |
| 127 | ++ } |
| 128 | + ts.tv_sec = tv.tv_sec; |
| 129 | + ts.tv_nsec = tv.tv_usec * 1000; |
| 130 | + ts_ptr = &ts; |
| 131 | +@@ -1328,22 +1347,30 @@ |
| 132 | + ts_ptr, NULL)); |
| 133 | + |
| 134 | + if (!is_error(ret)) { |
| 135 | +- if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n)) |
| 136 | ++ if (rfd_addr && copy_to_user_fdset(rfd_addr, rfds, n)) { |
| 137 | ++ free(rfds); free(wfds); free(efds); |
| 138 | + return -TARGET_EFAULT; |
| 139 | +- if (wfd_addr && copy_to_user_fdset(wfd_addr, &wfds, n)) |
| 140 | ++ } |
| 141 | ++ if (wfd_addr && copy_to_user_fdset(wfd_addr, wfds, n)) { |
| 142 | ++ free(rfds); free(wfds); free(efds); |
| 143 | + return -TARGET_EFAULT; |
| 144 | +- if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n)) |
| 145 | ++ } |
| 146 | ++ if (efd_addr && copy_to_user_fdset(efd_addr, efds, n)) { |
| 147 | ++ free(rfds); free(wfds); free(efds); |
| 148 | + return -TARGET_EFAULT; |
| 149 | ++ } |
| 150 | + |
| 151 | + if (target_tv_addr) { |
| 152 | + tv.tv_sec = ts.tv_sec; |
| 153 | + tv.tv_usec = ts.tv_nsec / 1000; |
| 154 | + if (copy_to_user_timeval(target_tv_addr, &tv)) { |
| 155 | ++ free(rfds); free(wfds); free(efds); |
| 156 | + return -TARGET_EFAULT; |
| 157 | + } |
| 158 | + } |
| 159 | + } |
| 160 | + |
| 161 | ++ free(rfds); free(wfds); free(efds); |
| 162 | + return ret; |
| 163 | + } |
| 164 | + |
| 165 | +@@ -1377,8 +1404,8 @@ |
| 166 | + bool time64) |
| 167 | + { |
| 168 | + abi_long rfd_addr, wfd_addr, efd_addr, n, ts_addr; |
| 169 | +- fd_set rfds, wfds, efds; |
| 170 | +- fd_set *rfds_ptr, *wfds_ptr, *efds_ptr; |
| 171 | ++ unsigned long *rfds, *wfds, *efds; |
| 172 | ++ unsigned long *rfds_ptr, *wfds_ptr, *efds_ptr; |
| 173 | + struct timespec ts, *ts_ptr; |
| 174 | + abi_long ret; |
| 175 | + |
| 176 | +@@ -1399,16 +1426,26 @@ |
| 177 | + efd_addr = arg4; |
| 178 | + ts_addr = arg5; |
| 179 | + |
| 180 | +- ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); |
| 181 | ++ rfds = malloc(DIV_ROUND_UP(n, sizeof(unsigned long) * 8) * |
| 182 | ++ sizeof(unsigned long)); |
| 183 | ++ wfds = malloc(DIV_ROUND_UP(n, sizeof(unsigned long) * 8) * |
| 184 | ++ sizeof(unsigned long)); |
| 185 | ++ efds = malloc(DIV_ROUND_UP(n, sizeof(unsigned long) * 8) * |
| 186 | ++ sizeof(unsigned long)); |
| 187 | ++ |
| 188 | ++ ret = copy_from_user_fdset_ptr(rfds, &rfds_ptr, rfd_addr, n); |
| 189 | + if (ret) { |
| 190 | ++ free(rfds); free(wfds); free(efds); |
| 191 | + return ret; |
| 192 | + } |
| 193 | +- ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n); |
| 194 | ++ ret = copy_from_user_fdset_ptr(wfds, &wfds_ptr, wfd_addr, n); |
| 195 | + if (ret) { |
| 196 | ++ free(rfds); free(wfds); free(efds); |
| 197 | + return ret; |
| 198 | + } |
| 199 | +- ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n); |
| 200 | ++ ret = copy_from_user_fdset_ptr(efds, &efds_ptr, efd_addr, n); |
| 201 | + if (ret) { |
| 202 | ++ free(rfds); free(wfds); free(efds); |
| 203 | + return ret; |
| 204 | + } |
| 205 | + |
| 206 | +@@ -1419,10 +1456,12 @@ |
| 207 | + if (ts_addr) { |
| 208 | + if (time64) { |
| 209 | + if (target_to_host_timespec64(&ts, ts_addr)) { |
| 210 | ++ free(rfds); free(wfds); free(efds); |
| 211 | + return -TARGET_EFAULT; |
| 212 | + } |
| 213 | + } else { |
| 214 | + if (target_to_host_timespec(&ts, ts_addr)) { |
| 215 | ++ free(rfds); free(wfds); free(efds); |
| 216 | + return -TARGET_EFAULT; |
| 217 | + } |
| 218 | + } |
| 219 | +@@ -1436,6 +1475,7 @@ |
| 220 | + if (arg6) { |
| 221 | + arg7 = lock_user(VERIFY_READ, arg6, sizeof(*arg7) * 2, 1); |
| 222 | + if (!arg7) { |
| 223 | ++ free(rfds); free(wfds); free(efds); |
| 224 | + return -TARGET_EFAULT; |
| 225 | + } |
| 226 | + arg_sigset = tswapal(arg7[0]); |
| 227 | +@@ -1445,6 +1485,7 @@ |
| 228 | + if (arg_sigset) { |
| 229 | + ret = process_sigsuspend_mask(&sig.set, arg_sigset, arg_sigsize); |
| 230 | + if (ret != 0) { |
| 231 | ++ free(rfds); free(wfds); free(efds); |
| 232 | + return ret; |
| 233 | + } |
| 234 | + sig_ptr = &sig; |
| 235 | +@@ -1460,25 +1501,31 @@ |
| 236 | + } |
| 237 | + |
| 238 | + if (!is_error(ret)) { |
| 239 | +- if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n)) { |
| 240 | ++ if (rfd_addr && copy_to_user_fdset(rfd_addr, rfds, n)) { |
| 241 | ++ free(rfds); free(wfds); free(efds); |
| 242 | + return -TARGET_EFAULT; |
| 243 | + } |
| 244 | +- if (wfd_addr && copy_to_user_fdset(wfd_addr, &wfds, n)) { |
| 245 | ++ if (wfd_addr && copy_to_user_fdset(wfd_addr, wfds, n)) { |
| 246 | ++ free(rfds); free(wfds); free(efds); |
| 247 | + return -TARGET_EFAULT; |
| 248 | + } |
| 249 | +- if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n)) { |
| 250 | ++ if (efd_addr && copy_to_user_fdset(efd_addr, efds, n)) { |
| 251 | ++ free(rfds); free(wfds); free(efds); |
| 252 | + return -TARGET_EFAULT; |
| 253 | + } |
| 254 | + if (time64) { |
| 255 | + if (ts_addr && host_to_target_timespec64(ts_addr, &ts)) { |
| 256 | ++ free(rfds); free(wfds); free(efds); |
| 257 | + return -TARGET_EFAULT; |
| 258 | + } |
| 259 | + } else { |
| 260 | + if (ts_addr && host_to_target_timespec(ts_addr, &ts)) { |
| 261 | ++ free(rfds); free(wfds); free(efds); |
| 262 | + return -TARGET_EFAULT; |
| 263 | + } |
| 264 | + } |
| 265 | + } |
| 266 | ++ free(rfds); free(wfds); free(efds); |
| 267 | + return ret; |
| 268 | + } |
| 269 | + #endif |
0 commit comments