1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 18:17:44 +00:00

Kernel: Make copy_to/from_user safe and remove unnecessary checks

Since the CPU already does almost all necessary validation steps
for us, we don't really need to attempt to do this. Doing it
ourselves doesn't really work very reliably, because we'd have to
account for other processors modifying virtual memory, and we'd
have to account for e.g. pages not being able to be allocated
due to insufficient resources.

So change the copy_to/from_user (and associated helper functions)
to use the new safe_memcpy, which will return whether it succeeded
or not. The only manual validation step needed (which the CPU
can't perform for us) is making sure the pointers provided by user
mode aren't pointing to kernel mappings.

To make it easier to read/write from/to either kernel or user mode
data add the UserOrKernelBuffer helper class, which will internally
either use copy_from/to_user or directly memcpy, or pass the data
through directly using a temporary buffer on the stack.

Last but not least we need to keep syscall params trivial as we
need to copy them from/to user mode using copy_from/to_user.
This commit is contained in:
Tom 2020-09-11 21:11:07 -06:00 committed by Andreas Kling
parent 7d1b8417bd
commit c8d9f1b9c9
149 changed files with 1585 additions and 1244 deletions

View file

@ -66,12 +66,11 @@ int Process::sys$getcwd(Userspace<char*> buffer, ssize_t size)
REQUIRE_PROMISE(rpath);
if (size < 0)
return -EINVAL;
if (!validate_write(buffer, size))
return -EFAULT;
auto path = current_directory().absolute_path();
if ((size_t)size < path.length() + 1)
return -ERANGE;
copy_to_user(buffer, path.characters(), path.length() + 1);
if (!copy_to_user(buffer, path.characters(), path.length() + 1))
return -EFAULT;
return 0;
}

View file

