diff options
Diffstat (limited to 'Kernel/Syscalls/select.cpp')
-rw-r--r-- | Kernel/Syscalls/select.cpp | 172 |
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(¶ms, 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(¶ms, user_params)) + if (!copy_from_user(¶ms, 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], ¶ms.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(¶ms.fds[0], &fds_copy[0], params.nfds * sizeof(pollfd))) + return -EFAULT; + return fds_with_revents; } |