The Pedigree Project 0.1
select-syscalls.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/Subsystem.h"
21#include "pedigree/kernel/compiler.h"
22#include "pedigree/kernel/process/Thread.h"
23#include "pedigree/kernel/processor/Processor.h"
24#include "pedigree/kernel/processor/types.h"
25#include "pedigree/kernel/syscallError.h"
26#include "pedigree/kernel/time/Time.h"
27
28#include <PosixSubsystem.h>
29#include <signal.h>
30
31#include "net-syscalls.h"
32#include "poll-syscalls.h"
33#include "select-syscalls.h"
34#include <sys/time.h>
35
36namespace {
37constexpr int MaximumSelectDescriptors = 16384;
38constexpr size_t SelectWordBits = sizeof(uintptr_t) * 8;
39constexpr size_t LinuxKernelSigsetSize = sizeof(uint64_t);
40constexpr uint64_t UnblockableSignals =
41 (static_cast<uint64_t>(1) << (SIGKILL - 1)) | (static_cast<uint64_t>(1) << (SIGSTOP - 1));
42constexpr short SelectReadReady = POLLIN | POLLRDNORM | POLLRDBAND | POLLHUP | POLLERR;
43constexpr short SelectWriteReady = POLLOUT | POLLWRNORM | POLLWRBAND | POLLERR;
44constexpr short SelectExceptionalReady = POLLPRI;
45
46struct SelectProjection {
47 bool read;
48 bool write;
49 bool exceptional;
50 int count;
51};
52
53SelectProjection projectSelectReadiness(short revents, bool checkRead, bool checkWrite,
54 bool checkExceptional) {
55 SelectProjection result = {
56 checkRead && (revents & SelectReadReady),
57 checkWrite && (revents & SelectWriteReady),
58 checkExceptional && (revents & SelectExceptionalReady),
59 0,
60 };
61 result.count = static_cast<int>(result.read) + static_cast<int>(result.write) +
62 static_cast<int>(result.exceptional);
63 return result;
64}
65
66size_t selectBitmapBytes(int nfds) {
67 if (!nfds) {
68 return 0;
69 }
70 return ((static_cast<size_t>(nfds) + SelectWordBits - 1) / SelectWordBits) * sizeof(uintptr_t);
71}
72
73bool selectBitIsSet(const uintptr_t* bitmap, size_t descriptor) {
74 return bitmap[descriptor / SelectWordBits] &
75 (static_cast<uintptr_t>(1) << (descriptor % SelectWordBits));
76}
77
78void setSelectBit(uintptr_t* bitmap, size_t descriptor) {
79 bitmap[descriptor / SelectWordBits] |= static_cast<uintptr_t>(1) << (descriptor % SelectWordBits);
80}
81
82struct SelectBitmaps {
83 explicit SelectBitmaps(size_t bitmapBytes)
84 : storage(nullptr),
85 readInput(nullptr),
86 writeInput(nullptr),
87 errorInput(nullptr),
88 readResult(nullptr),
89 writeResult(nullptr),
90 errorResult(nullptr),
91 wordCount(bitmapBytes / sizeof(uintptr_t)) {
92 if (!wordCount) {
93 return;
94 }
95
96 storage = new uintptr_t[wordCount * 6];
97 readInput = storage;
98 writeInput = readInput + wordCount;
99 errorInput = writeInput + wordCount;
100 readResult = errorInput + wordCount;
101 writeResult = readResult + wordCount;
102 errorResult = writeResult + wordCount;
103 for (size_t i = 0; i < wordCount * 6; ++i) {
104 storage[i] = 0;
105 }
106 }
107
108 ~SelectBitmaps() {
109 delete[] storage;
110 }
111
112 uintptr_t* storage;
113 uintptr_t* readInput;
114 uintptr_t* writeInput;
115 uintptr_t* errorInput;
116 uintptr_t* readResult;
117 uintptr_t* writeResult;
118 uintptr_t* errorResult;
119 size_t wordCount;
120};
121
122bool importSelectTimeval(timeval* userTimeout, PollDeadline& deadline) {
123 if (!userTimeout) {
124 deadline = posix_poll_deadline(nullptr);
125 return true;
126 }
127
128 timeval snapshot = {0, 0};
129 if (!PosixSubsystem::copyFromUser(&snapshot, userTimeout, sizeof(snapshot))) {
130 SYSCALL_ERROR(BadAddress);
131 return false;
132 }
133 if (snapshot.tv_sec < 0 || snapshot.tv_usec < 0) {
134 SYSCALL_ERROR(InvalidArgument);
135 return false;
136 }
137
138 const uint64_t microseconds = static_cast<uint64_t>(snapshot.tv_usec);
139 const uint64_t additionalSeconds = microseconds / 1000000;
140 const uint64_t seconds = static_cast<uint64_t>(snapshot.tv_sec);
141 const uint64_t maximumSeconds = static_cast<uint64_t>(INT64_MAX);
142 LinuxKernelTimespec normalized = {0, 0};
143 if (seconds > maximumSeconds - additionalSeconds) {
144 normalized.tv_sec = INT64_MAX;
145 normalized.tv_nsec = static_cast<int64_t>(Time::Multiplier::Second - 1);
146 } else {
147 normalized.tv_sec = static_cast<int64_t>(seconds + additionalSeconds);
148 normalized.tv_nsec = static_cast<int64_t>(microseconds % 1000000) *
149 static_cast<int64_t>(Time::Multiplier::Microsecond);
150 }
151 deadline = posix_poll_deadline(&normalized);
152 return true;
153}
154
155bool importPselectTimespec(LinuxKernelTimespec* userTimeout, PollDeadline& deadline) {
156 if (!userTimeout) {
157 deadline = posix_poll_deadline(nullptr);
158 return true;
159 }
160
161 LinuxKernelTimespec snapshot = {0, 0};
162 if (!PosixSubsystem::copyFromUser(&snapshot, userTimeout, sizeof(snapshot))) {
163 SYSCALL_ERROR(BadAddress);
164 return false;
165 }
166 if (snapshot.tv_sec < 0 || snapshot.tv_nsec < 0 ||
167 snapshot.tv_nsec >= static_cast<int64_t>(Time::Multiplier::Second)) {
168 SYSCALL_ERROR(InvalidArgument);
169 return false;
170 }
171 deadline = posix_poll_deadline(&snapshot);
172 return true;
173}
174
175LinuxKernelTimespec selectTimeoutRemainder(const PollDeadline& deadline) {
176 LinuxKernelTimespec remaining = {0, 0};
177 if (deadline.type != PollDeadlineType::Finite) {
178 return remaining;
179 }
180
181 const Time::Timestamp now = Time::getTicks();
182 if (now < deadline.expires) {
183 const Time::Timestamp nanoseconds = deadline.expires - now;
184 remaining.tv_sec = static_cast<int64_t>(nanoseconds / Time::Multiplier::Second);
185 remaining.tv_nsec = static_cast<int64_t>(nanoseconds % Time::Multiplier::Second);
186 }
187 return remaining;
188}
189
190void copySelectTimevalRemainder(timeval* userTimeout, const PollDeadline& deadline) {
191 if (!userTimeout || deadline.type != PollDeadlineType::Finite) {
192 return;
193 }
194
195 const LinuxKernelTimespec nanoseconds = selectTimeoutRemainder(deadline);
196 timeval remaining = {
197 static_cast<time_t>(nanoseconds.tv_sec),
198 static_cast<suseconds_t>(nanoseconds.tv_nsec / Time::Multiplier::Microsecond),
199 };
200 PosixSubsystem::copyToUser(userTimeout, &remaining, sizeof(remaining));
201}
202
203void copyPselectTimespecRemainder(LinuxKernelTimespec* userTimeout, const PollDeadline& deadline) {
204 if (!userTimeout || deadline.type != PollDeadlineType::Finite) {
205 return;
206 }
207
208 const LinuxKernelTimespec remaining = selectTimeoutRemainder(deadline);
209 PosixSubsystem::copyToUser(userTimeout, &remaining, sizeof(remaining));
210}
211
212bool importPselectSignalArgument(const LinuxPselectSigsetArgument* userArgument,
213 LinuxPselectSigsetArgument& snapshot) {
214 snapshot = {0, 0};
215 if (!userArgument) {
216 return true;
217 }
218 if (!PosixSubsystem::copyFromUser(&snapshot, userArgument, sizeof(snapshot))) {
219 SYSCALL_ERROR(BadAddress);
220 return false;
221 }
222 return true;
223}
224
225bool importPselectSignalMask(const LinuxPselectSigsetArgument& argument, bool& hasTemporaryMask,
226 uint64_t& temporaryMask) {
227 hasTemporaryMask = false;
228 temporaryMask = 0;
229 if (!argument.signalMask) {
230 return true;
231 }
232 if (argument.signalMaskSize != LinuxKernelSigsetSize) {
233 SYSCALL_ERROR(InvalidArgument);
234 return false;
235 }
236 if (!PosixSubsystem::copyFromUser(&temporaryMask,
237 reinterpret_cast<const void*>(argument.signalMask),
238 LinuxKernelSigsetSize)) {
239 SYSCALL_ERROR(BadAddress);
240 return false;
241 }
242 temporaryMask &= ~UnblockableSignals;
243 hasTemporaryMask = true;
244 return true;
245}
246
247int selectWithDeadline(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds,
248 const PollDeadline& deadline) {
249 if (nfds < 0 || nfds > MaximumSelectDescriptors) {
250 SYSCALL_ERROR(InvalidArgument);
251 return -1;
252 }
253
254 const size_t bitmapBytes = selectBitmapBytes(nfds);
255 SelectBitmaps bitmaps(bitmapBytes);
256 const bool copiedInputs =
257 (!readfds || PosixSubsystem::copyFromUser(bitmaps.readInput, readfds, bitmapBytes)) &&
258 (!writefds || PosixSubsystem::copyFromUser(bitmaps.writeInput, writefds, bitmapBytes)) &&
259 (!errorfds || PosixSubsystem::copyFromUser(bitmaps.errorInput, errorfds, bitmapBytes));
260 if (!copiedInputs) {
261 SYSCALL_ERROR(BadAddress);
262 return -1;
263 }
264
265 size_t selectedCount = 0;
266 for (int descriptor = 0; descriptor < nfds; ++descriptor) {
267 const size_t index = static_cast<size_t>(descriptor);
268 if ((readfds && selectBitIsSet(bitmaps.readInput, index)) ||
269 (writefds && selectBitIsSet(bitmaps.writeInput, index)) ||
270 (errorfds && selectBitIsSet(bitmaps.errorInput, index))) {
271 ++selectedCount;
272 }
273 }
274
275 struct pollfd* descriptors = selectedCount ? new struct pollfd[selectedCount] : nullptr;
276 size_t pollIndex = 0;
277 for (int descriptor = 0; descriptor < nfds; ++descriptor) {
278 const size_t index = static_cast<size_t>(descriptor);
279 const bool checkRead = readfds && selectBitIsSet(bitmaps.readInput, index);
280 const bool checkWrite = writefds && selectBitIsSet(bitmaps.writeInput, index);
281 const bool checkError = errorfds && selectBitIsSet(bitmaps.errorInput, index);
282 if (!(checkRead || checkWrite || checkError)) {
283 continue;
284 }
285
286 descriptors[pollIndex].fd = descriptor;
287 descriptors[pollIndex].events = 0;
288 if (checkRead) {
289 descriptors[pollIndex].events |= POLLIN;
290 }
291 if (checkWrite) {
292 descriptors[pollIndex].events |= POLLOUT;
293 }
294 if (checkError) {
295 descriptors[pollIndex].events |= POLLPRI;
296 }
297 descriptors[pollIndex].revents = 0;
298 ++pollIndex;
299 }
300
301 int result = posix_poll_safe(descriptors, static_cast<unsigned int>(selectedCount), deadline);
302 if (result >= 0) {
303 for (size_t i = 0; i < selectedCount; ++i) {
304 if (descriptors[i].revents & POLLNVAL) {
305 SYSCALL_ERROR(BadFileDescriptor);
306 result = -1;
307 break;
308 }
309 }
310 }
311
312 int readyCount = 0;
313 pollIndex = 0;
314 for (int descriptor = 0; result >= 0 && descriptor < nfds; ++descriptor) {
315 const size_t index = static_cast<size_t>(descriptor);
316 const bool checkRead = readfds && selectBitIsSet(bitmaps.readInput, index);
317 const bool checkWrite = writefds && selectBitIsSet(bitmaps.writeInput, index);
318 const bool checkError = errorfds && selectBitIsSet(bitmaps.errorInput, index);
319 if (!(checkRead || checkWrite || checkError)) {
320 continue;
321 }
322
323 const SelectProjection projection =
324 projectSelectReadiness(descriptors[pollIndex].revents, checkRead, checkWrite, checkError);
325 if (projection.read) {
326 setSelectBit(bitmaps.readResult, index);
327 }
328 if (projection.write) {
329 setSelectBit(bitmaps.writeResult, index);
330 }
331 if (projection.exceptional) {
332 setSelectBit(bitmaps.errorResult, index);
333 }
334 readyCount += projection.count;
335 ++pollIndex;
336 }
337
338 delete[] descriptors;
339 if (result < 0) {
340 return result;
341 }
342
343 const bool copiedResults =
344 (!readfds || PosixSubsystem::copyToUser(readfds, bitmaps.readResult, bitmapBytes)) &&
345 (!writefds || PosixSubsystem::copyToUser(writefds, bitmaps.writeResult, bitmapBytes)) &&
346 (!errorfds || PosixSubsystem::copyToUser(errorfds, bitmaps.errorResult, bitmapBytes));
347 if (!copiedResults) {
348 SYSCALL_ERROR(BadAddress);
349 return -1;
350 }
351 return readyCount;
352}
353} // namespace
354
355#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
356extern "C" EXPORTED_PUBLIC unsigned int posixSelectProjectionForTest(short revents, bool checkRead,
357 bool checkWrite,
358 bool checkExceptional) {
359 const SelectProjection projection =
360 projectSelectReadiness(revents, checkRead, checkWrite, checkExceptional);
361 return static_cast<unsigned int>(projection.read) |
362 (static_cast<unsigned int>(projection.write) << 1) |
363 (static_cast<unsigned int>(projection.exceptional) << 2) |
364 (static_cast<unsigned int>(projection.count) << 8);
365}
366
367#endif
368
369int posix_select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, timeval* timeout) {
370 POLL_NOTICE("select(" << nfds << ", " << readfds << ", " << writefds << ", " << errorfds << ", "
371 << timeout << ")");
372 PollDeadline deadline = {PollDeadlineType::Infinite, 0};
373 if (!importSelectTimeval(timeout, deadline)) {
374 return -1;
375 }
376
377 const int result = selectWithDeadline(nfds, readfds, writefds, errorfds, deadline);
378 copySelectTimevalRemainder(timeout, deadline);
379 POLL_NOTICE(" -> select via poll returns " << result);
380 return result;
381}
382
383int posix_pselect6(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds,
384 LinuxKernelTimespec* timeout, const LinuxPselectSigsetArgument* signalArgument) {
385 LinuxPselectSigsetArgument signalArgumentSnapshot = {0, 0};
386 if (!importPselectSignalArgument(signalArgument, signalArgumentSnapshot)) {
387 return -1;
388 }
389
390 PollDeadline deadline = {PollDeadlineType::Infinite, 0};
391 if (!importPselectTimespec(timeout, deadline)) {
392 return -1;
393 }
394
395 bool hasTemporaryMask = false;
396 uint64_t temporaryMask = 0;
397 if (!importPselectSignalMask(signalArgumentSnapshot, hasTemporaryMask, temporaryMask)) {
398 return -1;
399 }
400
401 int result = 0;
402 if (hasTemporaryMask) {
403 Thread* thread = Processor::information().getCurrentThread();
404 if (!thread) {
405 FATAL("pselect6 has no current Thread.");
406 }
407
408 Thread::TemporarySignalMask signalWait(*thread, temporaryMask);
409 result = selectWithDeadline(nfds, readfds, writefds, errorfds, deadline);
410 const bool signalInterrupted = signalWait.finish();
411 if (!result && signalInterrupted) {
412 SYSCALL_ERROR(Interrupted);
413 result = -1;
414 }
415 } else {
416 result = selectWithDeadline(nfds, readfds, writefds, errorfds, deadline);
417 }
418
419 copyPselectTimespecRemainder(timeout, deadline);
420 return result;
421}
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static ProcessorInformation & information()