summaryrefslogtreecommitdiff
path: root/Kernel/Syscalls/select.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Kernel/Syscalls/select.cpp')
-rw-r--r--Kernel/Syscalls/select.cpp172
1 files changed, 84 insertions, 88 deletions
diff --git a/Kernel/Syscalls/select.cpp b/Kernel/Syscalls/select.cpp
index ccf2110a3a..c518ae7acb 100644
--- a/Kernel/Syscalls/select.cpp
+++ b/Kernel/Syscalls/select.cpp
@@ -34,51 +34,43 @@
namespace Kernel {
-int Process::sys$select(const Syscall::SC_select_params* params)
+int Process::sys$select(const Syscall::SC_select_params* user_params)
{
REQUIRE_PROMISE(stdio);
- // FIXME: Return -EINVAL if timeout is invalid.
- if (!validate_read_typed(params))
- return -EFAULT;
+ Syscall::SC_select_params params;
SmapDisabler disabler;
-
- int nfds = params->nfds;
- fd_set* readfds = params->readfds;
- fd_set* writefds = params->writefds;
- fd_set* exceptfds = params->exceptfds;
- const timespec* timeout = params->timeout;
- const sigset_t* sigmask = params->sigmask;
-
- if (writefds && !validate_write_typed(writefds))
- return -EFAULT;
- if (readfds && !validate_write_typed(readfds))
+ if (!copy_from_user(&params, user_params))
return -EFAULT;
- if (exceptfds && !validate_write_typed(exceptfds))
- return -EFAULT;
- if (timeout && !validate_read_typed(timeout))
- return -EFAULT;
- if (sigmask && !validate_read_typed(sigmask))
- return -EFAULT;
- if (nfds < 0)
+
+ if (params.nfds < 0)
return -EINVAL;
timespec computed_timeout;
bool select_has_timeout = false;
- if (timeout && (timeout->tv_sec || timeout->tv_nsec)) {
- timespec ts_since_boot;
- timeval_to_timespec(Scheduler::time_since_boot(), ts_since_boot);
- timespec_add(ts_since_boot, *timeout, computed_timeout);
- select_has_timeout = true;
+ if (params.timeout) {
+ timespec timeout_copy;
+ if (!copy_from_user(&timeout_copy, params.timeout))
+ return -EFAULT;
+ if (timeout_copy.tv_sec || timeout_copy.tv_nsec) {
+ timespec ts_since_boot;
+ timeval_to_timespec(Scheduler::time_since_boot(), ts_since_boot);
+ timespec_add(ts_since_boot, timeout_copy, computed_timeout);
+ select_has_timeout = true;
+ }
}
auto current_thread = Thread::current();
u32 previous_signal_mask = 0;
- if (sigmask)
- previous_signal_mask = current_thread->update_signal_mask(*sigmask);
+ if (params.sigmask) {
+ sigset_t sigmask_copy;
+ if (!copy_from_user(&sigmask_copy, params.sigmask))
+ return -EFAULT;
+ previous_signal_mask = current_thread->update_signal_mask(sigmask_copy);
+ }
ScopeGuard rollback_signal_mask([&]() {
- if (sigmask)
+ if (params.sigmask)
current_thread->update_signal_mask(previous_signal_mask);
});
@@ -86,12 +78,15 @@ int Process::sys$select(const Syscall::SC_select_params* params)
Thread::SelectBlocker::FDVector wfds;
Thread::SelectBlocker::FDVector efds;
- auto transfer_fds = [&](auto* fds, auto& vector) -> int {
+ auto transfer_fds = [&](auto* fds_unsafe, auto& vector) -> int {
vector.clear_with_capacity();
- if (!fds)
+ if (!fds_unsafe)
return 0;
- for (int fd = 0; fd < nfds; ++fd) {
- if (FD_ISSET(fd, fds)) {
+ fd_set fds;
+ if (!copy_from_user(&fds, fds_unsafe))
+ return -EFAULT;
+ for (int fd = 0; fd < params.nfds; ++fd) {
+ if (FD_ISSET(fd, &fds)) {
if (!file_description(fd)) {
dbg() << "sys$select: Bad fd number " << fd;
return -EBADF;
@@ -101,47 +96,42 @@ int Process::sys$select(const Syscall::SC_select_params* params)
}
return 0;
};
- if (int error = transfer_fds(writefds, wfds))
+ if (int error = transfer_fds(params.writefds, wfds))
return error;
- if (int error = transfer_fds(readfds, rfds))
+ if (int error = transfer_fds(params.readfds, rfds))
return error;
- if (int error = transfer_fds(exceptfds, efds))
+ if (int error = transfer_fds(params.exceptfds, efds))
return error;
#if defined(DEBUG_IO) || defined(DEBUG_POLL_SELECT)
- dbg() << "selecting on (read:" << rfds.size() << ", write:" << wfds.size() << "), timeout=" << timeout;
+ dbg() << "selecting on (read:" << rfds.size() << ", write:" << wfds.size() << "), timeout=" << params.timeout;
#endif
- if (!timeout || select_has_timeout) {
+ if (!params.timeout || select_has_timeout) {
if (current_thread->block<Thread::SelectBlocker>(select_has_timeout ? &computed_timeout : nullptr, rfds, wfds, efds).was_interrupted())
return -EINTR;
- // While we blocked, the process lock was dropped. This gave other threads
- // the opportunity to mess with the memory. For example, it could free the
- // region, and map it to a region to which it has no write permissions.
- // Therefore, we need to re-validate all pointers.
- if (writefds && !validate_write_typed(writefds))
- return -EFAULT;
- if (readfds && !validate_write_typed(readfds))
- return -EFAULT;
- // See the fixme below.
- if (exceptfds && !validate_write_typed(exceptfds))
- return -EFAULT;
}
int marked_fd_count = 0;
- auto mark_fds = [&](auto* fds, auto& vector, auto should_mark) {
- if (!fds)
- return;
- FD_ZERO(fds);
+ auto mark_fds = [&](auto* fds_unsafe, auto& vector, auto should_mark) {
+ if (!fds_unsafe)
+ return 0;
+ fd_set fds;
+ FD_ZERO(&fds);
for (int fd : vector) {
if (auto description = file_description(fd); description && should_mark(*description)) {
- FD_SET(fd, fds);
+ FD_SET(fd, &fds);
++marked_fd_count;
}
}
+ if (!copy_to_user(fds_unsafe, &fds))
+ return -EFAULT;
+ return 0;
};
- mark_fds(readfds, rfds, [](auto& description) { return description.can_read(); });
- mark_fds(writefds, wfds, [](auto& description) { return description.can_write(); });
+ if (int error = mark_fds(params.readfds, rfds, [](auto& description) { return description.can_read(); }))
+ return error;
+ if (int error = mark_fds(params.writefds, wfds, [](auto& description) { return description.can_write(); }))
+ return error;
// FIXME: We should also mark exceptfds as appropriate.
return marked_fd_count;
@@ -153,33 +143,39 @@ int Process::sys$poll(Userspace<const Syscall::SC_poll_params*> user_params)
// FIXME: Return -EINVAL if timeout is invalid.
Syscall::SC_poll_params params;
- if (!validate_read_and_copy_typed(&params, user_params))
+ if (!copy_from_user(&params, user_params))
return -EFAULT;
SmapDisabler disabler;
- pollfd* fds = params.fds;
- unsigned nfds = params.nfds;
-
- if (fds && !validate_read_typed(fds, nfds))
- return -EFAULT;
-
timespec timeout = {};
- if (params.timeout && !validate_read_and_copy_typed(&timeout, params.timeout))
+ if (params.timeout && !copy_from_user(&timeout, params.timeout))
return -EFAULT;
sigset_t sigmask = {};
- if (params.sigmask && !validate_read_and_copy_typed(&sigmask, params.sigmask))
+ if (params.sigmask && !copy_from_user(&sigmask, params.sigmask))
return -EFAULT;
+ Vector<pollfd> fds_copy;
+ if (params.nfds > 0) {
+ Checked nfds_checked = sizeof(pollfd);
+ nfds_checked *= params.nfds;
+ if (nfds_checked.has_overflow())
+ return -EFAULT;
+ fds_copy.resize(params.nfds);
+ if (!copy_from_user(&fds_copy[0], &params.fds[0], params.nfds * sizeof(pollfd)))
+ return -EFAULT;
+ }
+
Thread::SelectBlocker::FDVector rfds;
Thread::SelectBlocker::FDVector wfds;
- for (unsigned i = 0; i < nfds; ++i) {
- if (fds[i].events & POLLIN)
- rfds.append(fds[i].fd);
- if (fds[i].events & POLLOUT)
- wfds.append(fds[i].fd);
+ for (unsigned i = 0; i < params.nfds; ++i) {
+ auto& pfd = fds_copy[i];
+ if (pfd.events & POLLIN)
+ rfds.append(pfd.fd);
+ if (pfd.events & POLLOUT)
+ wfds.append(pfd.fd);
}
timespec actual_timeout;
@@ -195,7 +191,7 @@ int Process::sys$poll(Userspace<const Syscall::SC_poll_params*> user_params)
u32 previous_signal_mask = 0;
if (params.sigmask)
- previous_signal_mask = current_thread->update_signal_mask(params.sigmask);
+ previous_signal_mask = current_thread->update_signal_mask(sigmask);
ScopeGuard rollback_signal_mask([&]() {
if (params.sigmask)
current_thread->update_signal_mask(previous_signal_mask);
@@ -210,28 +206,28 @@ int Process::sys$poll(Userspace<const Syscall::SC_poll_params*> user_params)
return -EINTR;
}
- // Validate we can still write after waking up.
- if (fds && !validate_write_typed(fds, nfds))
- return -EFAULT;
-
int fds_with_revents = 0;
- for (unsigned i = 0; i < nfds; ++i) {
- auto description = file_description(fds[i].fd);
+ for (unsigned i = 0; i < params.nfds; ++i) {
+ auto& pfd = fds_copy[i];
+ auto description = file_description(pfd.fd);
if (!description) {
- fds[i].revents = POLLNVAL;
- continue;
+ pfd.revents = POLLNVAL;
+ } else {
+ pfd.revents = 0;
+ if (pfd.events & POLLIN && description->can_read())
+ pfd.revents |= POLLIN;
+ if (pfd.events & POLLOUT && description->can_write())
+ pfd.revents |= POLLOUT;
+
+ if (pfd.revents)
+ ++fds_with_revents;
}
- fds[i].revents = 0;
- if (fds[i].events & POLLIN && description->can_read())
- fds[i].revents |= POLLIN;
- if (fds[i].events & POLLOUT && description->can_write())
- fds[i].revents |= POLLOUT;
-
- if (fds[i].revents)
- ++fds_with_revents;
}
+ if (params.nfds > 0 && !copy_to_user(&params.fds[0], &fds_copy[0], params.nfds * sizeof(pollfd)))
+ return -EFAULT;
+
return fds_with_revents;
}