@ -42,7 +42,7 @@ int Process::sys$chown(Userspace<const Syscall::SC_chown_params*> user_params)
{
REQUIRE_PROMISE(chown);
Syscall::SC_chown_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
auto path = get_syscall_path_argument(params.path);
if (path.is_error())

View file

@ -32,8 +32,6 @@ namespace Kernel {
int Process::sys$clock_gettime(clockid_t clock_id, Userspace<timespec*> user_ts)
{
REQUIRE_PROMISE(stdio);
if (!validate_write_typed(user_ts))
return -EFAULT;
timespec ts = {};
@ -49,7 +47,8 @@ int Process::sys$clock_gettime(clockid_t clock_id, Userspace<timespec*> user_ts)
return -EINVAL;
}
copy_to_user(user_ts, &ts);
if (!copy_to_user(user_ts, &ts))
return -EFAULT;
return 0;
}
@ -61,7 +60,7 @@ int Process::sys$clock_settime(clockid_t clock_id, Userspace<const timespec*> us
return -EPERM;
timespec ts;
if (!validate_read_and_copy_typed(&ts, user_ts))
if (!copy_from_user(&ts, user_ts))
return -EFAULT;
switch (clock_id) {
@ -79,16 +78,11 @@ int Process::sys$clock_nanosleep(Userspace<const Syscall::SC_clock_nanosleep_par
REQUIRE_PROMISE(stdio);
Syscall::SC_clock_nanosleep_params params;
if (!validate_read_and_copy_typed(&params, user_params))
return -EFAULT;
if (params.requested_sleep && !validate_read_typed(params.requested_sleep))
if (!copy_from_user(&params, user_params))
return -EFAULT;
timespec requested_sleep;
copy_from_user(&requested_sleep, params.requested_sleep);
if (params.remaining_sleep && !validate_write_typed(params.remaining_sleep))
if (!copy_from_user(&requested_sleep, params.requested_sleep))
return -EFAULT;
bool is_absolute = params.flags & TIMER_ABSTIME;
@ -109,18 +103,12 @@ int Process::sys$clock_nanosleep(Userspace<const Syscall::SC_clock_nanosleep_par
if (wakeup_time > g_uptime) {
u64 ticks_left = wakeup_time - g_uptime;
if (!is_absolute && params.remaining_sleep) {
if (!validate_write_typed(params.remaining_sleep)) {
// This can happen because the lock is dropped while
// sleeping, thus giving other threads the opportunity
// to make the region unwritable.
return -EFAULT;
}
timespec remaining_sleep = {};
remaining_sleep.tv_sec = ticks_left / TimeManagement::the().ticks_per_second();
ticks_left -= remaining_sleep.tv_sec * TimeManagement::the().ticks_per_second();
remaining_sleep.tv_nsec = ticks_left * 1000000000 / TimeManagement::the().ticks_per_second();
copy_to_user(params.remaining_sleep, &remaining_sleep);
if (!copy_to_user(params.remaining_sleep, &remaining_sleep))
return -EFAULT;
}
return -EINTR;
}
@ -134,10 +122,9 @@ int Process::sys$clock_nanosleep(Userspace<const Syscall::SC_clock_nanosleep_par
int Process::sys$gettimeofday(Userspace<timeval*> user_tv)
{
REQUIRE_PROMISE(stdio);
if (!validate_write_typed(user_tv))
return -EFAULT;
auto tv = kgettimeofday();
copy_to_user(user_tv, &tv);
if (!copy_to_user(user_tv, &tv))
return -EFAULT;
return 0;
}

View file

@ -27,6 +27,7 @@
#include <Kernel/IO.h>
#include <Kernel/KSyms.h>
#include <Kernel/Process.h>
#include <Kernel/UserOrKernelBuffer.h>
namespace Kernel {
@ -44,13 +45,20 @@ int Process::sys$dbgputch(u8 ch)
int Process::sys$dbgputstr(Userspace<const u8*> characters, int length)
{
if (!length)
if (length <= 0)
return 0;
if (!validate_read(characters, length))
return -EFAULT;
SmapDisabler disabler;
for (int i = 0; i < length; ++i)
IO::out8(0xe9, characters.unsafe_userspace_ptr()[i]);
auto buffer = UserOrKernelBuffer::for_user_buffer(characters, length);
if (!buffer.has_value())
return -EFAULT;
ssize_t nread = buffer.value().read_buffered<1024>(length, [&](const u8* buffer, size_t buffer_size) {
for (size_t i = 0; i < buffer_size; ++i)
IO::out8(0xe9, buffer[i]);
return (ssize_t)buffer_size;
});
if (nread < 0)
return (int)nread;
return 0;
}

View file

@ -423,7 +423,8 @@ KResultOr<NonnullRefPtr<FileDescription>> Process::find_elf_interpreter_for_exec
return KResult(-ENOEXEC);
memset(first_page, 0, sizeof(first_page));
auto nread_or_error = interpreter_description->read((u8*)&first_page, sizeof(first_page));
auto first_page_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&first_page);
auto nread_or_error = interpreter_description->read(first_page_buffer, sizeof(first_page));
if (nread_or_error.is_error())
return KResult(-ENOEXEC);
nread = nread_or_error.value();
@ -490,7 +491,8 @@ int Process::exec(String path, Vector<String> arguments, Vector<String> environm
// Read the first page of the program into memory so we can validate the binfmt of it
char first_page[PAGE_SIZE];
auto nread_or_error = description->read((u8*)&first_page, sizeof(first_page));
auto first_page_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&first_page);
auto nread_or_error = description->read(first_page_buffer, sizeof(first_page));
if (nread_or_error.is_error())
return -ENOEXEC;
@ -554,7 +556,7 @@ int Process::sys$execve(Userspace<const Syscall::SC_execve_params*> user_params)
// NOTE: Be extremely careful with allocating any kernel memory in exec().
// On success, the kernel stack will be lost.
Syscall::SC_execve_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
if (params.arguments.length > ARG_MAX || params.environment.length > ARG_MAX)
@ -574,13 +576,16 @@ int Process::sys$execve(Userspace<const Syscall::SC_execve_params*> user_params)
auto copy_user_strings = [this](const auto& list, auto& output) {
if (!list.length)
return true;
if (!validate_read_typed(list.strings, list.length))
Checked size = sizeof(list.length);
size *= list.length;
if (size.has_overflow())
return false;
Vector<Syscall::StringArgument, 32> strings;
strings.resize(list.length);
copy_from_user(strings.data(), list.strings.unsafe_userspace_ptr(), list.length * sizeof(Syscall::StringArgument));
if (!copy_from_user(strings.data(), list.strings, list.length * sizeof(Syscall::StringArgument)))
return false;
for (size_t i = 0; i < list.length; ++i) {
auto string = validate_and_copy_string_from_user(strings[i]);
auto string = copy_string_from_user(strings[i]);
if (string.is_null())
return false;
output.append(move(string));

View file

@ -55,21 +55,19 @@ int Process::sys$futex(Userspace<const Syscall::SC_futex_params*> user_params)
REQUIRE_PROMISE(thread);
Syscall::SC_futex_params params;
if (!validate_read_and_copy_typed(&params, user_params))
return -EFAULT;
if (!validate_read_typed(params.userspace_address))
if (!copy_from_user(&params, user_params))
return -EFAULT;
switch (params.futex_op) {
case FUTEX_WAIT: {
i32 user_value;
copy_from_user(&user_value, params.userspace_address);
if (!copy_from_user(&user_value, params.userspace_address))
return -EFAULT;
if (user_value != params.val)
return -EAGAIN;
timespec ts_abstimeout { 0, 0 };
if (params.timeout && !validate_read_and_copy_typed(&ts_abstimeout, params.timeout))
if (params.timeout && !copy_from_user(&ts_abstimeout, params.timeout))
return -EFAULT;
timeval* optional_timeout = nullptr;
@ -80,7 +78,7 @@ int Process::sys$futex(Userspace<const Syscall::SC_futex_params*> user_params)
}
// FIXME: This is supposed to be interruptible by a signal, but right now WaitQueue cannot be interrupted.
WaitQueue& wait_queue = futex_queue(params.userspace_address);
WaitQueue& wait_queue = futex_queue((FlatPtr)params.userspace_address);
Thread::BlockResult result = Thread::current()->wait_on(wait_queue, "Futex", optional_timeout);
if (result == Thread::BlockResult::InterruptedByTimeout) {
return -ETIMEDOUT;
@ -92,9 +90,9 @@ int Process::sys$futex(Userspace<const Syscall::SC_futex_params*> user_params)
if (params.val == 0)
return 0;
if (params.val == 1) {
futex_queue(params.userspace_address).wake_one();
futex_queue((FlatPtr)params.userspace_address).wake_one();
} else {
futex_queue(params.userspace_address).wake_n(params.val);
futex_queue((FlatPtr)params.userspace_address).wake_n(params.val);
}
break;
}

View file

@ -34,12 +34,13 @@ ssize_t Process::sys$get_dir_entries(int fd, void* buffer, ssize_t size)
REQUIRE_PROMISE(stdio);
if (size < 0)
return -EINVAL;
if (!validate_write(buffer, size))
return -EFAULT;
auto description = file_description(fd);
if (!description)
return -EBADF;
return description->get_dir_entries((u8*)buffer, size);
auto user_buffer = UserOrKernelBuffer::for_user_buffer((u8*)buffer, size);
if (!user_buffer.has_value())
return -EFAULT;
return description->get_dir_entries(user_buffer.value(), size);
}
}

View file

@ -31,11 +31,6 @@ namespace Kernel {
int Process::sys$get_stack_bounds(FlatPtr* user_stack_base, size_t* user_stack_size)
{
if (!validate_write_typed(user_stack_base))
return -EFAULT;
if (!validate_write_typed(user_stack_size))
return -EFAULT;
FlatPtr stack_pointer = Thread::current()->get_register_dump_from_stack().userspace_esp;
auto* stack_region = MM.find_region_from_vaddr(*this, VirtualAddress(stack_pointer));
if (!stack_region) {
@ -45,8 +40,10 @@ int Process::sys$get_stack_bounds(FlatPtr* user_stack_base, size_t* user_stack_s
FlatPtr stack_base = stack_region->range().base().get();
size_t stack_size = stack_region->size();
copy_to_user(user_stack_base, &stack_base);
copy_to_user(user_stack_size, &stack_size);
if (!copy_to_user(user_stack_base, &stack_base))
return -EFAULT;
if (!copy_to_user(user_stack_size, &stack_size))
return -EFAULT;
return 0;
}

View file

@ -26,6 +26,7 @@
#include <Kernel/Process.h>
#include <Kernel/Random.h>
#include <Kernel/UserOrKernelBuffer.h>
namespace Kernel {
@ -38,13 +39,15 @@ ssize_t Process::sys$getrandom(Userspace<void*> buffer, size_t buffer_size, [[ma
if (buffer_size <= 0)
return -EINVAL;
if (!validate_write(buffer, buffer_size))
return -EFAULT;
SmapDisabler disabler;
// FIXME: We should really push Userspace<T> down through the interface.
get_good_random_bytes((u8*)buffer.ptr(), buffer_size);
return 0;
auto data_buffer = UserOrKernelBuffer::for_user_buffer(buffer, buffer_size);
if (!data_buffer.has_value())
return -EFAULT;
ssize_t nwritten = data_buffer.value().write_buffered<1024>(buffer_size, [&](u8* buffer, size_t buffer_bytes) {
get_good_random_bytes(buffer, buffer_bytes);
return (ssize_t)buffer_bytes;
});
return nwritten;
}
}

View file

@ -55,22 +55,16 @@ gid_t Process::sys$getegid()
int Process::sys$getresuid(Userspace<uid_t*> ruid, Userspace<uid_t*> euid, Userspace<uid_t*> suid)
{
REQUIRE_PROMISE(stdio);
if (!validate_write_typed(ruid) || !validate_write_typed(euid) || !validate_write_typed(suid))
if (!copy_to_user(ruid, &m_uid) || !copy_to_user(euid, &m_euid) || !copy_to_user(suid, &m_suid))
return -EFAULT;
copy_to_user(ruid, &m_uid);
copy_to_user(euid, &m_euid);
copy_to_user(suid, &m_suid);
return 0;
}
int Process::sys$getresgid(Userspace<gid_t*> rgid, Userspace<gid_t*> egid, Userspace<gid_t*> sgid)
{
REQUIRE_PROMISE(stdio);
if (!validate_write_typed(rgid) || !validate_write_typed(egid) || !validate_write_typed(sgid))
if (!copy_to_user(rgid, &m_gid) || !copy_to_user(egid, &m_egid) || !copy_to_user(sgid, &m_sgid))
return -EFAULT;
copy_to_user(rgid, &m_gid);
copy_to_user(egid, &m_egid);
copy_to_user(sgid, &m_sgid);
return 0;
}
@ -83,10 +77,9 @@ int Process::sys$getgroups(ssize_t count, Userspace<gid_t*> user_gids)
return m_extra_gids.size();
if (count != (int)m_extra_gids.size())
return -EINVAL;
if (!validate_write_typed(user_gids, m_extra_gids.size()))
return -EFAULT;
copy_to_user(user_gids, m_extra_gids.data(), sizeof(gid_t) * count);
if (!copy_to_user(user_gids, m_extra_gids.data(), sizeof(gid_t) * count))
return -EFAULT;
return 0;
}

View file

@ -36,12 +36,11 @@ int Process::sys$gethostname(Userspace<char*> buffer, ssize_t size)
REQUIRE_PROMISE(stdio);
if (size < 0)
return -EINVAL;
if (!validate_write(buffer, size))
return -EFAULT;
LOCKER(*g_hostname_lock, Lock::Mode::Shared);
if ((size_t)size < (g_hostname->length() + 1))
return -ENAMETOOLONG;
copy_to_user(buffer, g_hostname->characters(), g_hostname->length() + 1);
if (!copy_to_user(buffer, g_hostname->characters(), g_hostname->length() + 1))
return -EFAULT;
return 0;
}
@ -55,7 +54,10 @@ int Process::sys$sethostname(Userspace<const char*> hostname, ssize_t length)
LOCKER(*g_hostname_lock, Lock::Mode::Exclusive);
if (length > 64)
return -ENAMETOOLONG;
*g_hostname = validate_and_copy_string_from_user(hostname, length);
auto copied_hostname = copy_string_from_user(hostname, length);
if (copied_hostname.is_null())
return -EFAULT;
*g_hostname = move(copied_hostname);
return 0;
}

View file

@ -34,11 +34,13 @@ int Process::sys$link(Userspace<const Syscall::SC_link_params*> user_params)
{
REQUIRE_PROMISE(cpath);
Syscall::SC_link_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
auto old_path = validate_and_copy_string_from_user(params.old_path);
auto new_path = validate_and_copy_string_from_user(params.new_path);
if (old_path.is_null() || new_path.is_null())
auto old_path = copy_string_from_user(params.old_path);
if (old_path.is_null())
return -EFAULT;
auto new_path = copy_string_from_user(params.new_path);
if (new_path.is_null())
return -EFAULT;
return VFS::the().link(old_path, new_path, current_directory());
}
@ -47,7 +49,7 @@ int Process::sys$symlink(Userspace<const Syscall::SC_symlink_params*> user_param
{
REQUIRE_PROMISE(cpath);
Syscall::SC_symlink_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
auto target = get_syscall_path_argument(params.target);
if (target.is_error())

View file

@ -34,7 +34,7 @@ int Process::sys$mknod(Userspace<const Syscall::SC_mknod_params*> user_params)
{
REQUIRE_PROMISE(dpath);
Syscall::SC_mknod_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
if (!is_superuser() && !is_regular_file(params.mode) && !is_fifo(params.mode) && !is_socket(params.mode))
return -EPERM;

View file

@ -81,7 +81,7 @@ void* Process::sys$mmap(Userspace<const Syscall::SC_mmap_params*> user_params)
REQUIRE_PROMISE(stdio);
Syscall::SC_mmap_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return (void*)-EFAULT;
void* addr = (void*)params.addr;
@ -102,7 +102,7 @@ void* Process::sys$mmap(Userspace<const Syscall::SC_mmap_params*> user_params)
if (params.name.characters) {
if (params.name.length > PATH_MAX)
return (void*)-ENAMETOOLONG;
name = validate_and_copy_string_from_user(params.name);
name = copy_string_from_user(params.name);
if (name.is_null())
return (void*)-EFAULT;
}
@ -336,13 +336,13 @@ int Process::sys$set_mmap_name(Userspace<const Syscall::SC_set_mmap_name_params*
REQUIRE_PROMISE(stdio);
Syscall::SC_set_mmap_name_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
if (params.name.length > PATH_MAX)
return -ENAMETOOLONG;
auto name = validate_and_copy_string_from_user(params.name);
auto name = copy_string_from_user(params.name);
if (name.is_null())
return -EFAULT;
@ -351,7 +351,7 @@ int Process::sys$set_mmap_name(Userspace<const Syscall::SC_set_mmap_name_params*
return -EINVAL;
if (!region->is_mmap())
return -EPERM;
region->set_name(name);
region->set_name(move(name));
return 0;
}

View file

@ -169,7 +169,7 @@ int Process::sys$module_unload(Userspace<const char*> user_name, size_t name_len
REQUIRE_NO_PROMISES;
auto module_name = validate_and_copy_string_from_user(user_name, name_length);
auto module_name = copy_string_from_user(user_name, name_length);
if (module_name.is_null())
return -EFAULT;

View file

@ -43,15 +43,16 @@ int Process::sys$mount(Userspace<const Syscall::SC_mount_params*> user_params)
REQUIRE_NO_PROMISES;
Syscall::SC_mount_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
auto source_fd = params.source_fd;
auto target = validate_and_copy_string_from_user(params.target);
auto fs_type = validate_and_copy_string_from_user(params.fs_type);
auto target = copy_string_from_user(params.target);
if (target.is_null())
return -EFAULT;
auto fs_type = copy_string_from_user(params.fs_type);
if (fs_type.is_null())
return -EFAULT;
auto description = file_description(source_fd);
if (!description.is_null())
@ -129,9 +130,6 @@ int Process::sys$umount(Userspace<const char*> user_mountpoint, size_t mountpoin
REQUIRE_NO_PROMISES;
if (!validate_read(user_mountpoint, mountpoint_length))
return -EFAULT;
auto mountpoint = get_syscall_path_argument(user_mountpoint, mountpoint_length);
if (mountpoint.is_error())
return mountpoint.error();

View file

@ -34,7 +34,7 @@ namespace Kernel {
int Process::sys$open(Userspace<const Syscall::SC_open_params*> user_params)
{
Syscall::SC_open_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
int dirfd = params.dirfd;

View file

@ -33,8 +33,6 @@ namespace Kernel {
int Process::sys$pipe(int pipefd[2], int flags)
{
REQUIRE_PROMISE(stdio);
if (!validate_write_typed(pipefd))
return -EFAULT;
if (number_of_open_file_descriptors() + 2 > max_open_file_descriptors())
return -EMFILE;
// Reject flags other than O_CLOEXEC.
@ -47,12 +45,14 @@ int Process::sys$pipe(int pipefd[2], int flags)
int reader_fd = alloc_fd();
m_fds[reader_fd].set(fifo->open_direction(FIFO::Direction::Reader), fd_flags);
m_fds[reader_fd].description()->set_readable(true);
copy_to_user(&pipefd[0], &reader_fd);
if (!copy_to_user(&pipefd[0], &reader_fd))
return -EFAULT;
int writer_fd = alloc_fd();
m_fds[writer_fd].set(fifo->open_direction(FIFO::Direction::Writer), fd_flags);
m_fds[writer_fd].description()->set_writable(true);
copy_to_user(&pipefd[1], &writer_fd);
if (!copy_to_user(&pipefd[1], &writer_fd))
return -EFAULT;
return 0;
}

View file

@ -32,7 +32,7 @@ namespace Kernel {
int Process::sys$pledge(Userspace<const Syscall::SC_pledge_params*> user_params)
{
Syscall::SC_pledge_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
if (params.promises.length > 1024 || params.execpromises.length > 1024)
@ -40,14 +40,14 @@ int Process::sys$pledge(Userspace<const Syscall::SC_pledge_params*> user_params)
String promises;
if (params.promises.characters) {
promises = validate_and_copy_string_from_user(params.promises);
auto promises = copy_string_from_user(params.promises);
if (promises.is_null())
return -EFAULT;
}
String execpromises;
if (params.execpromises.characters) {
execpromises = validate_and_copy_string_from_user(params.execpromises);
execpromises = copy_string_from_user(params.execpromises);
if (execpromises.is_null())
return -EFAULT;
}

View file

@ -58,13 +58,11 @@ int Process::sys$set_process_icon(int icon_id)
int Process::sys$get_process_name(Userspace<char*> buffer, size_t buffer_size)
{
REQUIRE_PROMISE(stdio);
if (!validate_write(buffer, buffer_size))
return -EFAULT;
if (m_name.length() + 1 > buffer_size)
return -ENAMETOOLONG;
copy_to_user(buffer, m_name.characters(), m_name.length() + 1);
if (!copy_to_user(buffer, m_name.characters(), m_name.length() + 1))
return -EFAULT;
return 0;
}
@ -73,7 +71,7 @@ int Process::sys$set_process_name(Userspace<const char*> user_name, size_t user_
REQUIRE_PROMISE(proc);
if (user_name_length > 256)
return -ENAMETOOLONG;
auto name = validate_and_copy_string_from_user(user_name, user_name_length);
auto name = copy_string_from_user(user_name, user_name_length);
if (name.is_null())
return -EFAULT;
m_name = move(name);

View file

@ -24,6 +24,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ScopeGuard.h>
#include <Kernel/Process.h>
#include <Kernel/Ptrace.h>
#include <Kernel/VM/MemoryManager.h>
@ -38,7 +39,7 @@ int Process::sys$ptrace(Userspace<const Syscall::SC_ptrace_params*> user_params)
{
REQUIRE_PROMISE(proc);
Syscall::SC_ptrace_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
auto result = Ptrace::handle_syscall(params, *this);
return result.is_error() ? result.error() : result.value();
@ -63,28 +64,21 @@ bool Process::has_tracee_thread(ProcessID tracer_pid) const
KResultOr<u32> Process::peek_user_data(Userspace<const u32*> address)
{
if (!MM.validate_user_read(*this, VirtualAddress(address), sizeof(u32))) {
dbg() << "Invalid address for peek_user_data: " << address.ptr();
return KResult(-EFAULT);
}
uint32_t result;
// This function can be called from the context of another
// process that called PT_PEEK
ProcessPagingScope scope(*this);
copy_from_user(&result, address);
if (!copy_from_user(&result, address)) {
dbg() << "Invalid address for peek_user_data: " << address.ptr();
return KResult(-EFAULT);
}
return result;
}
KResult Process::poke_user_data(Userspace<u32*> address, u32 data)
{
// We validate for read (rather than write) because PT_POKE can write to readonly pages.
// So we effectively only care that the poke operation is trying to write to user pages.
if (!MM.validate_user_read(*this, VirtualAddress(address), sizeof(u32))) {
dbg() << "Invalid address for poke_user_data: " << address.ptr();
return KResult(-EFAULT);
}
ProcessPagingScope scope(*this);
Range range = { VirtualAddress(address), sizeof(u32) };
auto* region = find_region_containing(range);
@ -97,18 +91,23 @@ KResult Process::poke_user_data(Userspace<u32*> address, u32 data)
region->set_shared(false);
}
const bool was_writable = region->is_writable();
if (!was_writable) //TODO refactor into scopeguard
if (!was_writable)
{
region->set_writable(true);
region->remap();
}
ScopeGuard rollback([&]() {
if (!was_writable) {
region->set_writable(false);
region->remap();
}
});
copy_to_user(address, &data);
if (!was_writable) {
region->set_writable(false);
region->remap();
if (!copy_to_user(address, &data)) {
dbg() << "Invalid address for poke_user_data: " << address.ptr();
return KResult(-EFAULT);
}
return KResult(KSuccess);
}

View file

@ -38,8 +38,6 @@ ssize_t Process::sys$read(int fd, Userspace<u8*> buffer, ssize_t size)
return -EINVAL;
if (size == 0)
return 0;
if (!validate_write(buffer, size))
return -EFAULT;
#ifdef DEBUG_IO
dbg() << "sys$read(" << fd << ", " << (const void*)buffer.ptr() << ", " << size << ")";
#endif
@ -58,7 +56,10 @@ ssize_t Process::sys$read(int fd, Userspace<u8*> buffer, ssize_t size)
return -EAGAIN;
}
}
auto result = description->read(buffer.unsafe_userspace_ptr(), size);
auto user_buffer = UserOrKernelBuffer::for_user_buffer(buffer, size);
if (!user_buffer.has_value())
return -EFAULT;
auto result = description->read(user_buffer.value(), size);
if (result.is_error())
return result.error();
return result.value();

View file

@ -36,10 +36,7 @@ int Process::sys$readlink(Userspace<const Syscall::SC_readlink_params*> user_par
REQUIRE_PROMISE(rpath);
Syscall::SC_readlink_params params;
if (!validate_read_and_copy_typed(&params, user_params))
return -EFAULT;
if (!validate(params.buffer))
if (!copy_from_user(&params, user_params))
return -EFAULT;
auto path = get_syscall_path_argument(params.path);
@ -60,7 +57,8 @@ int Process::sys$readlink(Userspace<const Syscall::SC_readlink_params*> user_par
auto& link_target = contents.value();
auto size_to_copy = min(link_target.size(), params.buffer.size);
copy_to_user(params.buffer.data, link_target.data(), size_to_copy);
if (!copy_to_user(params.buffer.data, link_target.data(), size_to_copy))
return -EFAULT;
// Note: we return the whole size here, not the copied size.
return link_target.size();
}

View file

@ -36,10 +36,7 @@ int Process::sys$realpath(Userspace<const Syscall::SC_realpath_params*> user_par
REQUIRE_PROMISE(rpath);
Syscall::SC_realpath_params params;
if (!validate_read_and_copy_typed(&params, user_params))
return -EFAULT;
if (!validate_write(params.buffer.data, params.buffer.size))
if (!copy_from_user(&params, user_params))
return -EFAULT;
auto path = get_syscall_path_argument(params.path);
@ -55,7 +52,8 @@ int Process::sys$realpath(Userspace<const Syscall::SC_realpath_params*> user_par
if (absolute_path.length() + 1 > params.buffer.size)
return -ENAMETOOLONG;
copy_to_user(params.buffer.data, absolute_path.characters(), absolute_path.length() + 1);
if (!copy_to_user(params.buffer.data, absolute_path.characters(), absolute_path.length() + 1))
return -EFAULT;
return 0;
};

View file

@ -34,7 +34,7 @@ int Process::sys$rename(Userspace<const Syscall::SC_rename_params*> user_params)
{
REQUIRE_PROMISE(cpath);
Syscall::SC_rename_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
auto old_path = get_syscall_path_argument(params.old_path);
if (old_path.is_error())

View file

@ -51,11 +51,9 @@ int Process::sys$donate(pid_t tid)
int Process::sys$sched_setparam(int pid, Userspace<const struct sched_param*> user_param)
{
REQUIRE_PROMISE(proc);
if (!validate_read_typed(user_param))
return -EFAULT;
struct sched_param desired_param;
copy_from_user(&desired_param, user_param);
if (!copy_from_user(&desired_param, user_param))
return -EFAULT;
InterruptDisabler disabler;
auto* peer = Thread::current();
@ -78,9 +76,6 @@ int Process::sys$sched_setparam(int pid, Userspace<const struct sched_param*> us
int Process::sys$sched_getparam(pid_t pid, Userspace<struct sched_param*> user_param)
{
REQUIRE_PROMISE(proc);
if (!validate_write_typed(user_param))
return -EFAULT;
InterruptDisabler disabler;
auto* peer = Thread::current();
if (pid != 0) {
@ -98,7 +93,8 @@ int Process::sys$sched_getparam(pid_t pid, Userspace<struct sched_param*> user_p
struct sched_param param {
(int)peer->priority()
};
copy_to_user(user_param, &param);
if (!copy_to_user(user_param, &param))
return -EFAULT;
return 0;
}

View file

@ -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;
if (!copy_from_user(&params, user_params))
return -EFAULT;
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))
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;
}
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;
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 (fds[i].revents)
++fds_with_revents;
if (pfd.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;
}

View file

@ -37,25 +37,20 @@ int Process::sys$setkeymap(Userspace<const Syscall::SC_setkeymap_params*> user_p
return -EPERM;
Syscall::SC_setkeymap_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
Keyboard::CharacterMapData character_map_data;
if (!validate_read(params.map, CHAR_MAP_SIZE))
if (!copy_from_user(character_map_data.map, params.map, CHAR_MAP_SIZE * sizeof(u32)))
return -EFAULT;
if (!validate_read(params.shift_map, CHAR_MAP_SIZE))
if (!copy_from_user(character_map_data.shift_map, params.shift_map, CHAR_MAP_SIZE * sizeof(u32)))
return -EFAULT;
if (!validate_read(params.alt_map, CHAR_MAP_SIZE))
if (!copy_from_user(character_map_data.alt_map, params.alt_map, CHAR_MAP_SIZE * sizeof(u32)))
return -EFAULT;
if (!validate_read(params.altgr_map, CHAR_MAP_SIZE))
if (!copy_from_user(character_map_data.altgr_map, params.altgr_map, CHAR_MAP_SIZE * sizeof(u32)))
return -EFAULT;
copy_from_user(character_map_data.map, params.map, CHAR_MAP_SIZE * sizeof(u32));
copy_from_user(character_map_data.shift_map, params.shift_map, CHAR_MAP_SIZE * sizeof(u32));
copy_from_user(character_map_data.alt_map, params.alt_map, CHAR_MAP_SIZE * sizeof(u32));
copy_from_user(character_map_data.altgr_map, params.altgr_map, CHAR_MAP_SIZE * sizeof(u32));
auto map_name = get_syscall_path_argument(params.map_name);
if (map_name.is_error()) {
return map_name.error();

View file

@ -125,8 +125,6 @@ int Process::sys$setgroups(ssize_t count, Userspace<const gid_t*> user_gids)
return -EINVAL;
if (!is_superuser())
return -EPERM;
if (count && !validate_read(user_gids, count))
return -EFAULT;
if (!count) {
m_extra_gids.clear();
@ -135,7 +133,8 @@ int Process::sys$setgroups(ssize_t count, Userspace<const gid_t*> user_gids)
Vector<gid_t> gids;
gids.resize(count);
copy_from_user(gids.data(), user_gids, sizeof(gid_t) * count);
if (!copy_from_user(gids.data(), user_gids.unsafe_userspace_ptr(), sizeof(gid_t) * count))
return -EFAULT;
HashTable<gid_t> unique_extra_gids;
for (auto& gid : gids) {

View file

@ -47,8 +47,6 @@ int Process::sys$shbuf_create(int size, void** buffer)
if (!size || size < 0)
return -EINVAL;
size = PAGE_ROUND_UP(size);
if (!validate_write_typed(buffer))
return -EFAULT;
LOCKER(shared_buffers().lock());
static int s_next_shbuf_id;
@ -57,7 +55,8 @@ int Process::sys$shbuf_create(int size, void** buffer)
shared_buffer->share_with(m_pid);
void* address = shared_buffer->ref_for_process_and_get_address(*this);
copy_to_user(buffer, &address);
if (!copy_to_user(buffer, &address))
return -EFAULT;
ASSERT((int)shared_buffer->size() >= size);
#ifdef SHARED_BUFFER_DEBUG
klog() << "Created shared buffer " << shbuf_id << " @ " << buffer << " (" << size << " bytes, vmobject is " << shared_buffer->size() << ")";
@ -123,8 +122,6 @@ int Process::sys$shbuf_release(int shbuf_id)
void* Process::sys$shbuf_get(int shbuf_id, Userspace<size_t*> user_size)
{
REQUIRE_PROMISE(shared_buffer);
if (user_size && !validate_write_typed(user_size))
return (void*)-EFAULT;
LOCKER(shared_buffers().lock());
auto it = shared_buffers().resource().find(shbuf_id);
if (it == shared_buffers().resource().end())
@ -137,7 +134,8 @@ void* Process::sys$shbuf_get(int shbuf_id, Userspace<size_t*> user_size)
#endif
if (user_size) {
size_t size = shared_buffer.size();
copy_to_user(user_size, &size);
if (!copy_to_user(user_size, &size))
return (void*)-EFAULT;
}
return shared_buffer.ref_for_process_and_get_address(*this);
}

View file

@ -34,10 +34,9 @@ int Process::sys$sigprocmask(int how, Userspace<const sigset_t*> set, Userspace<
auto current_thread = Thread::current();
u32 previous_signal_mask;
if (set) {
if (!validate_read_typed(set))
return -EFAULT;
sigset_t set_value;
copy_from_user(&set_value, set);
if (!copy_from_user(&set_value, set))
return -EFAULT;
switch (how) {
case SIG_BLOCK:
previous_signal_mask = current_thread->signal_mask_block(set_value, true);
@ -54,21 +53,17 @@ int Process::sys$sigprocmask(int how, Userspace<const sigset_t*> set, Userspace<
} else {
previous_signal_mask = current_thread->signal_mask();
}
if (old_set) {
if (!validate_write_typed(old_set))
return -EFAULT;
copy_to_user(old_set, &previous_signal_mask);
}
if (old_set && !copy_to_user(old_set, &previous_signal_mask))
return -EFAULT;
return 0;
}
int Process::sys$sigpending(Userspace<sigset_t*> set)
{
REQUIRE_PROMISE(stdio);
if (!validate_write_typed(set))
return -EFAULT;
auto pending_signals = Thread::current()->pending_signals();
copy_to_user(set, &pending_signals);
if (!copy_to_user(set, &pending_signals))
return -EFAULT;
return 0;
}
@ -77,18 +72,18 @@ int Process::sys$sigaction(int signum, const sigaction* act, sigaction* old_act)
REQUIRE_PROMISE(sigaction);
if (signum < 1 || signum >= 32 || signum == SIGKILL || signum == SIGSTOP)
return -EINVAL;
if (!validate_read_typed(act))
return -EFAULT;
InterruptDisabler disabler; // FIXME: This should use a narrower lock. Maybe a way to ignore signals temporarily?
auto& action = Thread::current()->m_signal_action_data[signum];
if (old_act) {
if (!validate_write_typed(old_act))
if (!copy_to_user(&old_act->sa_flags, &action.flags))
return -EFAULT;
if (!copy_to_user(&old_act->sa_sigaction, &action.handler_or_sigaction, sizeof(action.handler_or_sigaction)))
return -EFAULT;
copy_to_user(&old_act->sa_flags, &action.flags);
copy_to_user(&old_act->sa_sigaction, &action.handler_or_sigaction, sizeof(action.handler_or_sigaction));
}
copy_from_user(&action.flags, &act->sa_flags);
copy_from_user(&action.handler_or_sigaction, &act->sa_sigaction, sizeof(action.handler_or_sigaction));
if (!copy_from_user(&action.flags, &act->sa_flags))
return -EFAULT;
if (!copy_from_user(&action.handler_or_sigaction, &act->sa_sigaction, sizeof(action.handler_or_sigaction)))
return -EFAULT;
return 0;
}

View file

@ -65,8 +65,6 @@ int Process::sys$socket(int domain, int type, int protocol)
int Process::sys$bind(int sockfd, Userspace<const sockaddr*> address, socklen_t address_length)
{
if (!validate_read(address, address_length))
return -EFAULT;
auto description = file_description(sockfd);
if (!description)
return -EBADF;
@ -98,14 +96,8 @@ int Process::sys$accept(int accepting_socket_fd, Userspace<sockaddr*> user_addre
REQUIRE_PROMISE(accept);
socklen_t address_size = 0;
if (user_address) {
if (!validate_write_typed(user_address_size))
return -EFAULT;
if (!validate_read_and_copy_typed(&address_size, user_address_size))
return -EFAULT;
if (!validate_write(user_address, address_size))
return -EFAULT;
}
if (user_address && !copy_from_user(&address_size, static_ptr_cast<const socklen_t*>(user_address_size)))
return -EFAULT;
int accepted_socket_fd = alloc_fd();
if (accepted_socket_fd < 0)
@ -132,8 +124,10 @@ int Process::sys$accept(int accepting_socket_fd, Userspace<sockaddr*> user_addre
u8 address_buffer[sizeof(sockaddr_un)];
address_size = min(sizeof(sockaddr_un), static_cast<size_t>(address_size));
accepted_socket->get_peer_address((sockaddr*)address_buffer, &address_size);
copy_to_user(user_address, address_buffer, address_size);
copy_to_user(user_address_size, &address_size);
if (!copy_to_user(user_address, address_buffer, address_size))
return -EFAULT;
if (!copy_to_user(user_address_size, &address_size))
return -EFAULT;
}
auto accepted_socket_description = FileDescription::create(*accepted_socket);
@ -151,8 +145,6 @@ int Process::sys$accept(int accepting_socket_fd, Userspace<sockaddr*> user_addre
int Process::sys$connect(int sockfd, Userspace<const sockaddr*> user_address, socklen_t user_address_size)
{
if (!validate_read(user_address, user_address_size))
return -EFAULT;
int fd = alloc_fd();
if (fd < 0)
return fd;
@ -165,11 +157,7 @@ int Process::sys$connect(int sockfd, Userspace<const sockaddr*> user_address, so
auto& socket = *description->socket();
REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain());
u8 address[sizeof(sockaddr_un)];
size_t address_size = min(sizeof(address), static_cast<size_t>(user_address_size));
copy_from_user(address, user_address, address_size);
return socket.connect(*description, (const sockaddr*)address, address_size, description->is_blocking() ? ShouldBlock::Yes : ShouldBlock::No);
return socket.connect(*description, user_address, user_address_size, description->is_blocking() ? ShouldBlock::Yes : ShouldBlock::No);
}
int Process::sys$shutdown(int sockfd, int how)
@ -192,17 +180,13 @@ ssize_t Process::sys$sendto(Userspace<const Syscall::SC_sendto_params*> user_par
{
REQUIRE_PROMISE(stdio);
Syscall::SC_sendto_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
int flags = params.flags;
Userspace<const sockaddr*> addr = params.addr;
Userspace<const sockaddr*> addr((FlatPtr)params.addr);
socklen_t addr_length = params.addr_length;
if (!validate(params.data))
return -EFAULT;
if (addr && !validate_read(addr, addr_length))
return -EFAULT;
auto description = file_description(params.sockfd);
if (!description)
return -EBADF;
@ -212,7 +196,10 @@ ssize_t Process::sys$sendto(Userspace<const Syscall::SC_sendto_params*> user_par
if (socket.is_shut_down_for_writing())
return -EPIPE;
SmapDisabler disabler;
auto result = socket.sendto(*description, params.data.data, params.data.size, flags, addr, addr_length);
auto data_buffer = UserOrKernelBuffer::for_user_buffer(const_cast<u8*>((const u8*)params.data.data), params.data.size);
if (!data_buffer.has_value())
return -EFAULT;
auto result = socket.sendto(*description, data_buffer.value(), params.data.size, flags, addr, addr_length);
if (result.is_error())
return result.error();
return result.value();
@ -223,27 +210,17 @@ ssize_t Process::sys$recvfrom(Userspace<const Syscall::SC_recvfrom_params*> user
REQUIRE_PROMISE(stdio);
Syscall::SC_recvfrom_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
int flags = params.flags;
Userspace<sockaddr*> user_addr = params.addr;
Userspace<socklen_t*> user_addr_length = params.addr_length;
Userspace<sockaddr*> user_addr((FlatPtr)params.addr);
Userspace<socklen_t*> user_addr_length((FlatPtr)params.addr_length);
SmapDisabler disabler;
if (!validate(params.buffer))
return -EFAULT;
if (user_addr_length) {
socklen_t addr_length;
if (!validate_read_and_copy_typed(&addr_length, user_addr_length))
return -EFAULT;
if (!validate_write_typed(user_addr_length))
return -EFAULT;
if (!validate_write(user_addr, addr_length))
return -EFAULT;
} else if (user_addr) {
if (!user_addr_length && user_addr)
return -EINVAL;
}
auto description = file_description(params.sockfd);
if (!description)
return -EBADF;
@ -258,7 +235,10 @@ ssize_t Process::sys$recvfrom(Userspace<const Syscall::SC_recvfrom_params*> user
if (flags & MSG_DONTWAIT)
description->set_blocking(false);
auto result = socket.recvfrom(*description, params.buffer.data, params.buffer.size, flags, user_addr, user_addr_length);
auto data_buffer = UserOrKernelBuffer::for_user_buffer(const_cast<u8*>((const u8*)params.buffer.data), params.buffer.size);
if (!data_buffer.has_value())
return -EFAULT;
auto result = socket.recvfrom(*description, data_buffer.value(), params.buffer.size, flags, user_addr, user_addr_length);
if (flags & MSG_DONTWAIT)
description->set_blocking(original_blocking);
@ -271,18 +251,12 @@ template<bool sockname, typename Params>
int Process::get_sock_or_peer_name(const Params& params)
{
socklen_t addrlen_value;
if (!validate_read_and_copy_typed(&addrlen_value, params.addrlen))
if (!copy_from_user(&addrlen_value, params.addrlen, sizeof(socklen_t)))
return -EFAULT;
if (addrlen_value <= 0)
return -EINVAL;
if (!validate_write(params.addr, addrlen_value))
return -EFAULT;
if (!validate_write_typed(params.addrlen))
return -EFAULT;
auto description = file_description(params.sockfd);
if (!description)
return -EBADF;
@ -299,15 +273,17 @@ int Process::get_sock_or_peer_name(const Params& params)
socket.get_local_address((sockaddr*)address_buffer, &addrlen_value);
else
socket.get_peer_address((sockaddr*)address_buffer, &addrlen_value);
copy_to_user(params.addr, address_buffer, addrlen_value);
copy_to_user(params.addrlen, &addrlen_value);
if (!copy_to_user(params.addr, address_buffer, addrlen_value))
return -EFAULT;
if (!copy_to_user(params.addrlen, &addrlen_value))
return -EFAULT;
return 0;
}
int Process::sys$getsockname(Userspace<const Syscall::SC_getsockname_params*> user_params)
{
Syscall::SC_getsockname_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
return get_sock_or_peer_name<true>(params);
}
@ -315,7 +291,7 @@ int Process::sys$getsockname(Userspace<const Syscall::SC_getsockname_params*> us
int Process::sys$getpeername(Userspace<const Syscall::SC_getpeername_params*> user_params)
{
Syscall::SC_getpeername_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
return get_sock_or_peer_name<false>(params);
}
@ -323,22 +299,19 @@ int Process::sys$getpeername(Userspace<const Syscall::SC_getpeername_params*> us
int Process::sys$getsockopt(Userspace<const Syscall::SC_getsockopt_params*> user_params)
{
Syscall::SC_getsockopt_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
int sockfd = params.sockfd;
int level = params.level;
int option = params.option;
Userspace<void*> user_value = params.value;
Userspace<socklen_t*> user_value_size = params.value_size;
Userspace<void*> user_value((FlatPtr)params.value);
Userspace<socklen_t*> user_value_size((FlatPtr)params.value_size);
if (!validate_write_typed(user_value_size))
return -EFAULT;
socklen_t value_size;
if (!validate_read_and_copy_typed(&value_size, user_value_size))
return -EFAULT;
if (!validate_write(user_value, value_size))
if (!copy_from_user(&value_size, params.value_size, sizeof(socklen_t)))
return -EFAULT;
auto description = file_description(sockfd);
if (!description)
return -EBADF;
@ -357,10 +330,9 @@ int Process::sys$getsockopt(Userspace<const Syscall::SC_getsockopt_params*> user
int Process::sys$setsockopt(Userspace<const Syscall::SC_setsockopt_params*> user_params)
{
Syscall::SC_setsockopt_params params;
if (!validate_read_and_copy_typed(&params, user_params))
return -EFAULT;
if (!validate_read(params.value, params.value_size))
if (!copy_from_user(&params, user_params))
return -EFAULT;
Userspace<const void*> user_value((FlatPtr)params.value);
auto description = file_description(params.sockfd);
if (!description)
return -EBADF;
@ -368,7 +340,7 @@ int Process::sys$setsockopt(Userspace<const Syscall::SC_setsockopt_params*> user
return -ENOTSOCK;
auto& socket = *description->socket();
REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain());
return socket.setsockopt(params.level, params.option, params.value, params.value_size);
return socket.setsockopt(params.level, params.option, user_value, params.value_size);
}
}

View file

@ -34,14 +34,13 @@ namespace Kernel {
int Process::sys$fstat(int fd, Userspace<stat*> user_statbuf)
{
REQUIRE_PROMISE(stdio);
if (!validate_write_typed(user_statbuf))
return -EFAULT;
auto description = file_description(fd);
if (!description)
return -EBADF;
stat buffer = {};
int rc = description->stat(buffer);
copy_to_user(user_statbuf, &buffer);
if (!copy_to_user(user_statbuf, &buffer))
return -EFAULT;
return rc;
}
@ -49,9 +48,7 @@ int Process::sys$stat(Userspace<const Syscall::SC_stat_params*> user_params)
{
REQUIRE_PROMISE(rpath);
Syscall::SC_stat_params params;
if (!validate_read_and_copy_typed(&params, user_params))
return -EFAULT;
if (!validate_write_typed(params.statbuf))
if (!copy_from_user(&params, user_params))
return -EFAULT;
auto path = get_syscall_path_argument(params.path);
if (path.is_error())
@ -63,7 +60,8 @@ int Process::sys$stat(Userspace<const Syscall::SC_stat_params*> user_params)
auto result = metadata_or_error.value().stat(statbuf);
if (result.is_error())
return result;
copy_to_user(params.statbuf, &statbuf);
if (!copy_to_user(params.statbuf, &statbuf))
return -EFAULT;
return 0;
}

View file

@ -36,22 +36,16 @@ namespace Kernel {
int Process::sys$create_thread(void* (*entry)(void*), Userspace<const Syscall::SC_create_thread_params*> user_params)
{
REQUIRE_PROMISE(thread);
if (!validate_read((const void*)entry, sizeof(void*)))
return -EFAULT;
Syscall::SC_create_thread_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
unsigned detach_state = params.m_detach_state;
int schedule_priority = params.m_schedule_priority;
Userspace<void*> stack_location = params.m_stack_location;
unsigned stack_size = params.m_stack_size;
if (!validate_write(stack_location, stack_size))
return -EFAULT;
u32 user_stack_address = reinterpret_cast<u32>(stack_location.ptr()) + stack_size;
auto user_stack_address = (u8*)params.m_stack_location + stack_size;
if (!MM.validate_user_stack(*this, VirtualAddress(user_stack_address - 4)))
return -EFAULT;
@ -83,7 +77,7 @@ int Process::sys$create_thread(void* (*entry)(void*), Userspace<const Syscall::S
tss.eip = (FlatPtr)entry;
tss.eflags = 0x0202;
tss.cr3 = page_directory().cr3();
tss.esp = user_stack_address;
tss.esp = (u32)user_stack_address;
thread->make_thread_specific_region({});
thread->set_state(Thread::State::Runnable);
@ -120,8 +114,6 @@ int Process::sys$detach_thread(pid_t tid)
int Process::sys$join_thread(pid_t tid, Userspace<void**> exit_value)
{
REQUIRE_PROMISE(thread);
if (exit_value && !validate_write_typed(exit_value))
return -EFAULT;
InterruptDisabler disabler;
auto* thread = Thread::from_tid(tid);
@ -164,15 +156,15 @@ int Process::sys$join_thread(pid_t tid, Userspace<void**> exit_value)
// NOTE: 'thread' is very possibly deleted at this point. Clear it just to be safe.
thread = nullptr;
if (exit_value)
copy_to_user(exit_value, &joinee_exit_value);
if (exit_value && !copy_to_user(exit_value, &joinee_exit_value))
return -EFAULT;
return 0;
}
int Process::sys$set_thread_name(pid_t tid, Userspace<const char*> user_name, size_t user_name_length)
{
REQUIRE_PROMISE(thread);
auto name = validate_and_copy_string_from_user(user_name, user_name_length);
auto name = copy_string_from_user(user_name, user_name_length);
if (name.is_null())
return -EFAULT;
@ -185,7 +177,7 @@ int Process::sys$set_thread_name(pid_t tid, Userspace<const char*> user_name, si
if (!thread || thread->pid() != pid())
return -ESRCH;
thread->set_name(name);
thread->set_name(move(name));
return 0;
}
@ -195,9 +187,6 @@ int Process::sys$get_thread_name(pid_t tid, Userspace<char*> buffer, size_t buff
if (buffer_size == 0)
return -EINVAL;
if (!validate_write(buffer, buffer_size))
return -EFAULT;
InterruptDisabler disabler;
auto* thread = Thread::from_tid(tid);
if (!thread || thread->pid() != pid())
@ -206,7 +195,8 @@ int Process::sys$get_thread_name(pid_t tid, Userspace<char*> buffer, size_t buff
if (thread->name().length() + 1 > (size_t)buffer_size)
return -ENAMETOOLONG;
copy_to_user(buffer, thread->name().characters(), thread->name().length() + 1);
if (!copy_to_user(buffer, thread->name().characters(), thread->name().length() + 1))
return -EFAULT;
return 0;
}

View file

@ -31,16 +31,14 @@ namespace Kernel {
clock_t Process::sys$times(Userspace<tms*> user_times)
{
REQUIRE_PROMISE(stdio);
if (!validate_write_typed(user_times))
return -EFAULT;
tms times = {};
times.tms_utime = m_ticks_in_user;
times.tms_stime = m_ticks_in_kernel;
times.tms_cutime = m_ticks_in_user_for_dead_children;
times.tms_cstime = m_ticks_in_kernel_for_dead_children;
copy_to_user(user_times, &times);
if (!copy_to_user(user_times, &times))
return -EFAULT;
return g_uptime & 0x7fffffff;
}

View file

@ -34,8 +34,6 @@ namespace Kernel {
int Process::sys$ttyname(int fd, Userspace<char*> buffer, size_t size)
{
REQUIRE_PROMISE(tty);
if (!validate_write(buffer, size))
return -EFAULT;
auto description = file_description(fd);
if (!description)
return -EBADF;
@ -44,15 +42,14 @@ int Process::sys$ttyname(int fd, Userspace<char*> buffer, size_t size)
auto tty_name = description->tty()->tty_name();
if (size < tty_name.length() + 1)
return -ERANGE;
copy_to_user(buffer, tty_name.characters(), tty_name.length() + 1);
if (!copy_to_user(buffer, tty_name.characters(), tty_name.length() + 1))
return -EFAULT;
return 0;
}
int Process::sys$ptsname(int fd, Userspace<char*> buffer, size_t size)
{
REQUIRE_PROMISE(tty);
if (!validate_write(buffer, size))
return -EFAULT;
auto description = file_description(fd);
if (!description)
return -EBADF;
@ -62,7 +59,8 @@ int Process::sys$ptsname(int fd, Userspace<char*> buffer, size_t size)
auto pts_name = master_pty->pts_name();
if (size < pts_name.length() + 1)
return -ERANGE;
copy_to_user(buffer, pts_name.characters(), pts_name.length() + 1);
if (!copy_to_user(buffer, pts_name.characters(), pts_name.length() + 1))
return -EFAULT;
return 0;
}

View file

@ -34,9 +34,6 @@ int Process::sys$uname(Userspace<utsname*> buf)
extern Lock* g_hostname_lock;
REQUIRE_PROMISE(stdio);
if (!validate_write_typed(buf))
return -EFAULT;
LOCKER(*g_hostname_lock, Lock::Mode::Shared);
if (g_hostname->length() + 1 > sizeof(utsname::nodename))
@ -45,11 +42,16 @@ int Process::sys$uname(Userspace<utsname*> buf)
// We have already validated the entire utsname struct at this
// point, there is no need to re-validate every write to the struct.
utsname* user_buf = buf.unsafe_userspace_ptr();
copy_to_user(user_buf->sysname, "SerenityOS", 11);
copy_to_user(user_buf->release, "1.0-dev", 8);
copy_to_user(user_buf->version, "FIXME", 6);
copy_to_user(user_buf->machine, "i686", 5);
copy_to_user(user_buf->nodename, g_hostname->characters(), g_hostname->length() + 1);
if (!copy_to_user(user_buf->sysname, "SerenityOS", 11))
return -EFAULT;
if (!copy_to_user(user_buf->release, "1.0-dev", 8))
return -EFAULT;
if (!copy_to_user(user_buf->version, "FIXME", 6))
return -EFAULT;
if (!copy_to_user(user_buf->machine, "i686", 5))
return -EFAULT;
if (!copy_to_user(user_buf->nodename, g_hostname->characters(), g_hostname->length() + 1))
return -EFAULT;
return 0;
}

View file

@ -33,8 +33,6 @@ namespace Kernel {
int Process::sys$unlink(Userspace<const char*> user_path, size_t path_length)
{
REQUIRE_PROMISE(cpath);
if (!validate_read(user_path, path_length))
return -EFAULT;
auto path = get_syscall_path_argument(user_path, path_length);
if (path.is_error())
return path.error();

View file

@ -34,7 +34,7 @@ namespace Kernel {
int Process::sys$unveil(Userspace<const Syscall::SC_unveil_params*> user_params)
{
Syscall::SC_unveil_params params;
if (!validate_read_and_copy_typed(&params, user_params))
if (!copy_from_user(&params, user_params))
return -EFAULT;
if (!params.path.characters && !params.permissions.characters) {
@ -66,7 +66,7 @@ int Process::sys$unveil(Userspace<const Syscall::SC_unveil_params*> user_params)
auto& custody = custody_or_error.value();
auto new_unveiled_path = custody->absolute_path();
auto permissions = validate_and_copy_string_from_user(params.permissions);
auto permissions = copy_string_from_user(params.permissions);
if (permissions.is_null())
return -EFAULT;

View file

@ -33,14 +33,13 @@ namespace Kernel {
int Process::sys$utime(Userspace<const char*> user_path, size_t path_length, Userspace<const struct utimbuf*> user_buf)
{
REQUIRE_PROMISE(fattr);
if (user_buf && !validate_read_typed(user_buf))
return -EFAULT;
auto path = get_syscall_path_argument(user_path, path_length);
if (path.is_error())
return path.error();
utimbuf buf;
if (user_buf) {
copy_from_user(&buf, user_buf);
if (!copy_from_user(&buf, user_buf))
return -EFAULT;
} else {
auto now = kgettimeofday();
buf = { now.tv_sec, now.tv_sec };

View file

@ -104,27 +104,19 @@ pid_t Process::sys$waitid(Userspace<const Syscall::SC_waitid_params*> user_param
REQUIRE_PROMISE(proc);
Syscall::SC_waitid_params params;
if (!validate_read_and_copy_typed(&params, user_params))
return -EFAULT;
if (!validate_write_typed(params.infop))
if (!copy_from_user(&params, user_params))
return -EFAULT;
#ifdef PROCESS_DEBUG
dbg() << "sys$waitid(" << params.idtype << ", " << params.id << ", " << params.infop.ptr() << ", " << params.options << ")";
dbg() << "sys$waitid(" << params.idtype << ", " << params.id << ", " << params.infop << ", " << params.options << ")";
#endif
auto siginfo_or_error = do_waitid(static_cast<idtype_t>(params.idtype), params.id, params.options);
if (siginfo_or_error.is_error())
return siginfo_or_error.error();
// While we waited, 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 the pointer.
if (!validate_write_typed(params.infop))
return -EFAULT;
copy_to_user(params.infop, &siginfo_or_error.value());
if (!copy_to_user(params.infop, &siginfo_or_error.value()))
return -EFAULT;
return 0;
}

View file

@ -36,16 +36,19 @@ ssize_t Process::sys$writev(int fd, const struct iovec* iov, int iov_count)
if (iov_count < 0)
return -EINVAL;
if (!validate_read_typed(iov, iov_count))
return -EFAULT;
{
Checked checked_iov_count = sizeof(iovec);
checked_iov_count *= iov_count;
if (checked_iov_count.has_overflow())
return -EFAULT;
}
u64 total_length = 0;
Vector<iovec, 32> vecs;
vecs.resize(iov_count);
copy_from_user(vecs.data(), iov, iov_count * sizeof(iovec));
if (!copy_n_from_user(vecs.data(), iov, iov_count))
return -EFAULT;
for (auto& vec : vecs) {
if (!validate_read(vec.iov_base, vec.iov_len))
return -EFAULT;
total_length += vec.iov_len;
if (total_length > NumericLimits<i32>::max())
return -EINVAL;
@ -60,7 +63,10 @@ ssize_t Process::sys$writev(int fd, const struct iovec* iov, int iov_count)
int nwritten = 0;
for (auto& vec : vecs) {
int rc = do_write(*description, (const u8*)vec.iov_base, vec.iov_len);
auto buffer = UserOrKernelBuffer::for_user_buffer((u8*)vec.iov_base, vec.iov_len);
if (!buffer.has_value())
return -EFAULT;
int rc = do_write(*description, buffer.value(), vec.iov_len);
if (rc < 0) {
if (nwritten == 0)
return rc;
@ -72,7 +78,7 @@ ssize_t Process::sys$writev(int fd, const struct iovec* iov, int iov_count)
return nwritten;
}
ssize_t Process::do_write(FileDescription& description, const u8* data, int data_size)
ssize_t Process::do_write(FileDescription& description, const UserOrKernelBuffer& data, size_t data_size)
{
ssize_t total_nwritten = 0;
if (!description.is_blocking()) {
@ -83,7 +89,7 @@ ssize_t Process::do_write(FileDescription& description, const u8* data, int data
if (description.should_append())
description.seek(0, SEEK_END);
while (total_nwritten < data_size) {
while ((size_t)total_nwritten < data_size) {
if (!description.can_write()) {
if (!description.is_blocking()) {
// Short write: We can no longer write to this non-blocking description.
@ -95,7 +101,7 @@ ssize_t Process::do_write(FileDescription& description, const u8* data, int data
return -EINTR;
}
}
auto nwritten_or_error = description.write(data + total_nwritten, data_size - total_nwritten);
auto nwritten_or_error = description.write(data.offset(total_nwritten), data_size - total_nwritten);
if (nwritten_or_error.is_error()) {
if (total_nwritten)
return total_nwritten;
@ -115,8 +121,7 @@ ssize_t Process::sys$write(int fd, const u8* data, ssize_t size)
return -EINVAL;
if (size == 0)
return 0;
if (!validate_read(data, size))
return -EFAULT;
#ifdef DEBUG_IO
dbg() << "sys$write(" << fd << ", " << (const void*)(data) << ", " << size << ")";
#endif
@ -126,7 +131,10 @@ ssize_t Process::sys$write(int fd, const u8* data, ssize_t size)
if (!description->is_writable())
return -EBADF;
return do_write(*description, data, size);
auto buffer = UserOrKernelBuffer::for_user_buffer(const_cast<u8*>(data), (size_t)size);
if (!buffer.has_value())
return -EFAULT;
return do_write(*description, buffer.value(), size);
}
}