diff --git a/DevTools/UserspaceEmulator/Emulator.cpp b/DevTools/UserspaceEmulator/Emulator.cpp index 77d92aca67ea94..72b37b1fd3c9a4 100644 --- a/DevTools/UserspaceEmulator/Emulator.cpp +++ b/DevTools/UserspaceEmulator/Emulator.cpp @@ -930,7 +930,7 @@ int Emulator::virt$execve(FlatPtr params_addr) auto copy_string_list = [this](auto& output_vector, auto& string_list) { for (size_t i = 0; i < string_list.length; ++i) { Syscall::StringArgument string; - mmu().copy_from_vm(&string, (FlatPtr)&string_list.strings.ptr()[i], sizeof(string)); + mmu().copy_from_vm(&string, (FlatPtr)&string_list.strings[i], sizeof(string)); output_vector.append(String::copy(mmu().copy_buffer_from_vm((FlatPtr)string.characters, string.length))); } }; @@ -1307,7 +1307,7 @@ int Emulator::virt$waitid(FlatPtr params_addr) } if (params.infop) - mmu().copy_to_vm(params.infop, &info, sizeof(info)); + mmu().copy_to_vm((FlatPtr)params.infop, &info, sizeof(info)); return rc; } diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h index b4822cdcdf66d2..5a95809701c8c9 100644 --- a/Kernel/API/Syscall.h +++ b/Kernel/API/Syscall.h @@ -222,7 +222,7 @@ inline constexpr const char* to_string(Function function) #ifdef __serenity__ struct StringArgument { - Userspace characters; + const char* characters; size_t length { 0 }; }; @@ -239,7 +239,7 @@ struct ImmutableBufferArgument { }; struct StringListArgument { - Userspace strings {}; + StringArgument* strings {}; size_t length { 0 }; }; @@ -273,22 +273,22 @@ struct SC_select_params { struct SC_poll_params { struct pollfd* fds; unsigned nfds; - Userspace timeout; - Userspace sigmask; + const struct timespec* timeout; + const u32* sigmask; }; struct SC_clock_nanosleep_params { int clock_id; int flags; - Userspace requested_sleep; - Userspace remaining_sleep; + const struct timespec* requested_sleep; + struct timespec* remaining_sleep; }; struct SC_sendto_params { int sockfd; ImmutableBufferArgument data; int flags; - Userspace addr; + const sockaddr* addr; socklen_t addr_length; }; @@ -296,50 +296,50 @@ struct SC_recvfrom_params { int sockfd; MutableBufferArgument buffer; int flags; - Userspace addr; - Userspace addr_length; + sockaddr* addr; + socklen_t* addr_length; }; struct SC_getsockopt_params { int sockfd; int level; int option; - Userspace value; - Userspace value_size; + void* value; + socklen_t* value_size; }; struct SC_setsockopt_params { int sockfd; int level; int option; - Userspace value; + const void* value; socklen_t value_size; }; struct SC_getsockname_params { int sockfd; - Userspace addr; - Userspace addrlen; + sockaddr* addr; + socklen_t* addrlen; }; struct SC_getpeername_params { int sockfd; - Userspace addr; - Userspace addrlen; + sockaddr* addr; + socklen_t* addrlen; }; struct SC_futex_params { - Userspace userspace_address; + const i32* userspace_address; int futex_op; i32 val; - Userspace timeout; + const timespec* timeout; }; struct SC_setkeymap_params { - Userspace map; - Userspace shift_map; - Userspace alt_map; - Userspace altgr_map; + const u32* map; + const u32* shift_map; + const u32* alt_map; + const u32* altgr_map; StringArgument map_name; }; @@ -354,7 +354,7 @@ struct SC_create_thread_params { unsigned int m_guard_page_size = 0; // Rounded up to PAGE_SIZE unsigned int m_reported_guard_page_size = 0; // The lie we tell callers unsigned int m_stack_size = 4 * MiB; // Default PTHREAD_STACK_MIN - Userspace m_stack_location; // nullptr means any, o.w. process virtual address + void* m_stack_location; // nullptr means any, o.w. process virtual address }; struct SC_realpath_params { @@ -426,26 +426,26 @@ struct SC_unveil_params { struct SC_waitid_params { int idtype; int id; - Userspace infop; + struct siginfo* infop; int options; }; struct SC_stat_params { StringArgument path; - Userspace statbuf; + struct stat* statbuf; bool follow_symlinks; }; struct SC_ptrace_params { int request; pid_t tid; - Userspace addr; + u8* addr; int data; }; struct SC_ptrace_peek_params { - Userspace address; - Userspace out_data; + const u32* address; + u32* out_data; }; void initialize(); diff --git a/Kernel/Arch/i386/CPU.cpp b/Kernel/Arch/i386/CPU.cpp index 7fbe6faa35944a..d1a9ccb136ecb4 100644 --- a/Kernel/Arch/i386/CPU.cpp +++ b/Kernel/Arch/i386/CPU.cpp @@ -226,6 +226,7 @@ extern "C" u8* safe_memset_2_faulted; bool safe_memcpy(void* dest_ptr, const void* src_ptr, size_t n, void*& fault_at) { + fault_at = nullptr; size_t dest = (size_t)dest_ptr; size_t src = (size_t)src_ptr; size_t remainder; @@ -233,7 +234,6 @@ bool safe_memcpy(void* dest_ptr, const void* src_ptr, size_t n, void*& fault_at) if (!(dest & 0x3) && !(src & 0x3) && n >= 12) { size_t size_ts = n / sizeof(size_t); asm volatile( - "xor %[fault_at], %[fault_at] \n" ".global safe_memcpy_ins_1 \n" "safe_memcpy_ins_1: \n" "rep movsl \n" @@ -256,7 +256,6 @@ bool safe_memcpy(void* dest_ptr, const void* src_ptr, size_t n, void*& fault_at) } } asm volatile( - "xor %[fault_at], %[fault_at] \n" ".global safe_memcpy_ins_2 \n" "safe_memcpy_ins_2: \n" "rep movsb \n" @@ -277,8 +276,8 @@ bool safe_memcpy(void* dest_ptr, const void* src_ptr, size_t n, void*& fault_at) ssize_t safe_strnlen(const char* str, size_t max_n, void*& fault_at) { ssize_t count = 0; + fault_at = nullptr; asm volatile( - "xor %[fault_at], %[fault_at] \n" "1: \n" "test %[max_n], %[max_n] \n" "je 2f \n" @@ -307,6 +306,7 @@ ssize_t safe_strnlen(const char* str, size_t max_n, void*& fault_at) bool safe_memset(void* dest_ptr, int c, size_t n, void*& fault_at) { + fault_at = nullptr; size_t dest = (size_t)dest_ptr; size_t remainder; // FIXME: Support starting at an unaligned address. @@ -316,7 +316,6 @@ bool safe_memset(void* dest_ptr, int c, size_t n, void*& fault_at) expanded_c |= expanded_c << 8; expanded_c |= expanded_c << 16; asm volatile( - "xor %[fault_at], %[fault_at] \n" ".global safe_memset_ins_1 \n" "safe_memset_ins_1: \n" "rep stosl \n" @@ -338,7 +337,6 @@ bool safe_memset(void* dest_ptr, int c, size_t n, void*& fault_at) } } asm volatile( - "xor %[fault_at], %[fault_at] \n" ".global safe_memset_ins_2 \n" "safe_memset_ins_2: \n" "rep stosb \n" diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 1b7d04e10ed4d8..646cf406d115ce 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -176,6 +176,7 @@ set(KERNEL_SOURCES Time/RTC.cpp Time/TimeManagement.cpp TimerQueue.cpp + UserOrKernelBuffer.cpp VM/AnonymousVMObject.cpp VM/ContiguousVMObject.cpp VM/InodeVMObject.cpp diff --git a/Kernel/Console.cpp b/Kernel/Console.cpp index e511a2d8763ba0..b919f4fe086e42 100644 --- a/Kernel/Console.cpp +++ b/Kernel/Console.cpp @@ -65,20 +65,26 @@ bool Console::can_read(const Kernel::FileDescription&, size_t) const return false; } -Kernel::KResultOr Console::read(Kernel::FileDescription&, size_t, u8*, size_t) +Kernel::KResultOr Console::read(Kernel::FileDescription&, size_t, Kernel::UserOrKernelBuffer&, size_t) { // FIXME: Implement reading from the console. // Maybe we could use a ring buffer for this device? return 0; } -Kernel::KResultOr Console::write(Kernel::FileDescription&, size_t, const u8* data, size_t size) +Kernel::KResultOr Console::write(Kernel::FileDescription&, size_t, const Kernel::UserOrKernelBuffer& data, size_t size) { if (!size) return 0; - for (size_t i = 0; i < size; ++i) - put_char(data[i]); - return size; + + ssize_t nread = data.read_buffered<256>(size, [&](const u8* bytes, size_t bytes_count) { + for (size_t i = 0; i < bytes_count; i++) + put_char((char)bytes[i]); + return (ssize_t)bytes_count; + }); + if (nread < 0) + return Kernel::KResult(nread); + return (size_t)nread; } void Console::put_char(char ch) diff --git a/Kernel/Console.h b/Kernel/Console.h index f06ebf2a6ac3f3..c37731e4f7f34e 100644 --- a/Kernel/Console.h +++ b/Kernel/Console.h @@ -43,8 +43,8 @@ class Console final : public Kernel::CharacterDevice { // ^CharacterDevice virtual bool can_read(const Kernel::FileDescription&, size_t) const override; virtual bool can_write(const Kernel::FileDescription&, size_t) const override { return true; } - virtual Kernel::KResultOr read(Kernel::FileDescription&, size_t, u8*, size_t) override; - virtual Kernel::KResultOr write(Kernel::FileDescription&, size_t, const u8*, size_t) override; + virtual Kernel::KResultOr read(Kernel::FileDescription&, size_t, Kernel::UserOrKernelBuffer&, size_t) override; + virtual Kernel::KResultOr write(Kernel::FileDescription&, size_t, const Kernel::UserOrKernelBuffer&, size_t) override; virtual const char* class_name() const override { return "Console"; } void put_char(char); diff --git a/Kernel/Devices/BXVGADevice.cpp b/Kernel/Devices/BXVGADevice.cpp index c9f173226001e9..85040ed7472c99 100644 --- a/Kernel/Devices/BXVGADevice.cpp +++ b/Kernel/Devices/BXVGADevice.cpp @@ -203,18 +203,16 @@ int BXVGADevice::ioctl(FileDescription&, unsigned request, FlatPtr arg) switch (request) { case FB_IOCTL_GET_SIZE_IN_BYTES: { auto* out = (size_t*)arg; - if (!Process::current()->validate_write_typed(out)) - return -EFAULT; size_t value = framebuffer_size_in_bytes(); - copy_to_user(out, &value); + if (!copy_to_user(out, &value)) + return -EFAULT; return 0; } case FB_IOCTL_GET_BUFFER: { auto* index = (int*)arg; - if (!Process::current()->validate_write_typed(index)) - return -EFAULT; int value = m_y_offset == 0 ? 0 : 1; - copy_to_user(index, &value); + if (!copy_to_user(index, &value)) + return -EFAULT; return 0; } case FB_IOCTL_SET_BUFFER: { @@ -225,21 +223,18 @@ int BXVGADevice::ioctl(FileDescription&, unsigned request, FlatPtr arg) } case FB_IOCTL_GET_RESOLUTION: { auto* user_resolution = (FBResolution*)arg; - if (!Process::current()->validate_write_typed(user_resolution)) - return -EFAULT; FBResolution resolution; resolution.pitch = m_framebuffer_pitch; resolution.width = m_framebuffer_width; resolution.height = m_framebuffer_height; - copy_to_user(user_resolution, &resolution); + if (!copy_to_user(user_resolution, &resolution)) + return -EFAULT; return 0; } case FB_IOCTL_SET_RESOLUTION: { auto* user_resolution = (FBResolution*)arg; - if (!Process::current()->validate_write_typed(user_resolution)) - return -EFAULT; FBResolution resolution; - if (!Process::current()->validate_read_and_copy_typed(&resolution, user_resolution)) + if (!copy_from_user(&resolution, user_resolution)) return -EFAULT; if (resolution.width > MAX_RESOLUTION_WIDTH || resolution.height > MAX_RESOLUTION_HEIGHT) return -EINVAL; @@ -250,7 +245,8 @@ int BXVGADevice::ioctl(FileDescription&, unsigned request, FlatPtr arg) resolution.pitch = m_framebuffer_pitch; resolution.width = m_framebuffer_width; resolution.height = m_framebuffer_height; - copy_to_user(user_resolution, &resolution); + if (!copy_to_user(user_resolution, &resolution)) + return -EFAULT; return -EINVAL; } #ifdef BXVGA_DEBUG @@ -259,7 +255,8 @@ int BXVGADevice::ioctl(FileDescription&, unsigned request, FlatPtr arg) resolution.pitch = m_framebuffer_pitch; resolution.width = m_framebuffer_width; resolution.height = m_framebuffer_height; - copy_to_user(user_resolution, &resolution); + if (!copy_to_user(user_resolution, &resolution)) + return -EFAULT; return 0; } default: diff --git a/Kernel/Devices/BXVGADevice.h b/Kernel/Devices/BXVGADevice.h index 213fc1617ce85f..6b98db525bdd09 100644 --- a/Kernel/Devices/BXVGADevice.h +++ b/Kernel/Devices/BXVGADevice.h @@ -48,10 +48,10 @@ class BXVGADevice final : public BlockDevice { virtual const char* class_name() const override { return "BXVGA"; } virtual bool can_read(const FileDescription&, size_t) const override { return true; } virtual bool can_write(const FileDescription&, size_t) const override { return true; } - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override { return -EINVAL; } - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override { return -EINVAL; } - virtual bool read_blocks(unsigned, u16, u8*) override { return false; } - virtual bool write_blocks(unsigned, u16, const u8*) override { return false; } + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return -EINVAL; } + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override { return -EINVAL; } + virtual bool read_blocks(unsigned, u16, UserOrKernelBuffer&) override { return false; } + virtual bool write_blocks(unsigned, u16, const UserOrKernelBuffer&) override { return false; } void set_safe_resolution(); diff --git a/Kernel/Devices/BlockDevice.cpp b/Kernel/Devices/BlockDevice.cpp index 2120db0edcf30a..d78388b6109eb7 100644 --- a/Kernel/Devices/BlockDevice.cpp +++ b/Kernel/Devices/BlockDevice.cpp @@ -32,17 +32,17 @@ BlockDevice::~BlockDevice() { } -bool BlockDevice::read_block(unsigned index, u8* buffer) const +bool BlockDevice::read_block(unsigned index, UserOrKernelBuffer& buffer) const { return const_cast(this)->read_blocks(index, 1, buffer); } -bool BlockDevice::write_block(unsigned index, const u8* data) +bool BlockDevice::write_block(unsigned index, const UserOrKernelBuffer& data) { return write_blocks(index, 1, data); } -bool BlockDevice::read_raw(u32 offset, unsigned length, u8* out) const +bool BlockDevice::read_raw(u32 offset, unsigned length, UserOrKernelBuffer& out) const { ASSERT((offset % block_size()) == 0); ASSERT((length % block_size()) == 0); @@ -51,7 +51,7 @@ bool BlockDevice::read_raw(u32 offset, unsigned length, u8* out) const return const_cast(this)->read_blocks(first_block, end_block - first_block, out); } -bool BlockDevice::write_raw(u32 offset, unsigned length, const u8* in) +bool BlockDevice::write_raw(u32 offset, unsigned length, const UserOrKernelBuffer& in) { ASSERT((offset % block_size()) == 0); ASSERT((length % block_size()) == 0); diff --git a/Kernel/Devices/BlockDevice.h b/Kernel/Devices/BlockDevice.h index 042a2cfce1bfd8..69786df3f84693 100644 --- a/Kernel/Devices/BlockDevice.h +++ b/Kernel/Devices/BlockDevice.h @@ -37,13 +37,13 @@ class BlockDevice : public Device { size_t block_size() const { return m_block_size; } virtual bool is_seekable() const override { return true; } - bool read_block(unsigned index, u8*) const; - bool write_block(unsigned index, const u8*); - bool read_raw(u32 offset, unsigned length, u8*) const; - bool write_raw(u32 offset, unsigned length, const u8*); + bool read_block(unsigned index, UserOrKernelBuffer&) const; + bool write_block(unsigned index, const UserOrKernelBuffer&); + bool read_raw(u32 offset, unsigned length, UserOrKernelBuffer&) const; + bool write_raw(u32 offset, unsigned length, const UserOrKernelBuffer&); - virtual bool read_blocks(unsigned index, u16 count, u8*) = 0; - virtual bool write_blocks(unsigned index, u16 count, const u8*) = 0; + virtual bool read_blocks(unsigned index, u16 count, UserOrKernelBuffer&) = 0; + virtual bool write_blocks(unsigned index, u16 count, const UserOrKernelBuffer&) = 0; protected: BlockDevice(unsigned major, unsigned minor, size_t block_size = PAGE_SIZE) diff --git a/Kernel/Devices/DiskPartition.cpp b/Kernel/Devices/DiskPartition.cpp index 75f83d3976fc41..d1ccb90d1bba43 100644 --- a/Kernel/Devices/DiskPartition.cpp +++ b/Kernel/Devices/DiskPartition.cpp @@ -48,7 +48,7 @@ DiskPartition::~DiskPartition() { } -KResultOr DiskPartition::read(FileDescription& fd, size_t offset, u8* outbuf, size_t len) +KResultOr DiskPartition::read(FileDescription& fd, size_t offset, UserOrKernelBuffer& outbuf, size_t len) { unsigned adjust = m_block_offset * block_size(); @@ -70,7 +70,7 @@ bool DiskPartition::can_read(const FileDescription& fd, size_t offset) const return m_device->can_read(fd, offset + adjust); } -KResultOr DiskPartition::write(FileDescription& fd, size_t offset, const u8* inbuf, size_t len) +KResultOr DiskPartition::write(FileDescription& fd, size_t offset, const UserOrKernelBuffer& inbuf, size_t len) { unsigned adjust = m_block_offset * block_size(); @@ -92,7 +92,7 @@ bool DiskPartition::can_write(const FileDescription& fd, size_t offset) const return m_device->can_write(fd, offset + adjust); } -bool DiskPartition::read_blocks(unsigned index, u16 count, u8* out) +bool DiskPartition::read_blocks(unsigned index, u16 count, UserOrKernelBuffer& out) { #ifdef OFFD_DEBUG klog() << "DiskPartition::read_blocks " << index << " (really: " << (m_block_offset + index) << ") count=" << count; @@ -101,7 +101,7 @@ bool DiskPartition::read_blocks(unsigned index, u16 count, u8* out) return m_device->read_blocks(m_block_offset + index, count, out); } -bool DiskPartition::write_blocks(unsigned index, u16 count, const u8* data) +bool DiskPartition::write_blocks(unsigned index, u16 count, const UserOrKernelBuffer& data) { #ifdef OFFD_DEBUG klog() << "DiskPartition::write_blocks " << index << " (really: " << (m_block_offset + index) << ") count=" << count; diff --git a/Kernel/Devices/DiskPartition.h b/Kernel/Devices/DiskPartition.h index 51d3342869a770..e9551b80970810 100644 --- a/Kernel/Devices/DiskPartition.h +++ b/Kernel/Devices/DiskPartition.h @@ -36,13 +36,13 @@ class DiskPartition final : public BlockDevice { static NonnullRefPtr create(BlockDevice&, unsigned block_offset, unsigned block_limit); virtual ~DiskPartition(); - virtual bool read_blocks(unsigned index, u16 count, u8*) override; - virtual bool write_blocks(unsigned index, u16 count, const u8*) override; + virtual bool read_blocks(unsigned index, u16 count, UserOrKernelBuffer&) override; + virtual bool write_blocks(unsigned index, u16 count, const UserOrKernelBuffer&) override; // ^BlockDevice - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_write(const FileDescription&, size_t) const override; private: diff --git a/Kernel/Devices/EBRPartitionTable.cpp b/Kernel/Devices/EBRPartitionTable.cpp index 095fc78ecd9e39..48826636de5f7e 100644 --- a/Kernel/Devices/EBRPartitionTable.cpp +++ b/Kernel/Devices/EBRPartitionTable.cpp @@ -63,7 +63,8 @@ int EBRPartitionTable::index_of_ebr_container() const bool EBRPartitionTable::initialize() { - if (!m_device->read_block(0, m_cached_mbr_header)) { + auto mbr_header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_mbr_header); + if (!m_device->read_block(0, mbr_header_buffer)) { return false; } auto& header = this->header(); @@ -80,7 +81,8 @@ bool EBRPartitionTable::initialize() } auto& ebr_entry = header.entry[m_ebr_container_id - 1]; - if (!m_device->read_block(ebr_entry.offset, m_cached_ebr_header)) { + auto ebr_header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_ebr_header); + if (!m_device->read_block(ebr_entry.offset, ebr_header_buffer)) { return false; } size_t index = 1; @@ -89,7 +91,7 @@ bool EBRPartitionTable::initialize() break; } index++; - if (!m_device->read_block(ebr_extension().next_chained_ebr_extension.offset, m_cached_ebr_header)) { + if (!m_device->read_block(ebr_extension().next_chained_ebr_extension.offset, ebr_header_buffer)) { return false; } } @@ -140,7 +142,8 @@ RefPtr EBRPartitionTable::get_extended_partition(unsigned index) klog() << "EBRPartitionTable::partition: Extended partition, offset 0x" << String::format("%x", ebr_entry.offset) << ", type " << String::format("%x", ebr_entry.type); #endif - if (!m_device->read_block(ebr_entry.offset, m_cached_ebr_header)) { + auto ebr_header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_ebr_header); + if (!m_device->read_block(ebr_entry.offset, ebr_header_buffer)) { return nullptr; } size_t i = 0; @@ -154,7 +157,7 @@ RefPtr EBRPartitionTable::get_extended_partition(unsigned index) } i++; - if (!m_device->read_block(ebr_extension().next_chained_ebr_extension.offset, m_cached_ebr_header)) { + if (!m_device->read_block(ebr_extension().next_chained_ebr_extension.offset, ebr_header_buffer)) { return nullptr; } } diff --git a/Kernel/Devices/FullDevice.cpp b/Kernel/Devices/FullDevice.cpp index bfd2c15a828ae2..0d43d7a9509cab 100644 --- a/Kernel/Devices/FullDevice.cpp +++ b/Kernel/Devices/FullDevice.cpp @@ -46,14 +46,15 @@ bool FullDevice::can_read(const FileDescription&, size_t) const return true; } -KResultOr FullDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr FullDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { ssize_t count = min(static_cast(PAGE_SIZE), size); - memset(buffer, 0, count); + if (!buffer.memset(0, count)) + return KResult(-EFAULT); return count; } -KResultOr FullDevice::write(FileDescription&, size_t, const u8*, size_t size) +KResultOr FullDevice::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t size) { if (size == 0) return 0; diff --git a/Kernel/Devices/FullDevice.h b/Kernel/Devices/FullDevice.h index 3404ce22aaffe1..43070686c16fdc 100644 --- a/Kernel/Devices/FullDevice.h +++ b/Kernel/Devices/FullDevice.h @@ -38,8 +38,8 @@ class FullDevice final : public CharacterDevice { private: // ^CharacterDevice - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; virtual bool can_write(const FileDescription&, size_t) const override { return true; } virtual const char* class_name() const override { return "FullDevice"; } diff --git a/Kernel/Devices/GPTPartitionTable.cpp b/Kernel/Devices/GPTPartitionTable.cpp index e5413a03369c14..f442952cd6fc87 100644 --- a/Kernel/Devices/GPTPartitionTable.cpp +++ b/Kernel/Devices/GPTPartitionTable.cpp @@ -49,7 +49,8 @@ const GPTPartitionHeader& GPTPartitionTable::header() const bool GPTPartitionTable::initialize() { - if (!m_device->read_block(1, m_cached_header)) { + auto header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_header); + if (!m_device->read_block(1, header_buffer)) { return false; } @@ -82,7 +83,8 @@ RefPtr GPTPartitionTable::partition(unsigned index) u8 entries_per_sector = BytesPerSector / header.partition_entry_size; GPTPartitionEntry entries[entries_per_sector]; - this->m_device->read_blocks(lba, 1, (u8*)&entries); + auto entries_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&entries); + this->m_device->read_blocks(lba, 1, entries_buffer); GPTPartitionEntry& entry = entries[((index - 1) % entries_per_sector)]; #ifdef GPT_DEBUG diff --git a/Kernel/Devices/KeyboardDevice.cpp b/Kernel/Devices/KeyboardDevice.cpp index 531c488a9f6b56..9f60268986411f 100644 --- a/Kernel/Devices/KeyboardDevice.cpp +++ b/Kernel/Devices/KeyboardDevice.cpp @@ -369,7 +369,7 @@ bool KeyboardDevice::can_read(const FileDescription&, size_t) const return !m_queue.is_empty(); } -KResultOr KeyboardDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr KeyboardDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { size_t nread = 0; while (nread < size) { @@ -379,13 +379,19 @@ KResultOr KeyboardDevice::read(FileDescription&, size_t, u8* buffer, siz if ((size - nread) < (ssize_t)sizeof(Event)) break; auto event = m_queue.dequeue(); - memcpy(buffer, &event, sizeof(Event)); + ssize_t n = buffer.write_buffered(sizeof(Event), [&](u8* data, size_t data_bytes) { + memcpy(data, &event, sizeof(Event)); + return (ssize_t)data_bytes; + }); + if (n < 0) + return KResult(n); + ASSERT((size_t)n == sizeof(Event)); nread += sizeof(Event); } return nread; } -KResultOr KeyboardDevice::write(FileDescription&, size_t, const u8*, size_t) +KResultOr KeyboardDevice::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) { return 0; } diff --git a/Kernel/Devices/KeyboardDevice.h b/Kernel/Devices/KeyboardDevice.h index 74a53a0da69cc1..1d7f60ab7d54da 100644 --- a/Kernel/Devices/KeyboardDevice.h +++ b/Kernel/Devices/KeyboardDevice.h @@ -57,9 +57,9 @@ class KeyboardDevice final : public IRQHandler const String keymap_name() { return m_character_map.character_map_name(); } // ^CharacterDevice - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_write(const FileDescription&, size_t) const override { return true; } virtual const char* purpose() const override { return class_name(); } diff --git a/Kernel/Devices/MBRPartitionTable.cpp b/Kernel/Devices/MBRPartitionTable.cpp index b836a9b479dddd..a57a5db310dcb4 100644 --- a/Kernel/Devices/MBRPartitionTable.cpp +++ b/Kernel/Devices/MBRPartitionTable.cpp @@ -49,7 +49,8 @@ const MBRPartitionHeader& MBRPartitionTable::header() const bool MBRPartitionTable::initialize() { - if (!m_device->read_block(0, m_cached_header)) { + auto header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_header); + if (!m_device->read_block(0, header_buffer)) { return false; } diff --git a/Kernel/Devices/MBVGADevice.cpp b/Kernel/Devices/MBVGADevice.cpp index f19de93e41d308..84998f14c837fc 100644 --- a/Kernel/Devices/MBVGADevice.cpp +++ b/Kernel/Devices/MBVGADevice.cpp @@ -79,40 +79,36 @@ int MBVGADevice::ioctl(FileDescription&, unsigned request, FlatPtr arg) switch (request) { case FB_IOCTL_GET_SIZE_IN_BYTES: { auto* out = (size_t*)arg; - if (!Process::current()->validate_write_typed(out)) - return -EFAULT; size_t value = framebuffer_size_in_bytes(); - copy_to_user(out, &value); + if (!copy_to_user(out, &value)) + return -EFAULT; return 0; } case FB_IOCTL_GET_BUFFER: { auto* index = (int*)arg; - if (!Process::current()->validate_write_typed(index)) - return -EFAULT; int value = 0; - copy_to_user(index, &value); + if (!copy_to_user(index, &value)) + return -EFAULT; return 0; } case FB_IOCTL_GET_RESOLUTION: { auto* user_resolution = (FBResolution*)arg; - if (!Process::current()->validate_write_typed(user_resolution)) - return -EFAULT; FBResolution resolution; resolution.pitch = m_framebuffer_pitch; resolution.width = m_framebuffer_width; resolution.height = m_framebuffer_height; - copy_to_user(user_resolution, &resolution); + if (!copy_to_user(user_resolution, &resolution)) + return -EFAULT; return 0; } case FB_IOCTL_SET_RESOLUTION: { auto* user_resolution = (FBResolution*)arg; - if (!Process::current()->validate_read_typed(user_resolution) || !Process::current()->validate_write_typed(user_resolution)) - return -EFAULT; FBResolution resolution; resolution.pitch = m_framebuffer_pitch; resolution.width = m_framebuffer_width; resolution.height = m_framebuffer_height; - copy_to_user(user_resolution, &resolution); + if (!copy_to_user(user_resolution, &resolution)) + return -EFAULT; return 0; } default: diff --git a/Kernel/Devices/MBVGADevice.h b/Kernel/Devices/MBVGADevice.h index 8a7d60e54cc451..09dda1f85312de 100644 --- a/Kernel/Devices/MBVGADevice.h +++ b/Kernel/Devices/MBVGADevice.h @@ -47,10 +47,10 @@ class MBVGADevice final : public BlockDevice { virtual const char* class_name() const override { return "MBVGA"; } virtual bool can_read(const FileDescription&, size_t) const override { return true; } virtual bool can_write(const FileDescription&, size_t) const override { return true; } - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override { return -EINVAL; } - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override { return -EINVAL; } - virtual bool read_blocks(unsigned, u16, u8*) override { return false; } - virtual bool write_blocks(unsigned, u16, const u8*) override { return false; } + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return -EINVAL; } + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override { return -EINVAL; } + virtual bool read_blocks(unsigned, u16, UserOrKernelBuffer&) override { return false; } + virtual bool write_blocks(unsigned, u16, const UserOrKernelBuffer&) override { return false; } size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height; } diff --git a/Kernel/Devices/NullDevice.cpp b/Kernel/Devices/NullDevice.cpp index 5c6a5c59c52854..260e8d9aa8b4ae 100644 --- a/Kernel/Devices/NullDevice.cpp +++ b/Kernel/Devices/NullDevice.cpp @@ -56,12 +56,12 @@ bool NullDevice::can_read(const FileDescription&, size_t) const return true; } -KResultOr NullDevice::read(FileDescription&, size_t, u8*, size_t) +KResultOr NullDevice::read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) { return 0; } -KResultOr NullDevice::write(FileDescription&, size_t, const u8*, size_t buffer_size) +KResultOr NullDevice::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t buffer_size) { return min(static_cast(PAGE_SIZE), buffer_size); } diff --git a/Kernel/Devices/NullDevice.h b/Kernel/Devices/NullDevice.h index c7968258f72652..4fcc9c02581f8d 100644 --- a/Kernel/Devices/NullDevice.h +++ b/Kernel/Devices/NullDevice.h @@ -41,8 +41,8 @@ class NullDevice final : public CharacterDevice { private: // ^CharacterDevice - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_write(const FileDescription&, size_t) const override { return true; } virtual bool can_read(const FileDescription&, size_t) const override; virtual const char* class_name() const override { return "NullDevice"; } diff --git a/Kernel/Devices/PATAChannel.cpp b/Kernel/Devices/PATAChannel.cpp index 172816ea1b39f3..cc47ff532c8161 100644 --- a/Kernel/Devices/PATAChannel.cpp +++ b/Kernel/Devices/PATAChannel.cpp @@ -274,11 +274,11 @@ void PATAChannel::detect_disks() } } -bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, u8* outbuf, bool slave_request) +bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuffer& outbuf, bool slave_request) { LOCKER(s_lock()); #ifdef PATA_DEBUG - dbg() << "PATAChannel::ata_read_sectors_with_dma (" << lba << " x" << count << ") -> " << outbuf; + dbg() << "PATAChannel::ata_read_sectors_with_dma (" << lba << " x" << count << ") -> " << outbuf.user_or_kernel_ptr(); #endif prdt().offset = m_dma_buffer_page->paddr(); @@ -335,24 +335,26 @@ bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, u8* outbuf, bool if (m_device_error) return false; - memcpy(outbuf, m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * count); + if (!outbuf.write(m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * count)) + return false; // TODO: -EFAULT // I read somewhere that this may trigger a cache flush so let's do it. m_bus_master_base.offset(2).out(m_bus_master_base.offset(2).in() | 0x6); return true; } -bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf, bool slave_request) +bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const UserOrKernelBuffer& inbuf, bool slave_request) { LOCKER(s_lock()); #ifdef PATA_DEBUG - dbg() << "PATAChannel::ata_write_sectors_with_dma (" << lba << " x" << count << ") <- " << inbuf; + dbg() << "PATAChannel::ata_write_sectors_with_dma (" << lba << " x" << count << ") <- " << inbuf.user_or_kernel_ptr(); #endif prdt().offset = m_dma_buffer_page->paddr(); prdt().size = 512 * count; - memcpy(m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), inbuf, 512 * count); + if (!inbuf.read(m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * count)) + return false; // TODO: -EFAULT ASSERT(prdt().size <= PAGE_SIZE); @@ -406,12 +408,12 @@ bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf return true; } -bool PATAChannel::ata_read_sectors(u32 lba, u16 count, u8* outbuf, bool slave_request) +bool PATAChannel::ata_read_sectors(u32 lba, u16 count, UserOrKernelBuffer& outbuf, bool slave_request) { ASSERT(count <= 256); LOCKER(s_lock()); #ifdef PATA_DEBUG - dbg() << "PATAChannel::ata_read_sectors request (" << count << " sector(s) @ " << lba << " into " << outbuf << ")"; + dbg() << "PATAChannel::ata_read_sectors request (" << count << " sector(s) @ " << lba << " into " << outbuf.user_or_kernel_ptr() << ")"; #endif while (m_io_base.offset(ATA_REG_STATUS).in() & ATA_SR_BSY) @@ -460,14 +462,21 @@ bool PATAChannel::ata_read_sectors(u32 lba, u16 count, u8* outbuf, bool slave_re u8 status = m_control_base.offset(ATA_CTL_ALTSTATUS).in(); ASSERT(!(status & ATA_SR_BSY)); - auto* buffer = (u16*)(outbuf + i * 512); + auto out = outbuf.offset(i * 512); #ifdef PATA_DEBUG - dbg() << "PATAChannel: Retrieving 512 bytes (part " << i << ") (status=" << String::format("%b", status) << "), outbuf=(" << buffer << ")..."; + dbg() << "PATAChannel: Retrieving 512 bytes (part " << i << ") (status=" << String::format("%b", status) << "), outbuf=(" << out.user_or_kernel_ptr() << ")..."; #endif prepare_for_irq(); - for (int i = 0; i < 256; i++) { - buffer[i] = IO::in16(m_io_base.offset(ATA_REG_DATA).get()); + ssize_t nwritten = out.write_buffered<512>(512, [&](u8* buffer, size_t buffer_bytes) { + for (size_t i = 0; i < buffer_bytes; i += sizeof(u16)) + *(u16*)&buffer[i] = IO::in16(m_io_base.offset(ATA_REG_DATA).get()); + return (ssize_t)buffer_bytes; + }); + if (nwritten < 0) { + sti(); + disable_irq(); + return false; // TODO: -EFAULT } } @@ -476,7 +485,7 @@ bool PATAChannel::ata_read_sectors(u32 lba, u16 count, u8* outbuf, bool slave_re return true; } -bool PATAChannel::ata_write_sectors(u32 start_sector, u16 count, const u8* inbuf, bool slave_request) +bool PATAChannel::ata_write_sectors(u32 start_sector, u16 count, const UserOrKernelBuffer& inbuf, bool slave_request) { ASSERT(count <= 256); LOCKER(s_lock()); @@ -515,17 +524,21 @@ bool PATAChannel::ata_write_sectors(u32 start_sector, u16 count, const u8* inbuf u8 status = m_io_base.offset(ATA_REG_STATUS).in(); ASSERT(status & ATA_SR_DRQ); + auto in = inbuf.offset(i * 512); #ifdef PATA_DEBUG - dbg() << "PATAChannel: Writing 512 bytes (part " << i << ") (status=" << String::format("%b", status) << "), inbuf=(" << (inbuf + (512 * i)) << ")..."; + dbg() << "PATAChannel: Writing 512 bytes (part " << i << ") (status=" << String::format("%b", status) << "), inbuf=(" << in.user_or_kernel_ptr() << ")..."; #endif prepare_for_irq(); - auto* buffer = (u16*)(const_cast(inbuf) + i * 512); - for (int i = 0; i < 256; i++) { - IO::out16(m_io_base.offset(ATA_REG_DATA).get(), buffer[i]); - } + ssize_t nread = in.read_buffered<512>(512, [&](const u8* buffer, size_t buffer_bytes) { + for (size_t i = 0; i < buffer_bytes; i += sizeof(u16)) + IO::out16(m_io_base.offset(ATA_REG_DATA).get(), *(const u16*)&buffer[i]); + return (ssize_t)buffer_bytes; + }); wait_for_irq(); status = m_io_base.offset(ATA_REG_STATUS).in(); ASSERT(!(status & ATA_SR_BSY)); + if (nread < 0) + return false; // TODO: -EFAULT } prepare_for_irq(); m_io_base.offset(ATA_REG_COMMAND).out(ATA_CMD_CACHE_FLUSH); diff --git a/Kernel/Devices/PATAChannel.h b/Kernel/Devices/PATAChannel.h index 60ed95d6c1f179..40e331a4e8718b 100644 --- a/Kernel/Devices/PATAChannel.h +++ b/Kernel/Devices/PATAChannel.h @@ -84,10 +84,10 @@ class PATAChannel final : public PCI::Device { void detect_disks(); void wait_for_irq(); - bool ata_read_sectors_with_dma(u32, u16, u8*, bool); - bool ata_write_sectors_with_dma(u32, u16, const u8*, bool); - bool ata_read_sectors(u32, u16, u8*, bool); - bool ata_write_sectors(u32, u16, const u8*, bool); + bool ata_read_sectors_with_dma(u32, u16, UserOrKernelBuffer&, bool); + bool ata_write_sectors_with_dma(u32, u16, const UserOrKernelBuffer&, bool); + bool ata_read_sectors(u32, u16, UserOrKernelBuffer&, bool); + bool ata_write_sectors(u32, u16, const UserOrKernelBuffer&, bool); inline void prepare_for_irq(); diff --git a/Kernel/Devices/PATADiskDevice.cpp b/Kernel/Devices/PATADiskDevice.cpp index 384c0c7bba4ce0..a81f7bb55701cf 100644 --- a/Kernel/Devices/PATADiskDevice.cpp +++ b/Kernel/Devices/PATADiskDevice.cpp @@ -55,19 +55,19 @@ const char* PATADiskDevice::class_name() const return "PATADiskDevice"; } -bool PATADiskDevice::read_blocks(unsigned index, u16 count, u8* out) +bool PATADiskDevice::read_blocks(unsigned index, u16 count, UserOrKernelBuffer& out) { if (!m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource()) return read_sectors_with_dma(index, count, out); return read_sectors(index, count, out); } -bool PATADiskDevice::write_blocks(unsigned index, u16 count, const u8* data) +bool PATADiskDevice::write_blocks(unsigned index, u16 count, const UserOrKernelBuffer& data) { if (!m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource()) return write_sectors_with_dma(index, count, data); for (unsigned i = 0; i < count; ++i) { - if (!write_sectors(index + i, 1, data + i * 512)) + if (!write_sectors(index + i, 1, data.offset(i * 512))) return false; } return true; @@ -80,7 +80,7 @@ void PATADiskDevice::set_drive_geometry(u16 cyls, u16 heads, u16 spt) m_sectors_per_track = spt; } -KResultOr PATADiskDevice::read(FileDescription&, size_t offset, u8* outbuf, size_t len) +KResultOr PATADiskDevice::read(FileDescription&, size_t offset, UserOrKernelBuffer& outbuf, size_t len) { unsigned index = offset / block_size(); u16 whole_blocks = len / block_size(); @@ -107,10 +107,12 @@ KResultOr PATADiskDevice::read(FileDescription&, size_t offset, u8* outb off_t pos = whole_blocks * block_size(); if (remaining > 0) { - auto buf = ByteBuffer::create_uninitialized(block_size()); - if (!read_blocks(index + whole_blocks, 1, buf.data())) + auto data = ByteBuffer::create_uninitialized(block_size()); + auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data.data()); + if (!read_blocks(index + whole_blocks, 1, data_buffer)) return pos; - memcpy(&outbuf[pos], buf.data(), remaining); + if (!outbuf.write(data.data(), pos, remaining)) + return KResult(-EFAULT); } return pos + remaining; @@ -121,7 +123,7 @@ bool PATADiskDevice::can_read(const FileDescription&, size_t offset) const return offset < (m_cylinders * m_heads * m_sectors_per_track * block_size()); } -KResultOr PATADiskDevice::write(FileDescription&, size_t offset, const u8* inbuf, size_t len) +KResultOr PATADiskDevice::write(FileDescription&, size_t offset, const UserOrKernelBuffer& inbuf, size_t len) { unsigned index = offset / block_size(); u16 whole_blocks = len / block_size(); @@ -151,11 +153,13 @@ KResultOr PATADiskDevice::write(FileDescription&, size_t offset, const u // partial write, we have to read the block's content first, modify it, // then write the whole block back to the disk. if (remaining > 0) { - auto buf = ByteBuffer::create_zeroed(block_size()); - if (!read_blocks(index + whole_blocks, 1, buf.data())) + auto data = ByteBuffer::create_zeroed(block_size()); + auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data.data()); + if (!read_blocks(index + whole_blocks, 1, data_buffer)) return pos; - memcpy(buf.data(), &inbuf[pos], remaining); - if (!write_blocks(index + whole_blocks, 1, buf.data())) + if (!inbuf.read(data.data(), pos, remaining)) + return KResult(-EFAULT); + if (!write_blocks(index + whole_blocks, 1, data_buffer)) return pos; } @@ -167,22 +171,22 @@ bool PATADiskDevice::can_write(const FileDescription&, size_t offset) const return offset < (m_cylinders * m_heads * m_sectors_per_track * block_size()); } -bool PATADiskDevice::read_sectors_with_dma(u32 lba, u16 count, u8* outbuf) +bool PATADiskDevice::read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuffer& outbuf) { return m_channel.ata_read_sectors_with_dma(lba, count, outbuf, is_slave()); } -bool PATADiskDevice::read_sectors(u32 start_sector, u16 count, u8* outbuf) +bool PATADiskDevice::read_sectors(u32 start_sector, u16 count, UserOrKernelBuffer& outbuf) { return m_channel.ata_read_sectors(start_sector, count, outbuf, is_slave()); } -bool PATADiskDevice::write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf) +bool PATADiskDevice::write_sectors_with_dma(u32 lba, u16 count, const UserOrKernelBuffer& inbuf) { return m_channel.ata_write_sectors_with_dma(lba, count, inbuf, is_slave()); } -bool PATADiskDevice::write_sectors(u32 start_sector, u16 count, const u8* inbuf) +bool PATADiskDevice::write_sectors(u32 start_sector, u16 count, const UserOrKernelBuffer& inbuf) { return m_channel.ata_write_sectors(start_sector, count, inbuf, is_slave()); } diff --git a/Kernel/Devices/PATADiskDevice.h b/Kernel/Devices/PATADiskDevice.h index d9b7fa94322fa7..eb9377e1bf34f7 100644 --- a/Kernel/Devices/PATADiskDevice.h +++ b/Kernel/Devices/PATADiskDevice.h @@ -55,15 +55,15 @@ class PATADiskDevice final : public BlockDevice { virtual ~PATADiskDevice() override; // ^DiskDevice - virtual bool read_blocks(unsigned index, u16 count, u8*) override; - virtual bool write_blocks(unsigned index, u16 count, const u8*) override; + virtual bool read_blocks(unsigned index, u16 count, UserOrKernelBuffer&) override; + virtual bool write_blocks(unsigned index, u16 count, const UserOrKernelBuffer&) override; void set_drive_geometry(u16, u16, u16); // ^BlockDevice - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_write(const FileDescription&, size_t) const override; protected: @@ -74,10 +74,10 @@ class PATADiskDevice final : public BlockDevice { virtual const char* class_name() const override; bool wait_for_irq(); - bool read_sectors_with_dma(u32 lba, u16 count, u8*); - bool write_sectors_with_dma(u32 lba, u16 count, const u8*); - bool read_sectors(u32 lba, u16 count, u8* buffer); - bool write_sectors(u32 lba, u16 count, const u8* data); + bool read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuffer&); + bool write_sectors_with_dma(u32 lba, u16 count, const UserOrKernelBuffer&); + bool read_sectors(u32 lba, u16 count, UserOrKernelBuffer& buffer); + bool write_sectors(u32 lba, u16 count, const UserOrKernelBuffer& data); bool is_slave() const; Lock m_lock { "IDEDiskDevice" }; diff --git a/Kernel/Devices/PS2MouseDevice.cpp b/Kernel/Devices/PS2MouseDevice.cpp index 65fcc3326410e1..75ae40524eb66a 100644 --- a/Kernel/Devices/PS2MouseDevice.cpp +++ b/Kernel/Devices/PS2MouseDevice.cpp @@ -336,7 +336,7 @@ bool PS2MouseDevice::can_read(const FileDescription&, size_t) const return !m_queue.is_empty(); } -KResultOr PS2MouseDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr PS2MouseDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { ASSERT(size > 0); size_t nread = 0; @@ -349,14 +349,15 @@ KResultOr PS2MouseDevice::read(FileDescription&, size_t, u8* buffer, siz dbg() << "PS2 Mouse Read: Filter packets"; #endif size_t bytes_read_from_packet = min(remaining_space_in_buffer, sizeof(MousePacket)); - memcpy(buffer + nread, &packet, bytes_read_from_packet); + if (!buffer.write(&packet, nread, bytes_read_from_packet)) + return KResult(-EFAULT); nread += bytes_read_from_packet; remaining_space_in_buffer -= bytes_read_from_packet; } return nread; } -KResultOr PS2MouseDevice::write(FileDescription&, size_t, const u8*, size_t) +KResultOr PS2MouseDevice::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) { return 0; } diff --git a/Kernel/Devices/PS2MouseDevice.h b/Kernel/Devices/PS2MouseDevice.h index 10f382f43d5c40..07cfcc7a611b16 100644 --- a/Kernel/Devices/PS2MouseDevice.h +++ b/Kernel/Devices/PS2MouseDevice.h @@ -45,8 +45,8 @@ class PS2MouseDevice final : public IRQHandler // ^CharacterDevice virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_write(const FileDescription&, size_t) const override { return true; } virtual const char* purpose() const override { return class_name(); } diff --git a/Kernel/Devices/RandomDevice.cpp b/Kernel/Devices/RandomDevice.cpp index 5c6b7eb22b637b..fa625d0ecf45b5 100644 --- a/Kernel/Devices/RandomDevice.cpp +++ b/Kernel/Devices/RandomDevice.cpp @@ -43,13 +43,18 @@ bool RandomDevice::can_read(const FileDescription&, size_t) const return true; } -KResultOr RandomDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr RandomDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { - get_good_random_bytes(buffer, size); + bool success = buffer.write_buffered<256>(size, [&](u8* data, size_t data_size) { + get_good_random_bytes(data, data_size); + return (ssize_t)data_size; + }); + if (!success) + return KResult(-EFAULT); return size; } -KResultOr RandomDevice::write(FileDescription&, size_t, const u8*, size_t size) +KResultOr RandomDevice::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t size) { // FIXME: Use input for entropy? I guess that could be a neat feature? return min(static_cast(PAGE_SIZE), size); diff --git a/Kernel/Devices/RandomDevice.h b/Kernel/Devices/RandomDevice.h index d8951297abcb19..c4199341071aba 100644 --- a/Kernel/Devices/RandomDevice.h +++ b/Kernel/Devices/RandomDevice.h @@ -38,8 +38,8 @@ class RandomDevice final : public CharacterDevice { private: // ^CharacterDevice - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; virtual bool can_write(const FileDescription&, size_t) const override { return true; } virtual const char* class_name() const override { return "RandomDevice"; } diff --git a/Kernel/Devices/SB16.cpp b/Kernel/Devices/SB16.cpp index 8d7406f39b6d4a..eb6d15b9936269 100644 --- a/Kernel/Devices/SB16.cpp +++ b/Kernel/Devices/SB16.cpp @@ -177,7 +177,7 @@ bool SB16::can_read(const FileDescription&, size_t) const return false; } -KResultOr SB16::read(FileDescription&, size_t, u8*, size_t) +KResultOr SB16::read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) { return 0; } @@ -231,7 +231,7 @@ void SB16::wait_for_irq() disable_irq(); } -KResultOr SB16::write(FileDescription&, size_t, const u8* data, size_t length) +KResultOr SB16::write(FileDescription&, size_t, const UserOrKernelBuffer& data, size_t length) { if (!m_dma_region) { auto page = MM.allocate_supervisor_physical_page(); @@ -252,7 +252,8 @@ KResultOr SB16::write(FileDescription&, size_t, const u8* data, size_t l const int sample_rate = 44100; set_sample_rate(sample_rate); - memcpy(m_dma_region->vaddr().as_ptr(), data, length); + if (!data.read(m_dma_region->vaddr().as_ptr(), length)) + return KResult(-EFAULT); dma_start(length); // 16-bit single-cycle output. diff --git a/Kernel/Devices/SB16.h b/Kernel/Devices/SB16.h index e0d5ba7ca30652..cc18fde5ef9aa7 100644 --- a/Kernel/Devices/SB16.h +++ b/Kernel/Devices/SB16.h @@ -47,8 +47,8 @@ class SB16 final : public IRQHandler // ^CharacterDevice virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_write(const FileDescription&, size_t) const override { return true; } virtual const char* purpose() const override { return class_name(); } diff --git a/Kernel/Devices/SerialDevice.cpp b/Kernel/Devices/SerialDevice.cpp index e286afb7d4bdf9..a54a8bed3e2a66 100644 --- a/Kernel/Devices/SerialDevice.cpp +++ b/Kernel/Devices/SerialDevice.cpp @@ -45,7 +45,7 @@ bool SerialDevice::can_read(const FileDescription&, size_t) const return (get_line_status() & DataReady) != 0; } -KResultOr SerialDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr SerialDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { if (!size) return 0; @@ -53,9 +53,15 @@ KResultOr SerialDevice::read(FileDescription&, size_t, u8* buffer, size_ if (!(get_line_status() & DataReady)) return 0; - buffer[0] = IO::in8(m_base_addr); + ssize_t nwritten = buffer.write_buffered<128>(size, [&](u8* data, size_t data_size) { + for (size_t i = 0; i < data_size; i++) + data[i] = IO::in8(m_base_addr); + return (ssize_t)data_size; + }); + if (nwritten < 0) + return KResult(nwritten); - return 1; + return size; } bool SerialDevice::can_write(const FileDescription&, size_t) const @@ -63,7 +69,7 @@ bool SerialDevice::can_write(const FileDescription&, size_t) const return (get_line_status() & EmptyTransmitterHoldingRegister) != 0; } -KResultOr SerialDevice::write(FileDescription&, size_t, const u8* buffer, size_t size) +KResultOr SerialDevice::write(FileDescription&, size_t, const UserOrKernelBuffer& buffer, size_t size) { if (!size) return 0; @@ -71,9 +77,14 @@ KResultOr SerialDevice::write(FileDescription&, size_t, const u8* buffer if (!(get_line_status() & EmptyTransmitterHoldingRegister)) return 0; - IO::out8(m_base_addr, buffer[0]); - - return 1; + ssize_t nread = buffer.read_buffered<128>(size, [&](const u8* data, size_t data_size) { + for (size_t i = 0; i < data_size; i++) + IO::out8(m_base_addr, data[i]); + return (ssize_t)data_size; + }); + if (nread < 0) + return KResult(nread); + return (size_t)nread; } void SerialDevice::initialize() diff --git a/Kernel/Devices/SerialDevice.h b/Kernel/Devices/SerialDevice.h index bac77ce9b1ef5c..5c76f5abdfd080 100644 --- a/Kernel/Devices/SerialDevice.h +++ b/Kernel/Devices/SerialDevice.h @@ -43,9 +43,9 @@ class SerialDevice final : public CharacterDevice { // ^CharacterDevice virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual bool can_write(const FileDescription&, size_t) const override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; enum InterruptEnable { LowPowerMode = 0x01 << 5, diff --git a/Kernel/Devices/ZeroDevice.cpp b/Kernel/Devices/ZeroDevice.cpp index e568d9a2152585..ac326434d0c6a8 100644 --- a/Kernel/Devices/ZeroDevice.cpp +++ b/Kernel/Devices/ZeroDevice.cpp @@ -44,14 +44,15 @@ bool ZeroDevice::can_read(const FileDescription&, size_t) const return true; } -KResultOr ZeroDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr ZeroDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { ssize_t count = min(static_cast(PAGE_SIZE), size); - memset(buffer, 0, count); + if (!buffer.memset(0, count)) + return KResult(-EFAULT); return count; } -KResultOr ZeroDevice::write(FileDescription&, size_t, const u8*, size_t size) +KResultOr ZeroDevice::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t size) { return min(static_cast(PAGE_SIZE), size); } diff --git a/Kernel/Devices/ZeroDevice.h b/Kernel/Devices/ZeroDevice.h index 0f3e76e5d64766..f307c6f645ba92 100644 --- a/Kernel/Devices/ZeroDevice.h +++ b/Kernel/Devices/ZeroDevice.h @@ -38,8 +38,8 @@ class ZeroDevice final : public CharacterDevice { private: // ^CharacterDevice - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; virtual bool can_write(const FileDescription&, size_t) const override { return true; } virtual const char* class_name() const override { return "ZeroDevice"; } diff --git a/Kernel/DoubleBuffer.cpp b/Kernel/DoubleBuffer.cpp index 6cc74f5ed77b8b..4b9285e327838b 100644 --- a/Kernel/DoubleBuffer.cpp +++ b/Kernel/DoubleBuffer.cpp @@ -58,7 +58,7 @@ void DoubleBuffer::flip() compute_lockfree_metadata(); } -size_t DoubleBuffer::write(const u8* data, size_t size) +ssize_t DoubleBuffer::write(const UserOrKernelBuffer& data, size_t size) { if (!size) return 0; @@ -68,11 +68,12 @@ size_t DoubleBuffer::write(const u8* data, size_t size) u8* write_ptr = m_write_buffer->data + m_write_buffer->size; m_write_buffer->size += bytes_to_write; compute_lockfree_metadata(); - memcpy(write_ptr, data, bytes_to_write); - return bytes_to_write; + if (!data.read(write_ptr, bytes_to_write)) + return -EFAULT; + return (ssize_t)bytes_to_write; } -size_t DoubleBuffer::read(u8* data, size_t size) +ssize_t DoubleBuffer::read(UserOrKernelBuffer& data, size_t size) { if (!size) return 0; @@ -83,10 +84,11 @@ size_t DoubleBuffer::read(u8* data, size_t size) if (m_read_buffer_index >= m_read_buffer->size) return 0; size_t nread = min(m_read_buffer->size - m_read_buffer_index, size); - memcpy(data, m_read_buffer->data + m_read_buffer_index, nread); + if (!data.write(m_read_buffer->data + m_read_buffer_index, nread)) + return -EFAULT; m_read_buffer_index += nread; compute_lockfree_metadata(); - return nread; + return (ssize_t)nread; } } diff --git a/Kernel/DoubleBuffer.h b/Kernel/DoubleBuffer.h index 02a8a2bf12d23c..b855fb88538e7b 100644 --- a/Kernel/DoubleBuffer.h +++ b/Kernel/DoubleBuffer.h @@ -29,6 +29,7 @@ #include #include #include +#include namespace Kernel { @@ -36,8 +37,17 @@ class DoubleBuffer { public: explicit DoubleBuffer(size_t capacity = 65536); - size_t write(const u8*, size_t); - size_t read(u8*, size_t); + [[nodiscard]] ssize_t write(const UserOrKernelBuffer&, size_t); + [[nodiscard]] ssize_t write(const u8* data, size_t size) + { + return write(UserOrKernelBuffer::for_kernel_buffer(const_cast(data)), size); + } + [[nodiscard]] ssize_t read(UserOrKernelBuffer&, size_t); + [[nodiscard]] ssize_t read(u8* data, size_t size) + { + auto buffer = UserOrKernelBuffer::for_kernel_buffer(data); + return read(buffer, size); + } bool is_empty() const { return m_empty; } diff --git a/Kernel/FileSystem/BlockBasedFileSystem.cpp b/Kernel/FileSystem/BlockBasedFileSystem.cpp index e5880e0897da63..2df34a58bbf9a6 100644 --- a/Kernel/FileSystem/BlockBasedFileSystem.cpp +++ b/Kernel/FileSystem/BlockBasedFileSystem.cpp @@ -119,7 +119,7 @@ BlockBasedFS::~BlockBasedFS() { } -bool BlockBasedFS::write_block(unsigned index, const u8* data, size_t count, size_t offset, bool allow_cache) +int BlockBasedFS::write_block(unsigned index, const UserOrKernelBuffer& data, size_t count, size_t offset, bool allow_cache) { ASSERT(m_logical_block_size); ASSERT(offset + count <= block_size()); @@ -133,9 +133,9 @@ bool BlockBasedFS::write_block(unsigned index, const u8* data, size_t count, siz file_description().seek(base_offset, SEEK_SET); auto nwritten = file_description().write(data, count); if (nwritten.is_error()) - return false; + return -EIO; // TODO: Return error code as-is, could be -EFAULT! ASSERT(nwritten.value() == count); - return true; + return 0; } auto& entry = cache().get(index); @@ -143,15 +143,16 @@ bool BlockBasedFS::write_block(unsigned index, const u8* data, size_t count, siz // Fill the cache first. read_block(index, nullptr, block_size()); } - memcpy(entry.data + offset, data, count); + if (!data.read(entry.data + offset, count)) + return -EFAULT; entry.is_dirty = true; entry.has_data = true; cache().set_dirty(true); - return true; + return 0; } -bool BlockBasedFS::raw_read(unsigned index, u8* buffer) +bool BlockBasedFS::raw_read(unsigned index, UserOrKernelBuffer& buffer) { u32 base_offset = static_cast(index) * static_cast(m_logical_block_size); file_description().seek(base_offset, SEEK_SET); @@ -160,7 +161,7 @@ bool BlockBasedFS::raw_read(unsigned index, u8* buffer) ASSERT(nread.value() == m_logical_block_size); return true; } -bool BlockBasedFS::raw_write(unsigned index, const u8* buffer) +bool BlockBasedFS::raw_write(unsigned index, const UserOrKernelBuffer& buffer) { u32 base_offset = static_cast(index) * static_cast(m_logical_block_size); file_description().seek(base_offset, SEEK_SET); @@ -170,37 +171,39 @@ bool BlockBasedFS::raw_write(unsigned index, const u8* buffer) return true; } -bool BlockBasedFS::raw_read_blocks(unsigned index, size_t count, u8* buffer) +bool BlockBasedFS::raw_read_blocks(unsigned index, size_t count, UserOrKernelBuffer& buffer) { + auto current = buffer; for (unsigned block = index; block < (index + count); block++) { - if (!raw_read(block, buffer)) + if (!raw_read(block, current)) return false; - buffer += logical_block_size(); + current = current.offset(logical_block_size()); } return true; } -bool BlockBasedFS::raw_write_blocks(unsigned index, size_t count, const u8* buffer) +bool BlockBasedFS::raw_write_blocks(unsigned index, size_t count, const UserOrKernelBuffer& buffer) { + auto current = buffer; for (unsigned block = index; block < (index + count); block++) { - if (!raw_write(block, buffer)) + if (!raw_write(block, current)) return false; - buffer += logical_block_size(); + current = current.offset(logical_block_size()); } return true; } -bool BlockBasedFS::write_blocks(unsigned index, unsigned count, const u8* data, bool allow_cache) +int BlockBasedFS::write_blocks(unsigned index, unsigned count, const UserOrKernelBuffer& data, bool allow_cache) { ASSERT(m_logical_block_size); #ifdef BBFS_DEBUG klog() << "BlockBasedFileSystem::write_blocks " << index << " x" << count; #endif for (unsigned i = 0; i < count; ++i) - write_block(index + i, data + i * block_size(), block_size(), 0, allow_cache); - return true; + write_block(index + i, data.offset(i * block_size()), block_size(), 0, allow_cache); + return 0; } -bool BlockBasedFS::read_block(unsigned index, u8* buffer, size_t count, size_t offset, bool allow_cache) const +int BlockBasedFS::read_block(unsigned index, UserOrKernelBuffer* buffer, size_t count, size_t offset, bool allow_cache) const { ASSERT(m_logical_block_size); ASSERT(offset + count <= block_size()); @@ -212,44 +215,45 @@ bool BlockBasedFS::read_block(unsigned index, u8* buffer, size_t count, size_t o const_cast(this)->flush_specific_block_if_needed(index); u32 base_offset = static_cast(index) * static_cast(block_size()) + static_cast(offset); file_description().seek(base_offset, SEEK_SET); - auto nread = file_description().read(buffer, count); + auto nread = file_description().read(*buffer, count); if (nread.is_error()) - return false; + return -EIO; ASSERT(nread.value() == count); - return true; + return 0; } auto& entry = cache().get(index); if (!entry.has_data) { u32 base_offset = static_cast(index) * static_cast(block_size()); file_description().seek(base_offset, SEEK_SET); - auto nread = file_description().read(entry.data, block_size()); + auto entry_data_buffer = UserOrKernelBuffer::for_kernel_buffer(entry.data); + auto nread = file_description().read(entry_data_buffer, block_size()); if (nread.is_error()) - return false; + return -EIO; ASSERT(nread.value() == block_size()); entry.has_data = true; } - if (buffer) - memcpy(buffer, entry.data + offset, count); - return true; + if (buffer && !buffer->write(entry.data + offset, count)) + return -EFAULT; + return 0; } -bool BlockBasedFS::read_blocks(unsigned index, unsigned count, u8* buffer, bool allow_cache) const +int BlockBasedFS::read_blocks(unsigned index, unsigned count, UserOrKernelBuffer& buffer, bool allow_cache) const { ASSERT(m_logical_block_size); if (!count) return false; if (count == 1) - return read_block(index, buffer, block_size(), 0, allow_cache); - u8* out = buffer; - + return read_block(index, &buffer, block_size(), 0, allow_cache); + auto out = buffer; for (unsigned i = 0; i < count; ++i) { - if (!read_block(index + i, out, block_size(), 0, allow_cache)) - return false; - out += block_size(); + auto err = read_block(index + i, &out, block_size(), 0, allow_cache); + if (err < 0) + return err; + out = out.offset(block_size()); } - return true; + return 0; } void BlockBasedFS::flush_specific_block_if_needed(unsigned index) @@ -262,7 +266,8 @@ void BlockBasedFS::flush_specific_block_if_needed(unsigned index) u32 base_offset = static_cast(entry.block_index) * static_cast(block_size()); file_description().seek(base_offset, SEEK_SET); // FIXME: Should this error path be surfaced somehow? - (void)file_description().write(entry.data, block_size()); + auto entry_data_buffer = UserOrKernelBuffer::for_kernel_buffer(entry.data); + (void)file_description().write(entry_data_buffer, block_size()); entry.is_dirty = false; } }); @@ -280,7 +285,8 @@ void BlockBasedFS::flush_writes_impl() u32 base_offset = static_cast(entry.block_index) * static_cast(block_size()); file_description().seek(base_offset, SEEK_SET); // FIXME: Should this error path be surfaced somehow? - (void)file_description().write(entry.data, block_size()); + auto entry_data_buffer = UserOrKernelBuffer::for_kernel_buffer(entry.data); + (void)file_description().write(entry_data_buffer, block_size()); ++count; entry.is_dirty = false; }); diff --git a/Kernel/FileSystem/BlockBasedFileSystem.h b/Kernel/FileSystem/BlockBasedFileSystem.h index b9f7bdf601ca77..fcf096aec3e6c1 100644 --- a/Kernel/FileSystem/BlockBasedFileSystem.h +++ b/Kernel/FileSystem/BlockBasedFileSystem.h @@ -42,17 +42,17 @@ class BlockBasedFS : public FileBackedFS { protected: explicit BlockBasedFS(FileDescription&); - bool read_block(unsigned index, u8* buffer, size_t count, size_t offset = 0, bool allow_cache = true) const; - bool read_blocks(unsigned index, unsigned count, u8* buffer, bool allow_cache = true) const; + int read_block(unsigned index, UserOrKernelBuffer* buffer, size_t count, size_t offset = 0, bool allow_cache = true) const; + int read_blocks(unsigned index, unsigned count, UserOrKernelBuffer& buffer, bool allow_cache = true) const; - bool raw_read(unsigned index, u8* buffer); - bool raw_write(unsigned index, const u8* buffer); + bool raw_read(unsigned index, UserOrKernelBuffer& buffer); + bool raw_write(unsigned index, const UserOrKernelBuffer& buffer); - bool raw_read_blocks(unsigned index, size_t count, u8* buffer); - bool raw_write_blocks(unsigned index, size_t count, const u8* buffer); + bool raw_read_blocks(unsigned index, size_t count, UserOrKernelBuffer& buffer); + bool raw_write_blocks(unsigned index, size_t count, const UserOrKernelBuffer& buffer); - bool write_block(unsigned index, const u8* buffer, size_t count, size_t offset = 0, bool allow_cache = true); - bool write_blocks(unsigned index, unsigned count, const u8*, bool allow_cache = true); + int write_block(unsigned index, const UserOrKernelBuffer& buffer, size_t count, size_t offset = 0, bool allow_cache = true); + int write_blocks(unsigned index, unsigned count, const UserOrKernelBuffer&, bool allow_cache = true); size_t m_logical_block_size { 512 }; diff --git a/Kernel/FileSystem/DevPtsFS.cpp b/Kernel/FileSystem/DevPtsFS.cpp index 01b78c44c0071c..d05437591a8f3c 100644 --- a/Kernel/FileSystem/DevPtsFS.cpp +++ b/Kernel/FileSystem/DevPtsFS.cpp @@ -120,12 +120,12 @@ DevPtsFSInode::~DevPtsFSInode() { } -ssize_t DevPtsFSInode::read_bytes(off_t, ssize_t, u8*, FileDescription*) const +ssize_t DevPtsFSInode::read_bytes(off_t, ssize_t, UserOrKernelBuffer&, FileDescription*) const { ASSERT_NOT_REACHED(); } -ssize_t DevPtsFSInode::write_bytes(off_t, ssize_t, const u8*, FileDescription*) +ssize_t DevPtsFSInode::write_bytes(off_t, ssize_t, const UserOrKernelBuffer&, FileDescription*) { ASSERT_NOT_REACHED(); } diff --git a/Kernel/FileSystem/DevPtsFS.h b/Kernel/FileSystem/DevPtsFS.h index 3c302180fe5792..8ac7571136a481 100644 --- a/Kernel/FileSystem/DevPtsFS.h +++ b/Kernel/FileSystem/DevPtsFS.h @@ -67,12 +67,12 @@ class DevPtsFSInode final : public Inode { DevPtsFSInode(DevPtsFS&, unsigned index, SlavePTY*); // ^Inode - virtual ssize_t read_bytes(off_t, ssize_t, u8* buffer, FileDescription*) const override; + virtual ssize_t read_bytes(off_t, ssize_t, UserOrKernelBuffer& buffer, FileDescription*) const override; virtual InodeMetadata metadata() const override; virtual KResult traverse_as_directory(Function) const override; virtual RefPtr lookup(StringView name) override; virtual void flush_metadata() override; - virtual ssize_t write_bytes(off_t, ssize_t, const u8* buffer, FileDescription*) override; + virtual ssize_t write_bytes(off_t, ssize_t, const UserOrKernelBuffer& buffer, FileDescription*) override; virtual KResultOr> create_child(const String& name, mode_t, dev_t, uid_t, gid_t) override; virtual KResult add_child(Inode&, const StringView& name, mode_t) override; virtual KResult remove_child(const StringView& name) override; diff --git a/Kernel/FileSystem/Ext2FileSystem.cpp b/Kernel/FileSystem/Ext2FileSystem.cpp index cbc9a93968bf2c..52b3037c685eff 100644 --- a/Kernel/FileSystem/Ext2FileSystem.cpp +++ b/Kernel/FileSystem/Ext2FileSystem.cpp @@ -88,7 +88,8 @@ bool Ext2FS::flush_super_block() { LOCKER(m_lock); ASSERT((sizeof(ext2_super_block) % logical_block_size()) == 0); - bool success = raw_write_blocks(2, (sizeof(ext2_super_block) / logical_block_size()), (const u8*)&m_super_block); + auto super_block_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&m_super_block); + bool success = raw_write_blocks(2, (sizeof(ext2_super_block) / logical_block_size()), super_block_buffer); ASSERT(success); return true; } @@ -104,7 +105,8 @@ bool Ext2FS::initialize() { LOCKER(m_lock); ASSERT((sizeof(ext2_super_block) % logical_block_size()) == 0); - bool success = raw_read_blocks(2, (sizeof(ext2_super_block) / logical_block_size()), (u8*)&m_super_block); + auto super_block_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&m_super_block); + bool success = raw_read_blocks(2, (sizeof(ext2_super_block) / logical_block_size()), super_block_buffer); ASSERT(success); auto& super_block = this->super_block(); @@ -139,7 +141,8 @@ bool Ext2FS::initialize() unsigned blocks_to_read = ceil_div(m_block_group_count * sizeof(ext2_group_desc), block_size()); BlockIndex first_block_of_bgdt = block_size() == 1024 ? 2 : 1; m_cached_group_descriptor_table = KBuffer::create_with_size(block_size() * blocks_to_read, Region::Access::Read | Region::Access::Write, "Ext2FS: Block group descriptors"); - read_blocks(first_block_of_bgdt, blocks_to_read, m_cached_group_descriptor_table.value().data()); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_group_descriptor_table.value().data()); + read_blocks(first_block_of_bgdt, blocks_to_read, buffer); #ifdef EXT2_DEBUG for (unsigned i = 1; i <= m_block_group_count; ++i) { @@ -291,8 +294,9 @@ bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2in --remaining_blocks; } stream.fill_to_end(0); - bool success = write_block(e2inode.i_block[EXT2_IND_BLOCK], block_contents.data(), block_size()); - ASSERT(success); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(block_contents.data()); + int err = write_block(e2inode.i_block[EXT2_IND_BLOCK], buffer, block_size()); + ASSERT(err >= 0); } if (!remaining_blocks) @@ -329,7 +333,8 @@ bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2in memset(dind_block_contents.data(), 0, dind_block_contents.size()); dind_block_dirty = true; } else { - read_block(e2inode.i_block[EXT2_DIND_BLOCK], dind_block_contents.data(), block_size()); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(dind_block_contents.data()); + read_block(e2inode.i_block[EXT2_DIND_BLOCK], &buffer, block_size()); } auto* dind_block_as_pointers = (unsigned*)dind_block_contents.data(); @@ -351,7 +356,8 @@ bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2in memset(ind_block_contents.data(), 0, dind_block_contents.size()); ind_block_dirty = true; } else { - read_block(indirect_block_index, ind_block_contents.data(), block_size()); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(ind_block_contents.data()); + read_block(indirect_block_index, &buffer, block_size()); } auto* ind_block_as_pointers = (unsigned*)ind_block_contents.data(); @@ -376,8 +382,9 @@ bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2in } if (ind_block_dirty) { - bool success = write_block(indirect_block_index, ind_block_contents.data(), block_size()); - ASSERT(success); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(ind_block_contents.data()); + int err = write_block(indirect_block_index, buffer, block_size()); + ASSERT(err >= 0); } } for (unsigned i = indirect_block_count; i < entries_per_block; ++i) { @@ -388,8 +395,9 @@ bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2in } if (dind_block_dirty) { - bool success = write_block(e2inode.i_block[EXT2_DIND_BLOCK], dind_block_contents.data(), block_size()); - ASSERT(success); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(dind_block_contents.data()); + int err = write_block(e2inode.i_block[EXT2_DIND_BLOCK], buffer, block_size()); + ASSERT(err >= 0); } } @@ -462,7 +470,8 @@ Vector Ext2FS::block_list_for_inode_impl(const ext2_inode& e unsigned count = min(blocks_remaining, entries_per_block); size_t read_size = count * sizeof(__u32); auto array_block = ByteBuffer::create_uninitialized(read_size); - read_block(array_block_index, array_block.data(), read_size, 0); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(array_block.data()); + read_block(array_block_index, &buffer, read_size, 0); ASSERT(array_block); auto* array = reinterpret_cast(array_block.data()); for (BlockIndex i = 0; i < count; ++i) @@ -532,7 +541,8 @@ void Ext2FS::flush_block_group_descriptor_table() LOCKER(m_lock); unsigned blocks_to_write = ceil_div(m_block_group_count * sizeof(ext2_group_desc), block_size()); unsigned first_block_of_bgdt = block_size() == 1024 ? 2 : 1; - write_blocks(first_block_of_bgdt, blocks_to_write, (const u8*)block_group_descriptors()); + auto buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)block_group_descriptors()); + write_blocks(first_block_of_bgdt, blocks_to_write, buffer); } void Ext2FS::flush_writes() @@ -548,7 +558,8 @@ void Ext2FS::flush_writes() } for (auto& cached_bitmap : m_cached_bitmaps) { if (cached_bitmap->dirty) { - write_block(cached_bitmap->bitmap_block_index, cached_bitmap->buffer.data(), block_size()); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(cached_bitmap->buffer.data()); + write_block(cached_bitmap->bitmap_block_index, buffer, block_size()); cached_bitmap->dirty = false; #ifdef EXT2_DEBUG dbg() << "Flushed bitmap block " << cached_bitmap->bitmap_block_index; @@ -653,12 +664,13 @@ RefPtr Ext2FS::get_inode(InodeIdentifier inode) const return {}; auto new_inode = adopt(*new Ext2FSInode(const_cast(*this), inode.index())); - read_block(block_index, reinterpret_cast(&new_inode->m_raw_inode), sizeof(ext2_inode), offset); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast(&new_inode->m_raw_inode)); + read_block(block_index, &buffer, sizeof(ext2_inode), offset); m_inode_cache.set(inode.index(), new_inode); return new_inode; } -ssize_t Ext2FSInode::read_bytes(off_t offset, ssize_t count, u8* buffer, FileDescription* description) const +ssize_t Ext2FSInode::read_bytes(off_t offset, ssize_t count, UserOrKernelBuffer& buffer, FileDescription* description) const { Locker inode_locker(m_lock); ASSERT(offset >= 0); @@ -670,7 +682,8 @@ ssize_t Ext2FSInode::read_bytes(off_t offset, ssize_t count, u8* buffer, FileDes if (is_symlink() && size() < max_inline_symlink_length) { ASSERT(offset == 0); ssize_t nread = min((off_t)size() - offset, static_cast(count)); - memcpy(buffer, ((const u8*)m_raw_inode.i_block) + offset, (size_t)nread); + if (!buffer.write(((const u8*)m_raw_inode.i_block) + offset, (size_t)nread)) + return -EFAULT; return nread; } @@ -697,10 +710,9 @@ ssize_t Ext2FSInode::read_bytes(off_t offset, ssize_t count, u8* buffer, FileDes ssize_t nread = 0; size_t remaining_count = min((off_t)count, (off_t)size() - offset); - u8* out = buffer; #ifdef EXT2_DEBUG - dbg() << "Ext2FS: Reading up to " << count << " bytes " << offset << " bytes into inode " << identifier() << " to " << (const void*)buffer; + dbg() << "Ext2FS: Reading up to " << count << " bytes " << offset << " bytes into inode " << identifier() << " to " << buffer.user_or_kernel_ptr(); #endif for (size_t bi = first_block_logical_index; remaining_count && bi <= last_block_logical_index; ++bi) { @@ -708,14 +720,14 @@ ssize_t Ext2FSInode::read_bytes(off_t offset, ssize_t count, u8* buffer, FileDes ASSERT(block_index); size_t offset_into_block = (bi == first_block_logical_index) ? offset_into_first_block : 0; size_t num_bytes_to_copy = min(block_size - offset_into_block, remaining_count); - bool success = fs().read_block(block_index, out, num_bytes_to_copy, offset_into_block, allow_cache); - if (!success) { + auto buffer_offset = buffer.offset(nread); + int err = fs().read_block(block_index, &buffer_offset, num_bytes_to_copy, offset_into_block, allow_cache); + if (err < 0) { klog() << "ext2fs: read_bytes: read_block(" << block_index << ") failed (lbi: " << bi << ")"; - return -EIO; + return err; } remaining_count -= num_bytes_to_copy; nread += num_bytes_to_copy; - out += num_bytes_to_copy; } return nread; @@ -760,9 +772,9 @@ KResult Ext2FSInode::resize(u64 new_size) } } - bool success = fs().write_block_list_for_inode(index(), m_raw_inode, block_list); - if (!success) - return KResult(-EIO); + int err = fs().write_block_list_for_inode(index(), m_raw_inode, block_list); + if (err < 0) + return KResult(err); m_raw_inode.i_size = new_size; set_metadata_dirty(true); @@ -771,7 +783,7 @@ KResult Ext2FSInode::resize(u64 new_size) return KSuccess; } -ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const u8* data, FileDescription* description) +ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const UserOrKernelBuffer& data, FileDescription* description) { ASSERT(offset >= 0); ASSERT(count >= 0); @@ -787,9 +799,10 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const u8* data, Fi ASSERT(offset == 0); if (max((size_t)(offset + count), (size_t)m_raw_inode.i_size) < max_inline_symlink_length) { #ifdef EXT2_DEBUG - dbg() << "Ext2FS: write_bytes poking into i_block array for inline symlink '" << StringView(data, count) << " ' (" << count << " bytes)"; + dbg() << "Ext2FS: write_bytes poking into i_block array for inline symlink '" << data.copy_into_string(count) << " ' (" << count << " bytes)"; #endif - memcpy(((u8*)m_raw_inode.i_block) + offset, data, (size_t)count); + if (!data.read(((u8*)m_raw_inode.i_block) + offset, (size_t)count)) + return -EFAULT; if ((size_t)(offset + count) > (size_t)m_raw_inode.i_size) m_raw_inode.i_size = offset + count; set_metadata_dirty(true); @@ -824,10 +837,9 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const u8* data, Fi ssize_t nwritten = 0; size_t remaining_count = min((off_t)count, (off_t)new_size - offset); - const u8* in = data; #ifdef EXT2_DEBUG - dbg() << "Ext2FS: Writing " << count << " bytes " << offset << " bytes into inode " << identifier() << " from " << (const void*)data; + dbg() << "Ext2FS: Writing " << count << " bytes " << offset << " bytes into inode " << identifier() << " from " << data.user_or_kernel_ptr(); #endif for (size_t bi = first_block_logical_index; remaining_count && bi <= last_block_logical_index; ++bi) { @@ -836,15 +848,14 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const u8* data, Fi #ifdef EXT2_DEBUG dbg() << "Ext2FS: Writing block " << m_block_list[bi] << " (offset_into_block: " << offset_into_block << ")"; #endif - bool success = fs().write_block(m_block_list[bi], in, num_bytes_to_copy, offset_into_block, allow_cache); - if (!success) { + int err = fs().write_block(m_block_list[bi], data.offset(nwritten), num_bytes_to_copy, offset_into_block, allow_cache); + if (err < 0) { dbg() << "Ext2FS: write_block(" << m_block_list[bi] << ") failed (bi: " << bi << ")"; ASSERT_NOT_REACHED(); - return -EIO; + return err; } remaining_count -= num_bytes_to_copy; nwritten += num_bytes_to_copy; - in += num_bytes_to_copy; } #ifdef EXT2_DEBUG @@ -958,7 +969,8 @@ bool Ext2FSInode::write_directory(const Vector& entries) stream.fill_to_end(0); - ssize_t nwritten = write_bytes(0, directory_data.size(), directory_data.data(), nullptr); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(directory_data.data()); + ssize_t nwritten = write_bytes(0, directory_data.size(), buffer, nullptr); if (nwritten < 0) return false; set_metadata_dirty(true); @@ -1087,7 +1099,8 @@ bool Ext2FS::write_ext2_inode(unsigned inode, const ext2_inode& e2inode) unsigned offset; if (!find_block_containing_inode(inode, block_index, offset)) return false; - return write_block(block_index, reinterpret_cast(&e2inode), inode_size(), offset); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast((const u8*)&e2inode)); + return write_block(block_index, buffer, inode_size(), offset) >= 0; } Vector Ext2FS::allocate_blocks(GroupIndex preferred_group_index, size_t count) @@ -1314,8 +1327,9 @@ Ext2FS::CachedBitmap& Ext2FS::get_bitmap_block(BlockIndex bitmap_block_index) } auto block = KBuffer::create_with_size(block_size(), Region::Access::Read | Region::Access::Write, "Ext2FS: Cached bitmap block"); - bool success = read_block(bitmap_block_index, block.data(), block_size()); - ASSERT(success); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(block.data()); + int err = read_block(bitmap_block_index, &buffer, block_size()); + ASSERT(err >= 0); m_cached_bitmaps.append(make(bitmap_block_index, move(block))); return *m_cached_bitmaps.last(); } diff --git a/Kernel/FileSystem/Ext2FileSystem.h b/Kernel/FileSystem/Ext2FileSystem.h index bdaa3255ca7558..7d28b84ff2733a 100644 --- a/Kernel/FileSystem/Ext2FileSystem.h +++ b/Kernel/FileSystem/Ext2FileSystem.h @@ -58,12 +58,12 @@ class Ext2FSInode final : public Inode { private: // ^Inode - virtual ssize_t read_bytes(off_t, ssize_t, u8* buffer, FileDescription*) const override; + virtual ssize_t read_bytes(off_t, ssize_t, UserOrKernelBuffer& buffer, FileDescription*) const override; virtual InodeMetadata metadata() const override; virtual KResult traverse_as_directory(Function) const override; virtual RefPtr lookup(StringView name) override; virtual void flush_metadata() override; - virtual ssize_t write_bytes(off_t, ssize_t, const u8* data, FileDescription*) override; + virtual ssize_t write_bytes(off_t, ssize_t, const UserOrKernelBuffer& data, FileDescription*) override; virtual KResultOr> create_child(const String& name, mode_t, dev_t, uid_t, gid_t) override; virtual KResult add_child(Inode& child, const StringView& name, mode_t) override; virtual KResult remove_child(const StringView& name) override; diff --git a/Kernel/FileSystem/FIFO.cpp b/Kernel/FileSystem/FIFO.cpp index 3f9c1e33a12d2f..2aa6c55cd94959 100644 --- a/Kernel/FileSystem/FIFO.cpp +++ b/Kernel/FileSystem/FIFO.cpp @@ -145,14 +145,14 @@ bool FIFO::can_write(const FileDescription&, size_t) const return m_buffer.space_for_writing() || !m_readers; } -KResultOr FIFO::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr FIFO::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { if (!m_writers && m_buffer.is_empty()) return 0; return m_buffer.read(buffer, size); } -KResultOr FIFO::write(FileDescription&, size_t, const u8* buffer, size_t size) +KResultOr FIFO::write(FileDescription&, size_t, const UserOrKernelBuffer& buffer, size_t size) { if (!m_readers) { Thread::current()->send_signal(SIGPIPE, Process::current()); diff --git a/Kernel/FileSystem/FIFO.h b/Kernel/FileSystem/FIFO.h index 81b04597f26d0f..8e3a1460123719 100644 --- a/Kernel/FileSystem/FIFO.h +++ b/Kernel/FileSystem/FIFO.h @@ -57,8 +57,8 @@ class FIFO final : public File { private: // ^File - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual KResult stat(::stat&) const override; virtual bool can_read(const FileDescription&, size_t) const override; virtual bool can_write(const FileDescription&, size_t) const override; diff --git a/Kernel/FileSystem/File.h b/Kernel/FileSystem/File.h index 150f04f518870a..b2400b803b98dc 100644 --- a/Kernel/FileSystem/File.h +++ b/Kernel/FileSystem/File.h @@ -34,6 +34,7 @@ #include #include #include +#include #include namespace Kernel { @@ -77,8 +78,8 @@ class File virtual bool can_read(const FileDescription&, size_t) const = 0; virtual bool can_write(const FileDescription&, size_t) const = 0; - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) = 0; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) = 0; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) = 0; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) = 0; virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg); virtual KResultOr mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot, bool shared); virtual KResult stat(::stat&) const { return KResult(-EBADF); } diff --git a/Kernel/FileSystem/FileDescription.cpp b/Kernel/FileSystem/FileDescription.cpp index 80153e62255fa9..8c88dfc816de1a 100644 --- a/Kernel/FileSystem/FileDescription.cpp +++ b/Kernel/FileSystem/FileDescription.cpp @@ -116,7 +116,7 @@ off_t FileDescription::seek(off_t offset, int whence) return m_current_offset; } -KResultOr FileDescription::read(u8* buffer, size_t count) +KResultOr FileDescription::read(UserOrKernelBuffer& buffer, size_t count) { LOCKER(m_lock); Checked new_offset = m_current_offset; @@ -130,7 +130,7 @@ KResultOr FileDescription::read(u8* buffer, size_t count) return nread_or_error; } -KResultOr FileDescription::write(const u8* data, size_t size) +KResultOr FileDescription::write(const UserOrKernelBuffer& data, size_t size) { LOCKER(m_lock); Checked new_offset = m_current_offset; @@ -162,7 +162,7 @@ KResultOr FileDescription::read_entire_file() return m_inode->read_entire(this); } -ssize_t FileDescription::get_dir_entries(u8* buffer, ssize_t size) +ssize_t FileDescription::get_dir_entries(UserOrKernelBuffer& buffer, ssize_t size) { LOCKER(m_lock, Lock::Mode::Shared); if (!is_directory()) @@ -195,7 +195,8 @@ ssize_t FileDescription::get_dir_entries(u8* buffer, ssize_t size) if (static_cast(size) < temp_buffer.size()) return -EINVAL; - copy_to_user(buffer, temp_buffer.data(), temp_buffer.size()); + if (!buffer.write(temp_buffer.data(), temp_buffer.size())) + return -EFAULT; return stream.offset(); } diff --git a/Kernel/FileSystem/FileDescription.h b/Kernel/FileSystem/FileDescription.h index 3427ad1e1852ef..60d85db945aab0 100644 --- a/Kernel/FileSystem/FileDescription.h +++ b/Kernel/FileSystem/FileDescription.h @@ -60,8 +60,8 @@ class FileDescription : public RefCounted { KResult close(); off_t seek(off_t, int whence); - KResultOr read(u8*, size_t); - KResultOr write(const u8* data, size_t); + KResultOr read(UserOrKernelBuffer&, size_t); + KResultOr write(const UserOrKernelBuffer& data, size_t); KResult stat(::stat&); KResult chmod(mode_t); @@ -69,7 +69,7 @@ class FileDescription : public RefCounted { bool can_read() const; bool can_write() const; - ssize_t get_dir_entries(u8* buffer, ssize_t); + ssize_t get_dir_entries(UserOrKernelBuffer& buffer, ssize_t); KResultOr read_entire_file(); diff --git a/Kernel/FileSystem/FileSystem.h b/Kernel/FileSystem/FileSystem.h index 19fea4bafd25d9..99a8131ca41f6d 100644 --- a/Kernel/FileSystem/FileSystem.h +++ b/Kernel/FileSystem/FileSystem.h @@ -34,6 +34,7 @@ #include #include #include +#include namespace Kernel { diff --git a/Kernel/FileSystem/Inode.cpp b/Kernel/FileSystem/Inode.cpp index e78f87bf5989fb..b0e91bafc5e373 100644 --- a/Kernel/FileSystem/Inode.cpp +++ b/Kernel/FileSystem/Inode.cpp @@ -73,7 +73,10 @@ KResultOr Inode::read_entire(FileDescription* descriptor) const u8 buffer[4096]; off_t offset = 0; for (;;) { - nread = read_bytes(offset, sizeof(buffer), buffer, descriptor); + auto buf = UserOrKernelBuffer::for_kernel_buffer(buffer); + nread = read_bytes(offset, sizeof(buffer), buf, descriptor); + if (nread < 0) + return KResult(nread); ASSERT(nread <= (ssize_t)sizeof(buffer)); if (nread <= 0) break; @@ -124,7 +127,7 @@ void Inode::will_be_destroyed() flush_metadata(); } -void Inode::inode_contents_changed(off_t offset, ssize_t size, const u8* data) +void Inode::inode_contents_changed(off_t offset, ssize_t size, const UserOrKernelBuffer& data) { if (m_shared_vmobject) m_shared_vmobject->inode_contents_changed({}, offset, size, data); diff --git a/Kernel/FileSystem/Inode.h b/Kernel/FileSystem/Inode.h index f9ed0db158efea..d506ab0d186353 100644 --- a/Kernel/FileSystem/Inode.h +++ b/Kernel/FileSystem/Inode.h @@ -69,10 +69,10 @@ class Inode : public RefCounted KResultOr read_entire(FileDescription* = nullptr) const; - virtual ssize_t read_bytes(off_t, ssize_t, u8* buffer, FileDescription*) const = 0; + virtual ssize_t read_bytes(off_t, ssize_t, UserOrKernelBuffer& buffer, FileDescription*) const = 0; virtual KResult traverse_as_directory(Function) const = 0; virtual RefPtr lookup(StringView name) = 0; - virtual ssize_t write_bytes(off_t, ssize_t, const u8* data, FileDescription*) = 0; + virtual ssize_t write_bytes(off_t, ssize_t, const UserOrKernelBuffer& data, FileDescription*) = 0; virtual KResultOr> create_child(const String& name, mode_t, dev_t, uid_t, gid_t) = 0; virtual KResult add_child(Inode&, const StringView& name, mode_t) = 0; virtual KResult remove_child(const StringView& name) = 0; @@ -122,7 +122,7 @@ class Inode : public RefCounted protected: Inode(FS& fs, unsigned index); void set_metadata_dirty(bool); - void inode_contents_changed(off_t, ssize_t, const u8*); + void inode_contents_changed(off_t, ssize_t, const UserOrKernelBuffer&); void inode_size_changed(size_t old_size, size_t new_size); KResult prepare_to_write_data(); diff --git a/Kernel/FileSystem/InodeFile.cpp b/Kernel/FileSystem/InodeFile.cpp index c2657fe9699247..bc4d24395721f2 100644 --- a/Kernel/FileSystem/InodeFile.cpp +++ b/Kernel/FileSystem/InodeFile.cpp @@ -44,7 +44,7 @@ InodeFile::~InodeFile() { } -KResultOr InodeFile::read(FileDescription& description, size_t offset, u8* buffer, size_t count) +KResultOr InodeFile::read(FileDescription& description, size_t offset, UserOrKernelBuffer& buffer, size_t count) { ssize_t nread = m_inode->read_bytes(offset, count, buffer, &description); if (nread > 0) @@ -54,7 +54,7 @@ KResultOr InodeFile::read(FileDescription& description, size_t offset, u return nread; } -KResultOr InodeFile::write(FileDescription& description, size_t offset, const u8* data, size_t count) +KResultOr InodeFile::write(FileDescription& description, size_t offset, const UserOrKernelBuffer& data, size_t count) { ssize_t nwritten = m_inode->write_bytes(offset, count, data, &description); if (nwritten > 0) { diff --git a/Kernel/FileSystem/InodeFile.h b/Kernel/FileSystem/InodeFile.h index ab64201b3b2ee5..e051476b2b93bd 100644 --- a/Kernel/FileSystem/InodeFile.h +++ b/Kernel/FileSystem/InodeFile.h @@ -47,8 +47,8 @@ class InodeFile final : public File { virtual bool can_read(const FileDescription&, size_t) const override { return true; } virtual bool can_write(const FileDescription&, size_t) const override { return true; } - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual KResultOr mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot, bool shared) override; virtual String absolute_path(const FileDescription&) const override; diff --git a/Kernel/FileSystem/InodeWatcher.cpp b/Kernel/FileSystem/InodeWatcher.cpp index a4612a588d06e1..6e76cc69b6a1ad 100644 --- a/Kernel/FileSystem/InodeWatcher.cpp +++ b/Kernel/FileSystem/InodeWatcher.cpp @@ -57,7 +57,7 @@ bool InodeWatcher::can_write(const FileDescription&, size_t) const return true; } -KResultOr InodeWatcher::read(FileDescription&, size_t, u8* buffer, size_t buffer_size) +KResultOr InodeWatcher::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t buffer_size) { LOCKER(m_lock); ASSERT(!m_queue.is_empty() || !m_inode); @@ -68,11 +68,17 @@ KResultOr InodeWatcher::read(FileDescription&, size_t, u8* buffer, size_ // FIXME: What should we do if the output buffer is too small? ASSERT(buffer_size >= (int)sizeof(Event)); auto event = m_queue.dequeue(); - memcpy(buffer, &event, sizeof(event)); + ssize_t nwritten = buffer.write_buffered(sizeof(event), [&](u8* data, size_t data_bytes) { + memcpy(data, &event, sizeof(event)); + return (ssize_t)data_bytes; + }); + if (nwritten < 0) + return KResult(nwritten); + ASSERT((size_t)nwritten == sizeof(event)); return sizeof(event); } -KResultOr InodeWatcher::write(FileDescription&, size_t, const u8*, size_t) +KResultOr InodeWatcher::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) { return KResult(-EIO); } diff --git a/Kernel/FileSystem/InodeWatcher.h b/Kernel/FileSystem/InodeWatcher.h index 31a094b88f5818..cd371e2f1aa8ab 100644 --- a/Kernel/FileSystem/InodeWatcher.h +++ b/Kernel/FileSystem/InodeWatcher.h @@ -55,8 +55,8 @@ class InodeWatcher final : public File { virtual bool can_read(const FileDescription&, size_t) const override; virtual bool can_write(const FileDescription&, size_t) const override; - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual String absolute_path(const FileDescription&) const override; virtual const char* class_name() const override { return "InodeWatcher"; }; diff --git a/Kernel/FileSystem/Plan9FileSystem.cpp b/Kernel/FileSystem/Plan9FileSystem.cpp index 952a176066c106..975883df677d91 100644 --- a/Kernel/FileSystem/Plan9FileSystem.cpp +++ b/Kernel/FileSystem/Plan9FileSystem.cpp @@ -426,7 +426,8 @@ KResult Plan9FS::post_message(Message& message) if (Thread::current()->block(nullptr, description).was_interrupted()) return KResult(-EINTR); } - auto nwritten_or_error = description.write(data, size); + auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast(data)); + auto nwritten_or_error = description.write(data_buffer, size); if (nwritten_or_error.is_error()) return nwritten_or_error.error(); auto nwritten = nwritten_or_error.value(); @@ -445,7 +446,8 @@ KResult Plan9FS::do_read(u8* data, size_t size) if (Thread::current()->block(nullptr, description).was_interrupted()) return KResult(-EINTR); } - auto nread_or_error = description.read(data, size); + auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data); + auto nread_or_error = description.read(data_buffer, size); if (nread_or_error.is_error()) return nread_or_error.error(); auto nread = nread_or_error.value(); @@ -677,7 +679,7 @@ KResult Plan9FSInode::ensure_open_for_mode(int mode) } } -ssize_t Plan9FSInode::read_bytes(off_t offset, ssize_t size, u8* buffer, FileDescription*) const +ssize_t Plan9FSInode::read_bytes(off_t offset, ssize_t size, UserOrKernelBuffer& buffer, FileDescription*) const { auto result = const_cast(*this).ensure_open_for_mode(O_RDONLY); if (result.is_error()) @@ -710,12 +712,13 @@ ssize_t Plan9FSInode::read_bytes(off_t offset, ssize_t size, u8* buffer, FileDes // Guard against the server returning more data than requested. size_t nread = min(data.length(), (size_t)size); - memcpy(buffer, data.characters_without_null_termination(), nread); + if (!buffer.write(data.characters_without_null_termination(), nread)) + return -EFAULT; return nread; } -ssize_t Plan9FSInode::write_bytes(off_t offset, ssize_t size, const u8* data, FileDescription*) +ssize_t Plan9FSInode::write_bytes(off_t offset, ssize_t size, const UserOrKernelBuffer& data, FileDescription*) { auto result = ensure_open_for_mode(O_WRONLY); if (result.is_error()) @@ -723,9 +726,13 @@ ssize_t Plan9FSInode::write_bytes(off_t offset, ssize_t size, const u8* data, Fi size = fs().adjust_buffer_size(size); + auto data_copy = data.copy_into_string(size); // FIXME: this seems ugly + if (data_copy.is_null()) + return -EFAULT; + Plan9FS::Message message { fs(), Plan9FS::Message::Type::Twrite }; message << fid() << (u64)offset; - message.append_data({ data, (size_t)size }); + message.append_data(data_copy); result = fs().post_message_and_wait_for_a_reply(message); if (result.is_error()) return result.error(); diff --git a/Kernel/FileSystem/Plan9FileSystem.h b/Kernel/FileSystem/Plan9FileSystem.h index 77af39e47ae649..5851cbe8dd2aa8 100644 --- a/Kernel/FileSystem/Plan9FileSystem.h +++ b/Kernel/FileSystem/Plan9FileSystem.h @@ -124,8 +124,8 @@ class Plan9FSInode final : public Inode { // ^Inode virtual InodeMetadata metadata() const override; virtual void flush_metadata() override; - virtual ssize_t read_bytes(off_t, ssize_t, u8* buffer, FileDescription*) const override; - virtual ssize_t write_bytes(off_t, ssize_t, const u8* data, FileDescription*) override; + virtual ssize_t read_bytes(off_t, ssize_t, UserOrKernelBuffer& buffer, FileDescription*) const override; + virtual ssize_t write_bytes(off_t, ssize_t, const UserOrKernelBuffer& data, FileDescription*) override; virtual KResult traverse_as_directory(Function) const override; virtual RefPtr lookup(StringView name) override; virtual KResultOr> create_child(const String& name, mode_t, dev_t, uid_t, gid_t) override; diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 0fe482cf369aad..eef030b29fc61e 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -976,21 +976,33 @@ static ByteBuffer read_sys_bool(InodeIdentifier inode_id) return buffer; } -static ssize_t write_sys_bool(InodeIdentifier inode_id, const ByteBuffer& data) +static ssize_t write_sys_bool(InodeIdentifier inode_id, const UserOrKernelBuffer& buffer, size_t size) { auto& variable = SysVariable::for_inode(inode_id); ASSERT(variable.type == SysVariable::Type::Boolean); - if (data.is_empty() || !(data[0] == '0' || data[0] == '1')) - return data.size(); + char value = 0; + bool did_read = false; + ssize_t nread = buffer.read_buffered<1>(1, [&](const u8* data, size_t) { + if (did_read) + return 0; + value = (char)data[0]; + did_read = true; + return 1; + }); + if (nread < 0) + return nread; + ASSERT(nread == 0 || (nread == 1 && did_read)); + if (nread == 0 || !(value == '0' || value == '1')) + return (ssize_t)size; auto* lockable_bool = reinterpret_cast*>(variable.address); { LOCKER(lockable_bool->lock()); - lockable_bool->resource() = data[0] == '1'; + lockable_bool->resource() = value == '1'; } variable.notify(); - return data.size(); + return (ssize_t)size; } static ByteBuffer read_sys_string(InodeIdentifier inode_id) @@ -1003,18 +1015,22 @@ static ByteBuffer read_sys_string(InodeIdentifier inode_id) return lockable_string->resource().to_byte_buffer(); } -static ssize_t write_sys_string(InodeIdentifier inode_id, const ByteBuffer& data) +static ssize_t write_sys_string(InodeIdentifier inode_id, const UserOrKernelBuffer& buffer, size_t size) { auto& variable = SysVariable::for_inode(inode_id); ASSERT(variable.type == SysVariable::Type::String); + auto string_copy = buffer.copy_into_string(size); + if (string_copy.is_null()) + return -EFAULT; + { auto* lockable_string = reinterpret_cast*>(variable.address); LOCKER(lockable_string->lock()); - lockable_string->resource() = String((const char*)data.data(), data.size()); + lockable_string->resource() = move(string_copy); } variable.notify(); - return data.size(); + return (ssize_t)size; } void ProcFS::add_sys_bool(String&& name, Lockable& var, Function&& notify_callback) @@ -1180,13 +1196,13 @@ InodeMetadata ProcFSInode::metadata() const return metadata; } -ssize_t ProcFSInode::read_bytes(off_t offset, ssize_t count, u8* buffer, FileDescription* description) const +ssize_t ProcFSInode::read_bytes(off_t offset, ssize_t count, UserOrKernelBuffer& buffer, FileDescription* description) const { #ifdef PROCFS_DEBUG dbg() << "ProcFS: read_bytes " << index(); #endif ASSERT(offset >= 0); - ASSERT(buffer); + ASSERT(buffer.user_or_kernel_ptr()); auto* directory_entry = fs().get_directory_entry(identifier()); @@ -1240,7 +1256,8 @@ ssize_t ProcFSInode::read_bytes(off_t offset, ssize_t count, u8* buffer, FileDes return 0; ssize_t nread = min(static_cast(data.value().size() - offset), static_cast(count)); - memcpy(buffer, data.value().data() + offset, nread); + if (!buffer.write(data.value().data() + offset, nread)) + return -EFAULT; if (nread == 0 && description && description->generator_cache().has_value()) description->generator_cache().clear(); @@ -1461,7 +1478,7 @@ void ProcFSInode::flush_metadata() { } -ssize_t ProcFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, FileDescription*) +ssize_t ProcFSInode::write_bytes(off_t offset, ssize_t size, const UserOrKernelBuffer& buffer, FileDescription*) { auto result = prepare_to_write_data(); if (result.is_error()) @@ -1469,8 +1486,8 @@ ssize_t ProcFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, F auto* directory_entry = fs().get_directory_entry(identifier()); - Function callback_tmp; - Function* write_callback { nullptr }; + Function callback_tmp; + Function* write_callback { nullptr }; if (directory_entry == nullptr) { if (to_proc_parent_directory(identifier()) == PDI_Root_sys) { @@ -1496,9 +1513,10 @@ ssize_t ProcFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, F ASSERT(is_persistent_inode(identifier())); // FIXME: Being able to write into ProcFS at a non-zero offset seems like something we should maybe support.. ASSERT(offset == 0); - bool success = (*write_callback)(identifier(), ByteBuffer::wrap(const_cast(buffer), size)); - ASSERT(success); - return 0; + ssize_t nwritten = (*write_callback)(identifier(), buffer, (size_t)size); + if (nwritten < 0) + klog() << "ProcFS: Writing " << size << " bytes failed: " << nwritten; + return nwritten; } KResultOr> ProcFSInode::resolve_as_link(Custody& base, RefPtr* out_parent, int options, int symlink_recursion_level) const diff --git a/Kernel/FileSystem/ProcFS.h b/Kernel/FileSystem/ProcFS.h index a68df543f0e56f..5d2d3fb124f463 100644 --- a/Kernel/FileSystem/ProcFS.h +++ b/Kernel/FileSystem/ProcFS.h @@ -59,7 +59,7 @@ class ProcFS final : public FS { struct ProcFSDirectoryEntry { ProcFSDirectoryEntry() { } - ProcFSDirectoryEntry(const char* a_name, unsigned a_proc_file_type, bool a_supervisor_only, Function(InodeIdentifier)>&& a_read_callback = nullptr, Function&& a_write_callback = nullptr, RefPtr&& a_inode = nullptr) + ProcFSDirectoryEntry(const char* a_name, unsigned a_proc_file_type, bool a_supervisor_only, Function(InodeIdentifier)>&& a_read_callback = nullptr, Function&& a_write_callback = nullptr, RefPtr&& a_inode = nullptr) : name(a_name) , proc_file_type(a_proc_file_type) , supervisor_only(a_supervisor_only) @@ -73,7 +73,7 @@ class ProcFS final : public FS { unsigned proc_file_type { 0 }; bool supervisor_only { false }; Function(InodeIdentifier)> read_callback; - Function write_callback; + Function write_callback; RefPtr inode; InodeIdentifier identifier(unsigned fsid) const; }; @@ -96,12 +96,12 @@ class ProcFSInode final : public Inode { private: // ^Inode - virtual ssize_t read_bytes(off_t, ssize_t, u8* buffer, FileDescription*) const override; + virtual ssize_t read_bytes(off_t, ssize_t, UserOrKernelBuffer& buffer, FileDescription*) const override; virtual InodeMetadata metadata() const override; virtual KResult traverse_as_directory(Function) const override; virtual RefPtr lookup(StringView name) override; virtual void flush_metadata() override; - virtual ssize_t write_bytes(off_t, ssize_t, const u8* buffer, FileDescription*) override; + virtual ssize_t write_bytes(off_t, ssize_t, const UserOrKernelBuffer& buffer, FileDescription*) override; virtual KResultOr> create_child(const String& name, mode_t, dev_t, uid_t, gid_t) override; virtual KResult add_child(Inode&, const StringView& name, mode_t) override; virtual KResult remove_child(const StringView& name) override; @@ -123,12 +123,12 @@ class ProcFSProxyInode final : public Inode { private: // ^Inode - virtual ssize_t read_bytes(off_t, ssize_t, u8*, FileDescription*) const override { ASSERT_NOT_REACHED(); } + virtual ssize_t read_bytes(off_t, ssize_t, UserOrKernelBuffer&, FileDescription*) const override { ASSERT_NOT_REACHED(); } virtual InodeMetadata metadata() const override; virtual KResult traverse_as_directory(Function) const override { ASSERT_NOT_REACHED(); } virtual RefPtr lookup(StringView name) override; virtual void flush_metadata() override {}; - virtual ssize_t write_bytes(off_t, ssize_t, const u8*, FileDescription*) override { ASSERT_NOT_REACHED(); } + virtual ssize_t write_bytes(off_t, ssize_t, const UserOrKernelBuffer&, FileDescription*) override { ASSERT_NOT_REACHED(); } virtual KResultOr> create_child(const String& name, mode_t, dev_t, uid_t, gid_t) override; virtual KResult add_child(Inode&, const StringView& name, mode_t) override; virtual KResult remove_child(const StringView& name) override; diff --git a/Kernel/FileSystem/TmpFS.cpp b/Kernel/FileSystem/TmpFS.cpp index 04ac98d33d013b..31222d6e875ded 100644 --- a/Kernel/FileSystem/TmpFS.cpp +++ b/Kernel/FileSystem/TmpFS.cpp @@ -141,7 +141,7 @@ KResult TmpFSInode::traverse_as_directory(Function(size) > m_metadata.size - offset) size = m_metadata.size - offset; - memcpy(buffer, m_content.value().data() + offset, size); + if (!buffer.write(m_content.value().data() + offset, size)) + return -EFAULT; return size; } -ssize_t TmpFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, FileDescription*) +ssize_t TmpFSInode::write_bytes(off_t offset, ssize_t size, const UserOrKernelBuffer& buffer, FileDescription*) { LOCKER(m_lock); ASSERT(!is_directory()); @@ -199,7 +200,8 @@ ssize_t TmpFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, Fi inode_size_changed(old_size, new_size); } - memcpy(m_content.value().data() + offset, buffer, size); + if (!buffer.read(m_content.value().data() + offset, size)) // TODO: partial reads? + return -EFAULT; inode_contents_changed(offset, size, buffer); return size; @@ -343,8 +345,10 @@ KResult TmpFSInode::truncate(u64 size) if (old_size != (size_t)size) { inode_size_changed(old_size, size); - if (m_content.has_value()) - inode_contents_changed(0, size, m_content.value().data()); + if (m_content.has_value()) { + auto buffer = UserOrKernelBuffer::for_kernel_buffer(m_content.value().data()); + inode_contents_changed(0, size, buffer); + } } return KSuccess; diff --git a/Kernel/FileSystem/TmpFS.h b/Kernel/FileSystem/TmpFS.h index bb77e6b946da69..02eed3f17f4f7e 100644 --- a/Kernel/FileSystem/TmpFS.h +++ b/Kernel/FileSystem/TmpFS.h @@ -74,12 +74,12 @@ class TmpFSInode final : public Inode { const TmpFS& fs() const { return static_cast(Inode::fs()); } // ^Inode - virtual ssize_t read_bytes(off_t, ssize_t, u8* buffer, FileDescription*) const override; + virtual ssize_t read_bytes(off_t, ssize_t, UserOrKernelBuffer& buffer, FileDescription*) const override; virtual InodeMetadata metadata() const override; virtual KResult traverse_as_directory(Function) const override; virtual RefPtr lookup(StringView name) override; virtual void flush_metadata() override; - virtual ssize_t write_bytes(off_t, ssize_t, const u8* buffer, FileDescription*) override; + virtual ssize_t write_bytes(off_t, ssize_t, const UserOrKernelBuffer& buffer, FileDescription*) override; virtual KResultOr> create_child(const String& name, mode_t, dev_t, uid_t, gid_t) override; virtual KResult add_child(Inode&, const StringView& name, mode_t) override; virtual KResult remove_child(const StringView& name) override; diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp index ea06d462c3370a..c0c93dfecb8074 100644 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ b/Kernel/FileSystem/VirtualFileSystem.cpp @@ -707,7 +707,8 @@ KResult VFS::symlink(StringView target, StringView linkpath, Custody& base) if (inode_or_error.is_error()) return inode_or_error.error(); auto& inode = inode_or_error.value(); - ssize_t nwritten = inode->write_bytes(0, target.length(), (const u8*)target.characters_without_null_termination(), nullptr); + auto target_buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast((const u8*)target.characters_without_null_termination())); + ssize_t nwritten = inode->write_bytes(0, target.length(), target_buffer, nullptr); if (nwritten < 0) return KResult(nwritten); return KSuccess; diff --git a/Kernel/Forward.h b/Kernel/Forward.h index 8a40cdce043729..66afa3089628db 100644 --- a/Kernel/Forward.h +++ b/Kernel/Forward.h @@ -68,6 +68,7 @@ class TCPSocket; class TTY; class Thread; class UDPSocket; +class UserOrKernelBuffer; class VFS; class VMObject; class WaitQueue; diff --git a/Kernel/KSyms.cpp b/Kernel/KSyms.cpp index 5a7d26caac3b6a..69c01318e07e77 100644 --- a/Kernel/KSyms.cpp +++ b/Kernel/KSyms.cpp @@ -142,16 +142,22 @@ NEVER_INLINE static void dump_backtrace_impl(FlatPtr base_pointer, bool use_ksym RecognizedSymbol recognized_symbols[max_recognized_symbol_count]; size_t recognized_symbol_count = 0; if (use_ksyms) { - for (FlatPtr* stack_ptr = (FlatPtr*)base_pointer; - (current_process ? current_process->validate_read_from_kernel(VirtualAddress(stack_ptr), sizeof(void*) * 2) : 1) && recognized_symbol_count < max_recognized_symbol_count; stack_ptr = (FlatPtr*)*stack_ptr) { - FlatPtr retaddr = stack_ptr[1]; + FlatPtr copied_stack_ptr[2]; + for (FlatPtr* stack_ptr = (FlatPtr*)base_pointer; stack_ptr && recognized_symbol_count < max_recognized_symbol_count; stack_ptr = (FlatPtr*)copied_stack_ptr[0]) { + void* fault_at; + if (!safe_memcpy(copied_stack_ptr, stack_ptr, sizeof(copied_stack_ptr), fault_at)) + break; + FlatPtr retaddr = copied_stack_ptr[1]; recognized_symbols[recognized_symbol_count++] = { retaddr, symbolicate_kernel_address(retaddr) }; } } else { - for (FlatPtr* stack_ptr = (FlatPtr*)base_pointer; - (current_process ? current_process->validate_read_from_kernel(VirtualAddress(stack_ptr), sizeof(void*) * 2) : 1); stack_ptr = (FlatPtr*)*stack_ptr) { - FlatPtr retaddr = stack_ptr[1]; - dbg() << String::format("%x", retaddr) << " (next: " << String::format("%x", (stack_ptr ? (u32*)*stack_ptr : 0)) << ")"; + void* fault_at; + FlatPtr copied_stack_ptr[2]; + FlatPtr* stack_ptr = (FlatPtr*)base_pointer; + while (stack_ptr && safe_memcpy(copied_stack_ptr, stack_ptr, sizeof(copied_stack_ptr), fault_at)) { + FlatPtr retaddr = copied_stack_ptr[1]; + dbg() << String::format("%x", retaddr) << " (next: " << String::format("%x", (stack_ptr ? (u32*)copied_stack_ptr[0] : 0)) << ")"; + stack_ptr = (FlatPtr*)copied_stack_ptr[0]; } return; } diff --git a/Kernel/Net/IPv4Socket.cpp b/Kernel/Net/IPv4Socket.cpp index 7ddead3e15b1a5..c64c036c4de6b7 100644 --- a/Kernel/Net/IPv4Socket.cpp +++ b/Kernel/Net/IPv4Socket.cpp @@ -105,7 +105,8 @@ KResult IPv4Socket::bind(Userspace user_address, socklen_t addr return KResult(-EINVAL); sockaddr_in address; - copy_from_user(&address, user_address, sizeof(sockaddr_in)); + if (!copy_from_user(&address, user_address, sizeof(sockaddr_in))) + return KResult(-EFAULT); if (address.sin_family != AF_INET) return KResult(-EINVAL); @@ -144,18 +145,25 @@ KResult IPv4Socket::listen(size_t backlog) return protocol_listen(); } -KResult IPv4Socket::connect(FileDescription& description, const sockaddr* address, socklen_t address_size, ShouldBlock should_block) +KResult IPv4Socket::connect(FileDescription& description, Userspace address, socklen_t address_size, ShouldBlock should_block) { if (address_size != sizeof(sockaddr_in)) return KResult(-EINVAL); - if (address->sa_family != AF_INET) + u16 sa_family_copy; + auto* user_address = reinterpret_cast(address.unsafe_userspace_ptr()); + if (!copy_from_user(&sa_family_copy, &user_address->sa_family, sizeof(u16))) + return KResult(-EFAULT); + if (sa_family_copy != AF_INET) return KResult(-EINVAL); if (m_role == Role::Connected) return KResult(-EISCONN); - auto& ia = *(const sockaddr_in*)address; - m_peer_address = IPv4Address((const u8*)&ia.sin_addr.s_addr); - m_peer_port = ntohs(ia.sin_port); + sockaddr_in safe_address; + if (!copy_from_user(&safe_address, (const sockaddr_in*)user_address, sizeof(sockaddr_in))) + return KResult(-EFAULT); + + m_peer_address = IPv4Address((const u8*)&safe_address.sin_addr.s_addr); + m_peer_port = ntohs(safe_address.sin_port); return protocol_connect(description, should_block); } @@ -193,7 +201,7 @@ int IPv4Socket::allocate_local_port_if_needed() return port; } -KResultOr IPv4Socket::sendto(FileDescription&, const void* data, size_t data_length, int flags, Userspace addr, socklen_t addr_length) +KResultOr IPv4Socket::sendto(FileDescription&, const UserOrKernelBuffer& data, size_t data_length, int flags, Userspace addr, socklen_t addr_length) { (void)flags; if (addr && addr_length != sizeof(sockaddr_in)) @@ -201,7 +209,7 @@ KResultOr IPv4Socket::sendto(FileDescription&, const void* data, size_t if (addr) { sockaddr_in ia; - if (!Process::current()->validate_read_and_copy_typed(&ia, Userspace(addr.ptr()))) + if (!copy_from_user(&ia, Userspace(addr.ptr()))) return KResult(-EFAULT); if (ia.sin_family != AF_INET) { @@ -229,7 +237,9 @@ KResultOr IPv4Socket::sendto(FileDescription&, const void* data, size_t #endif if (type() == SOCK_RAW) { - routing_decision.adapter->send_ipv4(routing_decision.next_hop, m_peer_address, (IPv4Protocol)protocol(), { (const u8*)data, data_length }, m_ttl); + int err = routing_decision.adapter->send_ipv4(routing_decision.next_hop, m_peer_address, (IPv4Protocol)protocol(), data, data_length, m_ttl); + if (err < 0) + return KResult(err); return data_length; } @@ -239,7 +249,7 @@ KResultOr IPv4Socket::sendto(FileDescription&, const void* data, size_t return nsent_or_error; } -KResultOr IPv4Socket::receive_byte_buffered(FileDescription& description, void* buffer, size_t buffer_length, int, Userspace, Userspace) +KResultOr IPv4Socket::receive_byte_buffered(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int, Userspace, Userspace) { Locker locker(lock()); if (m_receive_buffer.is_empty()) { @@ -262,7 +272,7 @@ KResultOr IPv4Socket::receive_byte_buffered(FileDescription& description } ASSERT(!m_receive_buffer.is_empty()); - int nreceived = m_receive_buffer.read((u8*)buffer, buffer_length); + int nreceived = m_receive_buffer.read(buffer, buffer_length); if (nreceived > 0) Thread::current()->did_ipv4_socket_read((size_t)nreceived); @@ -270,7 +280,7 @@ KResultOr IPv4Socket::receive_byte_buffered(FileDescription& description return nreceived; } -KResultOr IPv4Socket::receive_packet_buffered(FileDescription& description, void* buffer, size_t buffer_length, int flags, Userspace addr, Userspace addr_length) +KResultOr IPv4Socket::receive_packet_buffered(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace addr, Userspace addr_length) { Locker locker(lock()); ReceivedPacket packet; @@ -330,27 +340,30 @@ KResultOr IPv4Socket::receive_packet_buffered(FileDescription& descripti out_addr.sin_port = htons(packet.peer_port); out_addr.sin_family = AF_INET; Userspace dest_addr = addr.ptr(); - copy_to_user(dest_addr, &out_addr); + if (!copy_to_user(dest_addr, &out_addr)) + return KResult(-EFAULT); socklen_t out_length = sizeof(sockaddr_in); ASSERT(addr_length); - copy_to_user(addr_length, &out_length); + if (!copy_to_user(addr_length, &out_length)) + return KResult(-EFAULT); } if (type() == SOCK_RAW) { size_t bytes_written = min((size_t) ipv4_packet.payload_size(), buffer_length); - memcpy(buffer, ipv4_packet.payload(), bytes_written); + if (!buffer.write(ipv4_packet.payload(), bytes_written)) + return KResult(-EFAULT); return bytes_written; } return protocol_receive(packet.data.value(), buffer, buffer_length, flags); } -KResultOr IPv4Socket::recvfrom(FileDescription& description, void* buffer, size_t buffer_length, int flags, Userspace user_addr, Userspace user_addr_length) +KResultOr IPv4Socket::recvfrom(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace user_addr, Userspace user_addr_length) { if (user_addr_length) { socklen_t addr_length; - if (!Process::current()->validate_read_and_copy_typed(&addr_length, user_addr_length)) + if (!copy_from_user(&addr_length, user_addr_length.unsafe_userspace_ptr())) return KResult(-EFAULT); if (addr_length < sizeof(sockaddr_in)) return KResult(-EINVAL); @@ -387,10 +400,13 @@ bool IPv4Socket::did_receive(const IPv4Address& source_address, u16 source_port, ASSERT(m_can_read); return false; } - auto nreceived_or_error = protocol_receive(packet, m_scratch_buffer.value().data(), m_scratch_buffer.value().size(), 0); + auto scratch_buffer = UserOrKernelBuffer::for_kernel_buffer(m_scratch_buffer.value().data()); + auto nreceived_or_error = protocol_receive(packet, scratch_buffer, m_scratch_buffer.value().size(), 0); if (nreceived_or_error.is_error()) return false; - m_receive_buffer.write(m_scratch_buffer.value().data(), nreceived_or_error.value()); + ssize_t nwritten = m_receive_buffer.write(scratch_buffer, nreceived_or_error.value()); + if (nwritten < 0) + return false; m_can_read = !m_receive_buffer.is_empty(); } else { if (m_receive_queue.size() > 2000) { @@ -452,7 +468,7 @@ KResult IPv4Socket::setsockopt(int level, int option, Userspace use if (user_value_size < sizeof(int)) return KResult(-EINVAL); int value; - if (!Process::current()->validate_read_and_copy_typed(&value, static_ptr_cast(user_value))) + if (!copy_from_user(&value, static_ptr_cast(user_value))) return KResult(-EFAULT); if (value < 0 || value > 255) return KResult(-EINVAL); @@ -470,16 +486,18 @@ KResult IPv4Socket::getsockopt(FileDescription& description, int level, int opti return Socket::getsockopt(description, level, option, value, value_size); socklen_t size; - if (!Process::current()->validate_read_and_copy_typed(&size, value_size)) + if (!copy_from_user(&size, value_size.unsafe_userspace_ptr())) return KResult(-EFAULT); switch (option) { case IP_TTL: if (size < sizeof(int)) return KResult(-EINVAL); - copy_to_user(static_ptr_cast(value), (int*)&m_ttl); + if (!copy_to_user(static_ptr_cast(value), (int*)&m_ttl)) + return KResult(-EFAULT); size = sizeof(int); - copy_to_user(value_size, &size); + if (!copy_to_user(value_size, &size)) + return KResult(-EFAULT); return KSuccess; default: return KResult(-ENOPROTOOPT); @@ -493,15 +511,15 @@ int IPv4Socket::ioctl(FileDescription&, unsigned request, FlatPtr arg) SmapDisabler disabler; auto ioctl_route = [request, arg]() { - auto* route = (rtentry*)arg; - if (!Process::current()->validate_read_typed(route)) + rtentry route; + if (!copy_from_user(&route, (rtentry*)arg)) return -EFAULT; - char namebuf[IFNAMSIZ + 1]; - memcpy(namebuf, route->rt_dev, IFNAMSIZ); - namebuf[sizeof(namebuf) - 1] = '\0'; + auto copied_ifname = copy_string_from_user(route.rt_dev, IFNAMSIZ); + if (copied_ifname.is_null()) + return -EFAULT; - auto adapter = NetworkAdapter::lookup_by_name(namebuf); + auto adapter = NetworkAdapter::lookup_by_name(copied_ifname); if (!adapter) return -ENODEV; @@ -509,11 +527,11 @@ int IPv4Socket::ioctl(FileDescription&, unsigned request, FlatPtr arg) case SIOCADDRT: if (!Process::current()->is_superuser()) return -EPERM; - if (route->rt_gateway.sa_family != AF_INET) + if (route.rt_gateway.sa_family != AF_INET) return -EAFNOSUPPORT; - if ((route->rt_flags & (RTF_UP | RTF_GATEWAY)) != (RTF_UP | RTF_GATEWAY)) + if ((route.rt_flags & (RTF_UP | RTF_GATEWAY)) != (RTF_UP | RTF_GATEWAY)) return -EINVAL; // FIXME: Find the correct value to return - adapter->set_ipv4_gateway(IPv4Address(((sockaddr_in&)route->rt_gateway).sin_addr.s_addr)); + adapter->set_ipv4_gateway(IPv4Address(((sockaddr_in&)route.rt_gateway).sin_addr.s_addr)); return 0; case SIOCDELRT: @@ -525,12 +543,13 @@ int IPv4Socket::ioctl(FileDescription&, unsigned request, FlatPtr arg) }; auto ioctl_interface = [request, arg]() { - auto* ifr = (ifreq*)arg; - if (!Process::current()->validate_read_typed(ifr)) + ifreq* user_ifr = (ifreq*)arg; + ifreq ifr; + if (!copy_from_user(&ifr, user_ifr)) return -EFAULT; char namebuf[IFNAMSIZ + 1]; - memcpy(namebuf, ifr->ifr_name, IFNAMSIZ); + memcpy(namebuf, ifr.ifr_name, IFNAMSIZ); namebuf[sizeof(namebuf) - 1] = '\0'; auto adapter = NetworkAdapter::lookup_by_name(namebuf); @@ -541,36 +560,39 @@ int IPv4Socket::ioctl(FileDescription&, unsigned request, FlatPtr arg) case SIOCSIFADDR: if (!Process::current()->is_superuser()) return -EPERM; - if (ifr->ifr_addr.sa_family != AF_INET) + if (ifr.ifr_addr.sa_family != AF_INET) return -EAFNOSUPPORT; - adapter->set_ipv4_address(IPv4Address(((sockaddr_in&)ifr->ifr_addr).sin_addr.s_addr)); + adapter->set_ipv4_address(IPv4Address(((sockaddr_in&)ifr.ifr_addr).sin_addr.s_addr)); return 0; case SIOCSIFNETMASK: if (!Process::current()->is_superuser()) return -EPERM; - if (ifr->ifr_addr.sa_family != AF_INET) + if (ifr.ifr_addr.sa_family != AF_INET) return -EAFNOSUPPORT; - adapter->set_ipv4_netmask(IPv4Address(((sockaddr_in&)ifr->ifr_netmask).sin_addr.s_addr)); + adapter->set_ipv4_netmask(IPv4Address(((sockaddr_in&)ifr.ifr_netmask).sin_addr.s_addr)); return 0; - case SIOCGIFADDR: - if (!Process::current()->validate_write_typed(ifr)) + case SIOCGIFADDR: { + u16 sa_family = AF_INET; + if (!copy_to_user(&user_ifr->ifr_addr.sa_family, &sa_family)) + return -EFAULT; + auto ip4_addr = adapter->ipv4_address().to_u32(); + if (!copy_to_user(&((sockaddr_in&)user_ifr->ifr_addr).sin_addr.s_addr, &ip4_addr, sizeof(ip4_addr))) return -EFAULT; - ifr->ifr_addr.sa_family = AF_INET; - ((sockaddr_in&)ifr->ifr_addr).sin_addr.s_addr = adapter->ipv4_address().to_u32(); return 0; + } - case SIOCGIFHWADDR: - if (!Process::current()->validate_write_typed(ifr)) + case SIOCGIFHWADDR: { + u16 sa_family = AF_INET; + if (!copy_to_user(&user_ifr->ifr_hwaddr.sa_family, &sa_family)) + return -EFAULT; + auto mac_address = adapter->mac_address(); + if (!copy_to_user(ifr.ifr_hwaddr.sa_data, &mac_address, sizeof(MACAddress))) return -EFAULT; - ifr->ifr_hwaddr.sa_family = AF_INET; - { - auto mac_address = adapter->mac_address(); - memcpy(ifr->ifr_hwaddr.sa_data, &mac_address, sizeof(MACAddress)); - } return 0; } + } return -EINVAL; }; diff --git a/Kernel/Net/IPv4Socket.h b/Kernel/Net/IPv4Socket.h index cb513247acbf98..675cfbfcd14352 100644 --- a/Kernel/Net/IPv4Socket.h +++ b/Kernel/Net/IPv4Socket.h @@ -50,7 +50,7 @@ class IPv4Socket : public Socket { virtual KResult close() override; virtual KResult bind(Userspace, socklen_t) override; - virtual KResult connect(FileDescription&, const sockaddr*, socklen_t, ShouldBlock = ShouldBlock::Yes) override; + virtual KResult connect(FileDescription&, Userspace, socklen_t, ShouldBlock = ShouldBlock::Yes) override; virtual KResult listen(size_t) override; virtual void get_local_address(sockaddr*, socklen_t*) override; virtual void get_peer_address(sockaddr*, socklen_t*) override; @@ -58,8 +58,8 @@ class IPv4Socket : public Socket { virtual void detach(FileDescription&) override; virtual bool can_read(const FileDescription&, size_t) const override; virtual bool can_write(const FileDescription&, size_t) const override; - virtual KResultOr sendto(FileDescription&, const void*, size_t, int, Userspace, socklen_t) override; - virtual KResultOr recvfrom(FileDescription&, void*, size_t, int flags, Userspace, Userspace) override; + virtual KResultOr sendto(FileDescription&, const UserOrKernelBuffer&, size_t, int, Userspace, socklen_t) override; + virtual KResultOr recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace, Userspace) override; virtual KResult setsockopt(int level, int option, Userspace, socklen_t) override; virtual KResult getsockopt(FileDescription&, int level, int option, Userspace, Userspace) override; @@ -96,8 +96,8 @@ class IPv4Socket : public Socket { virtual KResult protocol_bind() { return KSuccess; } virtual KResult protocol_listen() { return KSuccess; } - virtual KResultOr protocol_receive(const KBuffer&, void*, size_t, int) { return -ENOTIMPL; } - virtual KResultOr protocol_send(const void*, size_t) { return -ENOTIMPL; } + virtual KResultOr protocol_receive(const KBuffer&, UserOrKernelBuffer&, size_t, int) { return -ENOTIMPL; } + virtual KResultOr protocol_send(const UserOrKernelBuffer&, size_t) { return -ENOTIMPL; } virtual KResult protocol_connect(FileDescription&, ShouldBlock) { return KSuccess; } virtual int protocol_allocate_local_port() { return 0; } virtual bool protocol_is_disconnected() const { return false; } @@ -110,8 +110,8 @@ class IPv4Socket : public Socket { private: virtual bool is_ipv4() const override { return true; } - KResultOr receive_byte_buffered(FileDescription&, void* buffer, size_t buffer_length, int flags, Userspace, Userspace); - KResultOr receive_packet_buffered(FileDescription&, void* buffer, size_t buffer_length, int flags, Userspace, Userspace); + KResultOr receive_byte_buffered(FileDescription&, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace, Userspace); + KResultOr receive_packet_buffered(FileDescription&, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace, Userspace); IPv4Address m_local_address; IPv4Address m_peer_address; diff --git a/Kernel/Net/LocalSocket.cpp b/Kernel/Net/LocalSocket.cpp index a401af1e9f01da..ee08ca0c26e7b2 100644 --- a/Kernel/Net/LocalSocket.cpp +++ b/Kernel/Net/LocalSocket.cpp @@ -98,7 +98,8 @@ KResult LocalSocket::bind(Userspace user_address, socklen_t add return KResult(-EINVAL); sockaddr_un address; - copy_from_user(&address, user_address, sizeof(sockaddr_un)); + if (!copy_from_user(&address, user_address, sizeof(sockaddr_un))) + return KResult(-EFAULT); if (address.sun_family != AF_LOCAL) return KResult(-EINVAL); @@ -131,19 +132,25 @@ KResult LocalSocket::bind(Userspace user_address, socklen_t add return KSuccess; } -KResult LocalSocket::connect(FileDescription& description, const sockaddr* address, socklen_t address_size, ShouldBlock) +KResult LocalSocket::connect(FileDescription& description, Userspace address, socklen_t address_size, ShouldBlock) { ASSERT(!m_bound); if (address_size != sizeof(sockaddr_un)) return KResult(-EINVAL); - if (address->sa_family != AF_LOCAL) + u16 sa_family_copy; + auto* user_address = reinterpret_cast(address.unsafe_userspace_ptr()); + if (!copy_from_user(&sa_family_copy, &user_address->sa_family, sizeof(u16))) + return KResult(-EFAULT); + if (sa_family_copy != AF_LOCAL) return KResult(-EINVAL); if (is_connected()) return KResult(-EISCONN); - const sockaddr_un& local_address = *reinterpret_cast(address); + const auto& local_address = *reinterpret_cast(user_address); char safe_address[sizeof(local_address.sun_path) + 1] = { 0 }; - memcpy(safe_address, local_address.sun_path, sizeof(local_address.sun_path)); + if (!copy_from_user(&safe_address[0], &local_address.sun_path[0], sizeof(safe_address) - 1)) + return KResult(-EFAULT); + safe_address[sizeof(safe_address) - 1] = '\0'; #ifdef DEBUG_LOCAL_SOCKET dbg() << "LocalSocket{" << this << "} connect(" << safe_address << ")"; @@ -159,7 +166,8 @@ KResult LocalSocket::connect(FileDescription& description, const sockaddr* addre if (!m_file->inode()->socket()) return KResult(-ECONNREFUSED); - m_address = local_address; + m_address.sun_family = sa_family_copy; + memcpy(m_address.sun_path, safe_address, sizeof(m_address.sun_path)); ASSERT(m_connect_side_fd == &description); m_connect_side_role = Role::Connecting; @@ -260,11 +268,11 @@ bool LocalSocket::can_write(const FileDescription& description, size_t) const return false; } -KResultOr LocalSocket::sendto(FileDescription& description, const void* data, size_t data_size, int, Userspace, socklen_t) +KResultOr LocalSocket::sendto(FileDescription& description, const UserOrKernelBuffer& data, size_t data_size, int, Userspace, socklen_t) { if (!has_attached_peer(description)) return KResult(-EPIPE); - ssize_t nwritten = send_buffer_for(description).write((const u8*)data, data_size); + ssize_t nwritten = send_buffer_for(description).write(data, data_size); if (nwritten > 0) Thread::current()->did_unix_socket_write(nwritten); return nwritten; @@ -290,7 +298,7 @@ DoubleBuffer& LocalSocket::send_buffer_for(FileDescription& description) ASSERT_NOT_REACHED(); } -KResultOr LocalSocket::recvfrom(FileDescription& description, void* buffer, size_t buffer_size, int, Userspace, Userspace) +KResultOr LocalSocket::recvfrom(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_size, int, Userspace, Userspace) { auto& buffer_for_me = receive_buffer_for(description); if (!description.is_blocking()) { @@ -306,7 +314,7 @@ KResultOr LocalSocket::recvfrom(FileDescription& description, void* buff if (!has_attached_peer(description) && buffer_for_me.is_empty()) return 0; ASSERT(!buffer_for_me.is_empty()); - int nread = buffer_for_me.read((u8*)buffer, buffer_size); + int nread = buffer_for_me.read(buffer, buffer_size); if (nread > 0) Thread::current()->did_unix_socket_read(nread); return nread; @@ -350,7 +358,7 @@ KResult LocalSocket::getsockopt(FileDescription& description, int level, int opt return Socket::getsockopt(description, level, option, value, value_size); socklen_t size; - if (!Process::current()->validate_read_and_copy_typed(&size, value_size)) + if (!copy_from_user(&size, value_size.unsafe_userspace_ptr())) return KResult(-EFAULT); switch (option) { @@ -359,14 +367,18 @@ KResult LocalSocket::getsockopt(FileDescription& description, int level, int opt return KResult(-EINVAL); switch (role(description)) { case Role::Accepted: - copy_to_user(static_ptr_cast(value), &m_origin); + if (!copy_to_user(static_ptr_cast(value), &m_origin)) + return KResult(-EFAULT); size = sizeof(ucred); - copy_to_user(value_size, &size); + if (!copy_to_user(value_size, &size)) + return KResult(-EFAULT); return KSuccess; case Role::Connected: - copy_to_user(static_ptr_cast(value), &m_acceptor); + if (!copy_to_user(static_ptr_cast(value), &m_acceptor)) + return KResult(-EFAULT); size = sizeof(ucred); - copy_to_user(value_size, &size); + if (!copy_to_user(value_size, &size)) + return KResult(-EFAULT); return KSuccess; case Role::Connecting: return KResult(-ENOTCONN); diff --git a/Kernel/Net/LocalSocket.h b/Kernel/Net/LocalSocket.h index cafdf599a47ddb..d39e78d20b5d35 100644 --- a/Kernel/Net/LocalSocket.h +++ b/Kernel/Net/LocalSocket.h @@ -52,7 +52,7 @@ class LocalSocket final : public Socket // ^Socket virtual KResult bind(Userspace, socklen_t) override; - virtual KResult connect(FileDescription&, const sockaddr*, socklen_t, ShouldBlock = ShouldBlock::Yes) override; + virtual KResult connect(FileDescription&, Userspace, socklen_t, ShouldBlock = ShouldBlock::Yes) override; virtual KResult listen(size_t) override; virtual void get_local_address(sockaddr*, socklen_t*) override; virtual void get_peer_address(sockaddr*, socklen_t*) override; @@ -60,8 +60,8 @@ class LocalSocket final : public Socket virtual void detach(FileDescription&) override; virtual bool can_read(const FileDescription&, size_t) const override; virtual bool can_write(const FileDescription&, size_t) const override; - virtual KResultOr sendto(FileDescription&, const void*, size_t, int, Userspace, socklen_t) override; - virtual KResultOr recvfrom(FileDescription&, void*, size_t, int flags, Userspace, Userspace) override; + virtual KResultOr sendto(FileDescription&, const UserOrKernelBuffer&, size_t, int, Userspace, socklen_t) override; + virtual KResultOr recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace, Userspace) override; virtual KResult getsockopt(FileDescription&, int level, int option, Userspace, Userspace) override; virtual KResult chown(FileDescription&, uid_t, gid_t) override; virtual KResult chmod(FileDescription&, mode_t) override; diff --git a/Kernel/Net/NetworkAdapter.cpp b/Kernel/Net/NetworkAdapter.cpp index 4cfdd1cd43f3dc..4a81fa77af661e 100644 --- a/Kernel/Net/NetworkAdapter.cpp +++ b/Kernel/Net/NetworkAdapter.cpp @@ -100,15 +100,13 @@ void NetworkAdapter::send(const MACAddress& destination, const ARPPacket& packet send_raw({ (const u8*)eth, size_in_bytes }); } -void NetworkAdapter::send_ipv4(const MACAddress& destination_mac, const IPv4Address& destination_ipv4, IPv4Protocol protocol, ReadonlyBytes payload, u8 ttl) +int NetworkAdapter::send_ipv4(const MACAddress& destination_mac, const IPv4Address& destination_ipv4, IPv4Protocol protocol, const UserOrKernelBuffer& payload, size_t payload_size, u8 ttl) { - size_t ipv4_packet_size = sizeof(IPv4Packet) + payload.size(); - if (ipv4_packet_size > mtu()) { - send_ipv4_fragmented(destination_mac, destination_ipv4, protocol, payload, ttl); - return; - } + size_t ipv4_packet_size = sizeof(IPv4Packet) + payload_size; + if (ipv4_packet_size > mtu()) + return send_ipv4_fragmented(destination_mac, destination_ipv4, protocol, payload, payload_size, ttl); - size_t ethernet_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet) + payload.size(); + size_t ethernet_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet) + payload_size; auto buffer = ByteBuffer::create_zeroed(ethernet_frame_size); auto& eth = *(EthernetFrameHeader*)buffer.data(); eth.set_source(mac_address()); @@ -120,22 +118,25 @@ void NetworkAdapter::send_ipv4(const MACAddress& destination_mac, const IPv4Addr ipv4.set_source(ipv4_address()); ipv4.set_destination(destination_ipv4); ipv4.set_protocol((u8)protocol); - ipv4.set_length(sizeof(IPv4Packet) + payload.size()); + ipv4.set_length(sizeof(IPv4Packet) + payload_size); ipv4.set_ident(1); ipv4.set_ttl(ttl); ipv4.set_checksum(ipv4.compute_checksum()); m_packets_out++; m_bytes_out += ethernet_frame_size; - memcpy(ipv4.payload(), payload.data(), payload.size()); + + if (!payload.read(ipv4.payload(), payload_size)) + return -EFAULT; send_raw({ (const u8*)ð, ethernet_frame_size }); + return 0; } -void NetworkAdapter::send_ipv4_fragmented(const MACAddress& destination_mac, const IPv4Address& destination_ipv4, IPv4Protocol protocol, ReadonlyBytes payload, u8 ttl) +int NetworkAdapter::send_ipv4_fragmented(const MACAddress& destination_mac, const IPv4Address& destination_ipv4, IPv4Protocol protocol, const UserOrKernelBuffer& payload, size_t payload_size, u8 ttl) { // packets must be split on the 64-bit boundary auto packet_boundary_size = (mtu() - sizeof(IPv4Packet) - sizeof(EthernetFrameHeader)) & 0xfffffff8; - auto fragment_block_count = (payload.size() + packet_boundary_size) / packet_boundary_size; - auto last_block_size = payload.size() - packet_boundary_size * (fragment_block_count - 1); + auto fragment_block_count = (payload_size + packet_boundary_size) / packet_boundary_size; + auto last_block_size = payload_size - packet_boundary_size * (fragment_block_count - 1); auto number_of_blocks_in_fragment = packet_boundary_size / 8; auto identification = get_good_random(); @@ -163,9 +164,11 @@ void NetworkAdapter::send_ipv4_fragmented(const MACAddress& destination_mac, con ipv4.set_checksum(ipv4.compute_checksum()); m_packets_out++; m_bytes_out += ethernet_frame_size; - memcpy(ipv4.payload(), payload.data() + packet_index * packet_boundary_size, packet_payload_size); + if (!payload.read(ipv4.payload(), packet_index * packet_boundary_size, packet_payload_size)) + return -EFAULT; send_raw({ (const u8*)ð, ethernet_frame_size }); } + return 0; } void NetworkAdapter::did_receive(ReadonlyBytes payload) diff --git a/Kernel/Net/NetworkAdapter.h b/Kernel/Net/NetworkAdapter.h index 7fc178bf7df269..619b25f281bc02 100644 --- a/Kernel/Net/NetworkAdapter.h +++ b/Kernel/Net/NetworkAdapter.h @@ -37,6 +37,7 @@ #include #include #include +#include namespace Kernel { @@ -63,8 +64,8 @@ class NetworkAdapter : public RefCounted { void set_ipv4_gateway(const IPv4Address&); void send(const MACAddress&, const ARPPacket&); - void send_ipv4(const MACAddress&, const IPv4Address&, IPv4Protocol, ReadonlyBytes payload, u8 ttl); - void send_ipv4_fragmented(const MACAddress&, const IPv4Address&, IPv4Protocol, ReadonlyBytes payload, u8 ttl); + int send_ipv4(const MACAddress&, const IPv4Address&, IPv4Protocol, const UserOrKernelBuffer& payload, size_t payload_size, u8 ttl); + int send_ipv4_fragmented(const MACAddress&, const IPv4Address&, IPv4Protocol, const UserOrKernelBuffer& payload, size_t payload_size, u8 ttl); size_t dequeue_packet(u8* buffer, size_t buffer_size); diff --git a/Kernel/Net/NetworkTask.cpp b/Kernel/Net/NetworkTask.cpp index f198bc06073483..0098d686fd90f2 100644 --- a/Kernel/Net/NetworkTask.cpp +++ b/Kernel/Net/NetworkTask.cpp @@ -285,7 +285,8 @@ void handle_icmp(const EthernetFrameHeader& eth, const IPv4Packet& ipv4_packet) memcpy(response.payload(), request.payload(), icmp_payload_size); response.header.set_checksum(internet_checksum(&response, icmp_packet_size)); // FIXME: What is the right TTL value here? Is 64 ok? Should we use the same TTL as the echo request? - adapter->send_ipv4(eth.source(), ipv4_packet.source(), IPv4Protocol::ICMP, buffer, 64); + auto response_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&response); + adapter->send_ipv4(eth.source(), ipv4_packet.source(), IPv4Protocol::ICMP, response_buffer, buffer.size(), 64); } } @@ -379,7 +380,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) return; case TCPSocket::State::TimeWait: klog() << "handle_tcp: unexpected flags in TimeWait state"; - socket->send_tcp_packet(TCPFlags::RST); + (void)socket->send_tcp_packet(TCPFlags::RST); socket->set_state(TCPSocket::State::Closed); return; case TCPSocket::State::Listen: @@ -400,46 +401,46 @@ void handle_tcp(const IPv4Packet& ipv4_packet) #endif client->set_sequence_number(1000); client->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - client->send_tcp_packet(TCPFlags::SYN | TCPFlags::ACK); + (void)client->send_tcp_packet(TCPFlags::SYN | TCPFlags::ACK); client->set_state(TCPSocket::State::SynReceived); return; } default: klog() << "handle_tcp: unexpected flags in Listen state"; - // socket->send_tcp_packet(TCPFlags::RST); + // (void)socket->send_tcp_packet(TCPFlags::RST); return; } case TCPSocket::State::SynSent: switch (tcp_packet.flags()) { case TCPFlags::SYN: socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - socket->send_tcp_packet(TCPFlags::ACK); + (void)socket->send_tcp_packet(TCPFlags::ACK); socket->set_state(TCPSocket::State::SynReceived); return; case TCPFlags::ACK | TCPFlags::SYN: socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - socket->send_tcp_packet(TCPFlags::ACK); + (void)socket->send_tcp_packet(TCPFlags::ACK); socket->set_state(TCPSocket::State::Established); socket->set_setup_state(Socket::SetupState::Completed); socket->set_connected(true); return; case TCPFlags::ACK | TCPFlags::FIN: socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - socket->send_tcp_packet(TCPFlags::ACK); + (void)socket->send_tcp_packet(TCPFlags::ACK); socket->set_state(TCPSocket::State::Closed); socket->set_error(TCPSocket::Error::FINDuringConnect); socket->set_setup_state(Socket::SetupState::Completed); return; case TCPFlags::ACK | TCPFlags::RST: socket->set_ack_number(tcp_packet.sequence_number() + payload_size); - socket->send_tcp_packet(TCPFlags::ACK); + (void)socket->send_tcp_packet(TCPFlags::ACK); socket->set_state(TCPSocket::State::Closed); socket->set_error(TCPSocket::Error::RSTDuringConnect); socket->set_setup_state(Socket::SetupState::Completed); return; default: klog() << "handle_tcp: unexpected flags in SynSent state"; - socket->send_tcp_packet(TCPFlags::RST); + (void)socket->send_tcp_packet(TCPFlags::RST); socket->set_state(TCPSocket::State::Closed); socket->set_error(TCPSocket::Error::UnexpectedFlagsDuringConnect); socket->set_setup_state(Socket::SetupState::Completed); @@ -454,7 +455,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) case TCPSocket::Direction::Incoming: if (!socket->has_originator()) { klog() << "handle_tcp: connection doesn't have an originating socket; maybe it went away?"; - socket->send_tcp_packet(TCPFlags::RST); + (void)socket->send_tcp_packet(TCPFlags::RST); socket->set_state(TCPSocket::State::Closed); return; } @@ -470,7 +471,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) return; default: klog() << "handle_tcp: got ACK in SynReceived state but direction is invalid (" << TCPSocket::to_string(socket->direction()) << ")"; - socket->send_tcp_packet(TCPFlags::RST); + (void)socket->send_tcp_packet(TCPFlags::RST); socket->set_state(TCPSocket::State::Closed); return; } @@ -478,7 +479,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) return; default: klog() << "handle_tcp: unexpected flags in SynReceived state"; - socket->send_tcp_packet(TCPFlags::RST); + (void)socket->send_tcp_packet(TCPFlags::RST); socket->set_state(TCPSocket::State::Closed); return; } @@ -486,7 +487,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) switch (tcp_packet.flags()) { default: klog() << "handle_tcp: unexpected flags in CloseWait state"; - socket->send_tcp_packet(TCPFlags::RST); + (void)socket->send_tcp_packet(TCPFlags::RST); socket->set_state(TCPSocket::State::Closed); return; } @@ -498,7 +499,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) return; default: klog() << "handle_tcp: unexpected flags in LastAck state"; - socket->send_tcp_packet(TCPFlags::RST); + (void)socket->send_tcp_packet(TCPFlags::RST); socket->set_state(TCPSocket::State::Closed); return; } @@ -514,7 +515,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) return; default: klog() << "handle_tcp: unexpected flags in FinWait1 state"; - socket->send_tcp_packet(TCPFlags::RST); + (void)socket->send_tcp_packet(TCPFlags::RST); socket->set_state(TCPSocket::State::Closed); return; } @@ -529,7 +530,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) return; default: klog() << "handle_tcp: unexpected flags in FinWait2 state"; - socket->send_tcp_packet(TCPFlags::RST); + (void)socket->send_tcp_packet(TCPFlags::RST); socket->set_state(TCPSocket::State::Closed); return; } @@ -541,7 +542,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) return; default: klog() << "handle_tcp: unexpected flags in Closing state"; - socket->send_tcp_packet(TCPFlags::RST); + (void)socket->send_tcp_packet(TCPFlags::RST); socket->set_state(TCPSocket::State::Closed); return; } @@ -551,7 +552,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) socket->did_receive(ipv4_packet.source(), tcp_packet.source_port(), KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size())); socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1); - socket->send_tcp_packet(TCPFlags::ACK); + (void)socket->send_tcp_packet(TCPFlags::ACK); socket->set_state(TCPSocket::State::CloseWait); socket->set_connected(false); return; @@ -565,7 +566,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet) if (payload_size) { if (socket->did_receive(ipv4_packet.source(), tcp_packet.source_port(), KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()))) - socket->send_tcp_packet(TCPFlags::ACK); + (void)socket->send_tcp_packet(TCPFlags::ACK); } } } diff --git a/Kernel/Net/Socket.cpp b/Kernel/Net/Socket.cpp index 061068e3ecf99b..1caa3c53282b19 100644 --- a/Kernel/Net/Socket.cpp +++ b/Kernel/Net/Socket.cpp @@ -108,18 +108,20 @@ KResult Socket::setsockopt(int level, int option, Userspace user_va case SO_SNDTIMEO: if (user_value_size != sizeof(timeval)) return KResult(-EINVAL); - copy_from_user(&m_send_timeout, static_ptr_cast(user_value)); + if (!copy_from_user(&m_send_timeout, static_ptr_cast(user_value))) + return KResult(-EFAULT); return KSuccess; case SO_RCVTIMEO: if (user_value_size != sizeof(timeval)) return KResult(-EINVAL); - copy_from_user(&m_receive_timeout, static_ptr_cast(user_value)); + if (!copy_from_user(&m_receive_timeout, static_ptr_cast(user_value))) + return KResult(-EFAULT); return KSuccess; case SO_BINDTODEVICE: { if (user_value_size != IFNAMSIZ) return KResult(-EINVAL); auto user_string = static_ptr_cast(user_value); - auto ifname = Process::current()->validate_and_copy_string_from_user(user_string, user_value_size); + auto ifname = copy_string_from_user(user_string, user_value_size); if (ifname.is_null()) return KResult(-EFAULT); auto device = NetworkAdapter::lookup_by_name(ifname); @@ -140,7 +142,7 @@ KResult Socket::setsockopt(int level, int option, Userspace user_va KResult Socket::getsockopt(FileDescription&, int level, int option, Userspace value, Userspace value_size) { socklen_t size; - if (!Process::current()->validate_read_and_copy_typed(&size, value_size)) + if (!copy_from_user(&size, value_size.unsafe_userspace_ptr())) return KResult(-EFAULT); ASSERT(level == SOL_SOCKET); @@ -148,25 +150,31 @@ KResult Socket::getsockopt(FileDescription&, int level, int option, Userspace(value), &m_send_timeout); + if (!copy_to_user(static_ptr_cast(value), &m_send_timeout)) + return KResult(-EFAULT); size = sizeof(timeval); - copy_to_user(value_size, &size); + if (!copy_to_user(value_size, &size)) + return KResult(-EFAULT); return KSuccess; case SO_RCVTIMEO: if (size < sizeof(timeval)) return KResult(-EINVAL); - copy_to_user(static_ptr_cast(value), &m_receive_timeout); + if (!copy_to_user(static_ptr_cast(value), &m_receive_timeout)) + return KResult(-EFAULT); size = sizeof(timeval); - copy_to_user(value_size, &size); + if (!copy_to_user(value_size, &size)) + return KResult(-EFAULT); return KSuccess; case SO_ERROR: { if (size < sizeof(int)) return KResult(-EINVAL); dbg() << "getsockopt(SO_ERROR): FIXME!"; int errno = 0; - copy_to_user(static_ptr_cast(value), &errno); + if (!copy_to_user(static_ptr_cast(value), &errno)) + return KResult(-EFAULT); size = sizeof(int); - copy_to_user(value_size, &size); + if (!copy_to_user(value_size, &size)) + return KResult(-EFAULT); return KSuccess; } case SO_BINDTODEVICE: @@ -175,13 +183,16 @@ KResult Socket::getsockopt(FileDescription&, int level, int option, Userspacename(); auto length = name.length() + 1; - copy_to_user(static_ptr_cast(value), name.characters(), length); + if (!copy_to_user(static_ptr_cast(value), name.characters(), length)) + return KResult(-EFAULT); size = length; - copy_to_user(value_size, &size); + if (!copy_to_user(value_size, &size)) + return KResult(-EFAULT); return KSuccess; } else { size = 0; - copy_to_user(value_size, &size); + if (!copy_to_user(value_size, &size)) + return KResult(-EFAULT); return KResult(-EFAULT); } @@ -191,14 +202,14 @@ KResult Socket::getsockopt(FileDescription&, int level, int option, Userspace Socket::read(FileDescription& description, size_t, u8* buffer, size_t size) +KResultOr Socket::read(FileDescription& description, size_t, UserOrKernelBuffer& buffer, size_t size) { if (is_shut_down_for_reading()) return 0; return recvfrom(description, buffer, size, 0, {}, 0); } -KResultOr Socket::write(FileDescription& description, size_t, const u8* data, size_t size) +KResultOr Socket::write(FileDescription& description, size_t, const UserOrKernelBuffer& data, size_t size) { if (is_shut_down_for_writing()) return -EPIPE; diff --git a/Kernel/Net/Socket.h b/Kernel/Net/Socket.h index b72fa5f837b45c..c7687932926c97 100644 --- a/Kernel/Net/Socket.h +++ b/Kernel/Net/Socket.h @@ -99,7 +99,7 @@ class Socket : public File { KResult shutdown(int how); virtual KResult bind(Userspace, socklen_t) = 0; - virtual KResult connect(FileDescription&, const sockaddr*, socklen_t, ShouldBlock) = 0; + virtual KResult connect(FileDescription&, Userspace, socklen_t, ShouldBlock) = 0; virtual KResult listen(size_t) = 0; virtual void get_local_address(sockaddr*, socklen_t*) = 0; virtual void get_peer_address(sockaddr*, socklen_t*) = 0; @@ -107,8 +107,8 @@ class Socket : public File { virtual bool is_ipv4() const { return false; } virtual void attach(FileDescription&) = 0; virtual void detach(FileDescription&) = 0; - virtual KResultOr sendto(FileDescription&, const void*, size_t, int flags, Userspace, socklen_t) = 0; - virtual KResultOr recvfrom(FileDescription&, void*, size_t, int flags, Userspace, Userspace) = 0; + virtual KResultOr sendto(FileDescription&, const UserOrKernelBuffer&, size_t, int flags, Userspace, socklen_t) = 0; + virtual KResultOr recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace, Userspace) = 0; virtual KResult setsockopt(int level, int option, Userspace, socklen_t); virtual KResult getsockopt(FileDescription&, int level, int option, Userspace, Userspace); @@ -124,8 +124,8 @@ class Socket : public File { Lock& lock() { return m_lock; } // ^File - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override final; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override final; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override final; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override final; virtual KResult stat(::stat&) const override; virtual String absolute_path(const FileDescription&) const override = 0; diff --git a/Kernel/Net/TCPSocket.cpp b/Kernel/Net/TCPSocket.cpp index b7b615c9515b47..e14ecf277023d9 100644 --- a/Kernel/Net/TCPSocket.cpp +++ b/Kernel/Net/TCPSocket.cpp @@ -161,7 +161,7 @@ NonnullRefPtr TCPSocket::create(int protocol) return adopt(*new TCPSocket(protocol)); } -KResultOr TCPSocket::protocol_receive(const KBuffer& packet_buffer, void* buffer, size_t buffer_size, int flags) +KResultOr TCPSocket::protocol_receive(const KBuffer& packet_buffer, UserOrKernelBuffer& buffer, size_t buffer_size, int flags) { (void)flags; auto& ipv4_packet = *(const IPv4Packet*)(packet_buffer.data()); @@ -171,17 +171,20 @@ KResultOr TCPSocket::protocol_receive(const KBuffer& packet_buffer, void klog() << "payload_size " << payload_size << ", will it fit in " << buffer_size << "?"; #endif ASSERT(buffer_size >= payload_size); - memcpy(buffer, tcp_packet.payload(), payload_size); + if (!buffer.write(tcp_packet.payload(), payload_size)) + return KResult(-EFAULT); return payload_size; } -KResultOr TCPSocket::protocol_send(const void* data, size_t data_length) +KResultOr TCPSocket::protocol_send(const UserOrKernelBuffer& data, size_t data_length) { - send_tcp_packet(TCPFlags::PUSH | TCPFlags::ACK, data, data_length); + int err = send_tcp_packet(TCPFlags::PUSH | TCPFlags::ACK, &data, data_length); + if (err < 0) + return KResult(err); return data_length; } -void TCPSocket::send_tcp_packet(u16 flags, const void* payload, size_t payload_size) +int TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload, size_t payload_size) { auto buffer = ByteBuffer::create_zeroed(sizeof(TCPPacket) + payload_size); auto& tcp_packet = *(TCPPacket*)(buffer.data()); @@ -196,31 +199,37 @@ void TCPSocket::send_tcp_packet(u16 flags, const void* payload, size_t payload_s if (flags & TCPFlags::ACK) tcp_packet.set_ack_number(m_ack_number); + if (payload && !payload->read(tcp_packet.payload(), payload_size)) + return -EFAULT; + if (flags & TCPFlags::SYN) { ++m_sequence_number; } else { m_sequence_number += payload_size; } - memcpy(tcp_packet.payload(), payload, payload_size); tcp_packet.set_checksum(compute_tcp_checksum(local_address(), peer_address(), tcp_packet, payload_size)); if (tcp_packet.has_syn() || payload_size > 0) { LOCKER(m_not_acked_lock); m_not_acked.append({ m_sequence_number, move(buffer) }); send_outgoing_packets(); - return; + return 0; } auto routing_decision = route_to(peer_address(), local_address(), bound_interface()); ASSERT(!routing_decision.is_zero()); - routing_decision.adapter->send_ipv4( + auto packet_buffer = UserOrKernelBuffer::for_kernel_buffer(buffer.data()); + int err = routing_decision.adapter->send_ipv4( routing_decision.next_hop, peer_address(), IPv4Protocol::TCP, - buffer, ttl()); + packet_buffer, buffer.size(), ttl()); + if (err < 0) + return err; m_packets_out++; m_bytes_out += buffer.size(); + return 0; } void TCPSocket::send_outgoing_packets() @@ -243,12 +252,17 @@ void TCPSocket::send_outgoing_packets() auto& tcp_packet = *(TCPPacket*)(packet.buffer.data()); klog() << "sending tcp packet from " << local_address().to_string().characters() << ":" << local_port() << " to " << peer_address().to_string().characters() << ":" << peer_port() << " with (" << (tcp_packet.has_syn() ? "SYN " : "") << (tcp_packet.has_ack() ? "ACK " : "") << (tcp_packet.has_fin() ? "FIN " : "") << (tcp_packet.has_rst() ? "RST " : "") << ") seq_no=" << tcp_packet.sequence_number() << ", ack_no=" << tcp_packet.ack_number() << ", tx_counter=" << packet.tx_counter; #endif - routing_decision.adapter->send_ipv4( + auto packet_buffer = UserOrKernelBuffer::for_kernel_buffer(packet.buffer.data()); + int err = routing_decision.adapter->send_ipv4( routing_decision.next_hop, peer_address(), IPv4Protocol::TCP, - packet.buffer, ttl()); - - m_packets_out++; - m_bytes_out += packet.buffer.size(); + packet_buffer, packet.buffer.size(), ttl()); + if (err < 0) { + auto& tcp_packet = *(TCPPacket*)(packet.buffer.data()); + klog() << "Error (" << err << ") sending tcp packet from " << local_address().to_string().characters() << ":" << local_port() << " to " << peer_address().to_string().characters() << ":" << peer_port() << " with (" << (tcp_packet.has_syn() ? "SYN " : "") << (tcp_packet.has_ack() ? "ACK " : "") << (tcp_packet.has_fin() ? "FIN " : "") << (tcp_packet.has_rst() ? "RST " : "") << ") seq_no=" << tcp_packet.sequence_number() << ", ack_no=" << tcp_packet.ack_number() << ", tx_counter=" << packet.tx_counter; + } else { + m_packets_out++; + m_bytes_out += packet.buffer.size(); + } } } @@ -366,7 +380,9 @@ KResult TCPSocket::protocol_connect(FileDescription& description, ShouldBlock sh m_ack_number = 0; set_setup_state(SetupState::InProgress); - send_tcp_packet(TCPFlags::SYN); + int err = send_tcp_packet(TCPFlags::SYN); + if (err < 0) + return KResult(err); m_state = State::SynSent; m_role = Role::Connecting; m_direction = Direction::Outgoing; @@ -433,7 +449,7 @@ void TCPSocket::shut_down_for_writing() #ifdef TCP_SOCKET_DEBUG dbg() << " Sending FIN/ACK from Established and moving into FinWait1"; #endif - send_tcp_packet(TCPFlags::FIN | TCPFlags::ACK); + (void)send_tcp_packet(TCPFlags::FIN | TCPFlags::ACK); set_state(State::FinWait1); } else { dbg() << " Shutting down TCPSocket for writing but not moving to FinWait1 since state is " << to_string(state()); @@ -447,7 +463,7 @@ KResult TCPSocket::close() #ifdef TCP_SOCKET_DEBUG dbg() << " Sending FIN from CloseWait and moving into LastAck"; #endif - send_tcp_packet(TCPFlags::FIN | TCPFlags::ACK); + (void)send_tcp_packet(TCPFlags::FIN | TCPFlags::ACK); set_state(State::LastAck); } diff --git a/Kernel/Net/TCPSocket.h b/Kernel/Net/TCPSocket.h index bff5ac14c272af..0a8e25119d2be4 100644 --- a/Kernel/Net/TCPSocket.h +++ b/Kernel/Net/TCPSocket.h @@ -148,7 +148,7 @@ class TCPSocket final : public IPv4Socket { u32 packets_out() const { return m_packets_out; } u32 bytes_out() const { return m_bytes_out; } - void send_tcp_packet(u16 flags, const void* = nullptr, size_t = 0); + [[nodiscard]] int send_tcp_packet(u16 flags, const UserOrKernelBuffer* = nullptr, size_t = 0); void send_outgoing_packets(); void receive_tcp_packet(const TCPPacket&, u16 size); @@ -177,8 +177,8 @@ class TCPSocket final : public IPv4Socket { virtual void shut_down_for_writing() override; - virtual KResultOr protocol_receive(const KBuffer&, void* buffer, size_t buffer_size, int flags) override; - virtual KResultOr protocol_send(const void*, size_t) override; + virtual KResultOr protocol_receive(const KBuffer&, UserOrKernelBuffer& buffer, size_t buffer_size, int flags) override; + virtual KResultOr protocol_send(const UserOrKernelBuffer&, size_t) override; virtual KResult protocol_connect(FileDescription&, ShouldBlock) override; virtual int protocol_allocate_local_port() override; virtual bool protocol_is_disconnected() const override; diff --git a/Kernel/Net/UDPSocket.cpp b/Kernel/Net/UDPSocket.cpp index c770a4e1844c41..22db799af7c91b 100644 --- a/Kernel/Net/UDPSocket.cpp +++ b/Kernel/Net/UDPSocket.cpp @@ -79,18 +79,19 @@ NonnullRefPtr UDPSocket::create(int protocol) return adopt(*new UDPSocket(protocol)); } -KResultOr UDPSocket::protocol_receive(const KBuffer& packet_buffer, void* buffer, size_t buffer_size, int flags) +KResultOr UDPSocket::protocol_receive(const KBuffer& packet_buffer, UserOrKernelBuffer& buffer, size_t buffer_size, int flags) { (void)flags; auto& ipv4_packet = *(const IPv4Packet*)(packet_buffer.data()); auto& udp_packet = *static_cast(ipv4_packet.payload()); ASSERT(udp_packet.length() >= sizeof(UDPPacket)); // FIXME: This should be rejected earlier. ASSERT(buffer_size >= (udp_packet.length() - sizeof(UDPPacket))); - memcpy(buffer, udp_packet.payload(), udp_packet.length() - sizeof(UDPPacket)); + if (!buffer.write(udp_packet.payload(), udp_packet.length() - sizeof(UDPPacket))) + return KResult(-EFAULT); return udp_packet.length() - sizeof(UDPPacket); } -KResultOr UDPSocket::protocol_send(const void* data, size_t data_length) +KResultOr UDPSocket::protocol_send(const UserOrKernelBuffer& data, size_t data_length) { auto routing_decision = route_to(peer_address(), local_address(), bound_interface()); if (routing_decision.is_zero()) @@ -100,9 +101,11 @@ KResultOr UDPSocket::protocol_send(const void* data, size_t data_length) udp_packet.set_source_port(local_port()); udp_packet.set_destination_port(peer_port()); udp_packet.set_length(sizeof(UDPPacket) + data_length); - memcpy(udp_packet.payload(), data, data_length); + if (!data.read(udp_packet.payload(), data_length)) + return KResult(-EFAULT); klog() << "sending as udp packet from " << routing_decision.adapter->ipv4_address().to_string().characters() << ":" << local_port() << " to " << peer_address().to_string().characters() << ":" << peer_port() << "!"; - routing_decision.adapter->send_ipv4(routing_decision.next_hop, peer_address(), IPv4Protocol::UDP, buffer, ttl()); + auto udp_packet_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&udp_packet); + routing_decision.adapter->send_ipv4(routing_decision.next_hop, peer_address(), IPv4Protocol::UDP, udp_packet_buffer, buffer.size(), ttl()); return data_length; } diff --git a/Kernel/Net/UDPSocket.h b/Kernel/Net/UDPSocket.h index ab711a9dc81ed2..d640be316e90cc 100644 --- a/Kernel/Net/UDPSocket.h +++ b/Kernel/Net/UDPSocket.h @@ -43,8 +43,8 @@ class UDPSocket final : public IPv4Socket { virtual const char* class_name() const override { return "UDPSocket"; } static Lockable>& sockets_by_port(); - virtual KResultOr protocol_receive(const KBuffer&, void* buffer, size_t buffer_size, int flags) override; - virtual KResultOr protocol_send(const void*, size_t) override; + virtual KResultOr protocol_receive(const KBuffer&, UserOrKernelBuffer& buffer, size_t buffer_size, int flags) override; + virtual KResultOr protocol_send(const UserOrKernelBuffer&, size_t) override; virtual KResult protocol_connect(FileDescription&, ShouldBlock) override; virtual int protocol_allocate_local_port() override; virtual KResult protocol_bind() override; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 2fc9c11e6bf473..5e1348b07a47a5 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -520,24 +520,6 @@ int Process::fd_flags(int fd) const return -1; } -String Process::validate_and_copy_string_from_user(const char* user_characters, size_t user_length) const -{ - if (user_length == 0) - return String::empty(); - if (!user_characters) - return {}; - if (!validate_read(user_characters, user_length)) - return {}; - SmapDisabler disabler; - size_t measured_length = strnlen(user_characters, user_length); - return String(user_characters, measured_length); -} - -String Process::validate_and_copy_string_from_user(const Syscall::StringArgument& string) const -{ - return validate_and_copy_string_from_user(string.characters, string.length); -} - int Process::number_of_open_file_descriptors() const { int count = 0; @@ -602,27 +584,6 @@ siginfo_t Process::reap(Process& process) return siginfo; } -bool Process::validate_read_from_kernel(VirtualAddress vaddr, size_t size) const -{ - if (vaddr.is_null()) - return false; - return MM.validate_kernel_read(*this, vaddr, size); -} - -bool Process::validate_read(const void* address, size_t size) const -{ - if (!size) - return false; - return MM.validate_user_read(*this, VirtualAddress(address), size); -} - -bool Process::validate_write(void* address, size_t size) const -{ - if (!size) - return false; - return MM.validate_user_write(*this, VirtualAddress(address), size); -} - Custody& Process::current_directory() { if (!m_cwd) @@ -636,9 +597,10 @@ KResultOr Process::get_syscall_path_argument(const char* user_path, size return KResult(-EINVAL); if (path_length > PATH_MAX) return KResult(-ENAMETOOLONG); - if (!validate_read(user_path, path_length)) + auto copied_string = copy_string_from_user(user_path, path_length); + if (copied_string.is_null()) return KResult(-EFAULT); - return copy_string_from_user(user_path, path_length); + return copied_string; } KResultOr Process::get_syscall_path_argument(const Syscall::StringArgument& path) const @@ -659,7 +621,8 @@ void Process::finalize() auto& description = description_or_error.value(); auto json = m_perf_event_buffer->to_json(m_pid, m_executable ? m_executable->absolute_path() : ""); // FIXME: Should this error path be surfaced somehow? - (void)description->write(json.data(), json.size()); + auto json_buffer = UserOrKernelBuffer::for_kernel_buffer(json.data()); + (void)description->write(json_buffer, json.size()); } } diff --git a/Kernel/Process.h b/Kernel/Process.h index 6f19527c0a8e58..76bc8c51d7651f 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -362,111 +362,6 @@ class Process u32 m_ticks_in_user_for_dead_children { 0 }; u32 m_ticks_in_kernel_for_dead_children { 0 }; - [[nodiscard]] bool validate_read_from_kernel(VirtualAddress, size_t) const; - - [[nodiscard]] bool validate_read(const void*, size_t) const; - [[nodiscard]] bool validate_write(void*, size_t) const; - - template - [[nodiscard]] bool validate_read(Userspace ptr, size_t size) const - { - return validate_read(ptr.unsafe_userspace_ptr(), size); - } - - template - [[nodiscard]] bool validate_write(Userspace ptr, size_t size) const - { - return validate_write(ptr.unsafe_userspace_ptr(), size); - } - - template - [[nodiscard]] bool validate_read_typed(T* value, size_t count = 1) - { - Checked size = sizeof(T); - size *= count; - if (size.has_overflow()) - return false; - return validate_read(value, size.value()); - } - - template - [[nodiscard]] bool validate_read_typed(Userspace value, size_t count = 1) - { - Checked size = sizeof(T); - size *= count; - if (size.has_overflow()) - return false; - return validate_read(value, size.value()); - } - - template - [[nodiscard]] bool validate_read_and_copy_typed(T* dest, const T* src) - { - bool validated = validate_read_typed(src); - if (validated) { - copy_from_user(dest, src); - } - return validated; - } - - template - [[nodiscard]] bool validate_read_and_copy_typed(T* dest, Userspace src) - { - bool validated = validate_read_typed(src); - if (validated) { - copy_from_user(dest, src); - } - return validated; - } - - template - [[nodiscard]] bool validate_read_and_copy_typed(T* dest, Userspace src) - { - Userspace const_src { src.ptr() }; - return validate_read_and_copy_typed(dest, const_src); - } - - template - [[nodiscard]] bool validate_write_typed(T* value, size_t count = 1) - { - Checked size = sizeof(T); - size *= count; - if (size.has_overflow()) - return false; - return validate_write(value, size.value()); - } - - template - [[nodiscard]] bool validate_write_typed(Userspace value, size_t count = 1) - { - Checked size = sizeof(T); - size *= count; - if (size.has_overflow()) - return false; - return validate_write(value, size.value()); - } - - template - [[nodiscard]] bool validate(const Syscall::MutableBufferArgument& buffer) - { - return validate_write(buffer.data, buffer.size); - } - - template - [[nodiscard]] bool validate(const Syscall::ImmutableBufferArgument& buffer) - { - return validate_read(buffer.data, buffer.size); - } - - [[nodiscard]] String validate_and_copy_string_from_user(const char*, size_t) const; - - [[nodiscard]] String validate_and_copy_string_from_user(Userspace user_characters, size_t size) const - { - return validate_and_copy_string_from_user(user_characters.unsafe_userspace_ptr(), size); - } - - [[nodiscard]] String validate_and_copy_string_from_user(const Syscall::StringArgument&) const; - Custody& current_directory(); Custody* executable() { @@ -582,7 +477,7 @@ class Process void kill_all_threads(); int do_exec(NonnullRefPtr main_program_description, Vector arguments, Vector environment, RefPtr interpreter_description, Thread*& new_main_thread, u32& prev_flags); - ssize_t do_write(FileDescription&, const u8*, int data_size); + ssize_t do_write(FileDescription&, const UserOrKernelBuffer&, size_t); KResultOr> find_elf_interpreter_for_executable(const String& path, char (&first_page)[PAGE_SIZE], int nread, size_t file_size); Vector generate_auxiliary_vector() const; @@ -834,3 +729,8 @@ inline u32 Thread::effective_priority() const } while (0) } + +inline static String copy_string_from_user(const Kernel::Syscall::StringArgument& string) +{ + return copy_string_from_user(string.characters, string.length); +} diff --git a/Kernel/Ptrace.cpp b/Kernel/Ptrace.cpp index ab085c3054047e..8e8bc6c7bf039c 100644 --- a/Kernel/Ptrace.cpp +++ b/Kernel/Ptrace.cpp @@ -105,10 +105,9 @@ KResultOr handle_syscall(const Kernel::Syscall::SC_ptrace_params& params, P if (!tracer->has_regs()) return KResult(-EINVAL); - auto* regs = reinterpret_cast(params.addr.unsafe_userspace_ptr()); - if (!caller.validate_write_typed(regs)) + auto* regs = reinterpret_cast(params.addr); + if (!copy_to_user(regs, &tracer->regs())) return KResult(-EFAULT); - copy_to_user(regs, &tracer->regs()); break; } @@ -117,7 +116,7 @@ KResultOr handle_syscall(const Kernel::Syscall::SC_ptrace_params& params, P return KResult(-EINVAL); PtraceRegisters regs; - if (!caller.validate_read_and_copy_typed(®s, (const PtraceRegisters*)params.addr.unsafe_userspace_ptr())) + if (!copy_from_user(®s, (const PtraceRegisters*)params.addr)) return KResult(-EFAULT); auto& peer_saved_registers = peer->get_register_dump_from_stack(); @@ -132,21 +131,20 @@ KResultOr handle_syscall(const Kernel::Syscall::SC_ptrace_params& params, P case PT_PEEK: { Kernel::Syscall::SC_ptrace_peek_params peek_params; - if (!caller.validate_read_and_copy_typed(&peek_params, reinterpret_cast(params.addr.unsafe_userspace_ptr()))) + if (!copy_from_user(&peek_params, reinterpret_cast(params.addr))) return -EFAULT; // read validation is done inside 'peek_user_data' - auto result = peer->process().peek_user_data(peek_params.address); + auto result = peer->process().peek_user_data((FlatPtr)peek_params.address); if (result.is_error()) return -EFAULT; - if (!caller.validate_write(peek_params.out_data, sizeof(u32))) + if (!copy_to_user(peek_params.out_data, &result.value())) return -EFAULT; - copy_to_user(peek_params.out_data, &result.value()); break; } case PT_POKE: { - Userspace addr = reinterpret_cast(params.addr.ptr()); + Userspace addr = reinterpret_cast(params.addr); // write validation is done inside 'poke_user_data' return peer->process().poke_user_data(addr, params.data); } diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 620558caf7d827..2a1e6e82a7e762 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -651,7 +651,7 @@ void Scheduler::initialize() ASSERT(s_colonel_process); ASSERT(idle_thread); idle_thread->set_priority(THREAD_PRIORITY_MIN); - idle_thread->set_name("idle thread #0"); + idle_thread->set_name(StringView("idle thread #0")); set_idle_thread(idle_thread); } diff --git a/Kernel/StdLib.cpp b/Kernel/StdLib.cpp index 27dee8d3c43824..1bdb4c912b1be2 100644 --- a/Kernel/StdLib.cpp +++ b/Kernel/StdLib.cpp @@ -35,27 +35,68 @@ String copy_string_from_user(const char* user_str, size_t user_str_size) { + bool is_user = Kernel::is_user_range(VirtualAddress(user_str), user_str_size); + ASSERT(is_user); // For now assert to catch bugs, but technically not an error + if (!is_user) + return {}; Kernel::SmapDisabler disabler; - size_t length = strnlen(user_str, user_str_size); - return String(user_str, length); + void* fault_at; + ssize_t length = Kernel::safe_strnlen(user_str, user_str_size, fault_at); + if (length < 0) { + klog() << "copy_string_from_user(" << user_str << ", " << user_str_size << ") failed at " << VirtualAddress(fault_at) << " (strnlen)"; + return {}; + } + if (length == 0) + return String::empty(); + + char* buffer; + auto copied_string = StringImpl::create_uninitialized((size_t)length, buffer); + if (!Kernel::safe_memcpy(buffer, user_str, (size_t)length, fault_at)) { + klog() << "copy_string_from_user(" << user_str << ", " << user_str_size << ") failed at " << VirtualAddress(fault_at) << " (memcpy)"; + return {}; + } + return copied_string; +} + +String copy_string_from_user(Userspace user_str, size_t user_str_size) +{ + return copy_string_from_user(user_str.unsafe_userspace_ptr(), user_str_size); } extern "C" { -void copy_to_user(void* dest_ptr, const void* src_ptr, size_t n) +bool copy_to_user(void* dest_ptr, const void* src_ptr, size_t n) { - ASSERT(Kernel::is_user_range(VirtualAddress(dest_ptr), n)); + bool is_user = Kernel::is_user_range(VirtualAddress(dest_ptr), n); + ASSERT(is_user); // For now assert to catch bugs, but technically not an error + if (!is_user) + return false; ASSERT(!Kernel::is_user_range(VirtualAddress(src_ptr), n)); Kernel::SmapDisabler disabler; - memcpy(dest_ptr, src_ptr, n); + void* fault_at; + if (!Kernel::safe_memcpy(dest_ptr, src_ptr, n, fault_at)) { + ASSERT(VirtualAddress(fault_at) >= VirtualAddress(dest_ptr) && VirtualAddress(fault_at) <= VirtualAddress((FlatPtr)dest_ptr + n)); + klog() << "copy_to_user(" << dest_ptr << ", " << src_ptr << ", " << n << ") failed at " << VirtualAddress(fault_at); + return false; + } + return true; } -void copy_from_user(void* dest_ptr, const void* src_ptr, size_t n) +bool copy_from_user(void* dest_ptr, const void* src_ptr, size_t n) { - ASSERT(Kernel::is_user_range(VirtualAddress(src_ptr), n)); + bool is_user = Kernel::is_user_range(VirtualAddress(src_ptr), n); + ASSERT(is_user); // For now assert to catch bugs, but technically not an error + if (!is_user) + return false; ASSERT(!Kernel::is_user_range(VirtualAddress(dest_ptr), n)); Kernel::SmapDisabler disabler; - memcpy(dest_ptr, src_ptr, n); + void* fault_at; + if (!Kernel::safe_memcpy(dest_ptr, src_ptr, n, fault_at)) { + ASSERT(VirtualAddress(fault_at) >= VirtualAddress(src_ptr) && VirtualAddress(fault_at) <= VirtualAddress((FlatPtr)src_ptr + n)); + klog() << "copy_from_user(" << dest_ptr << ", " << src_ptr << ", " << n << ") failed at " << VirtualAddress(fault_at); + return false; + } + return true; } void* memcpy(void* dest_ptr, const void* src_ptr, size_t n) @@ -97,11 +138,19 @@ const void* memmem(const void* haystack, size_t haystack_length, const void* nee return AK::memmem(haystack, haystack_length, needle, needle_length); } -void memset_user(void* dest_ptr, int c, size_t n) +[[nodiscard]] bool memset_user(void* dest_ptr, int c, size_t n) { - ASSERT(Kernel::is_user_range(VirtualAddress(dest_ptr), n)); + bool is_user = Kernel::is_user_range(VirtualAddress(dest_ptr), n); + ASSERT(is_user); // For now assert to catch bugs, but technically not an error + if (!is_user) + return false; Kernel::SmapDisabler disabler; - memset(dest_ptr, c, n); + void* fault_at; + if (!Kernel::safe_memset(dest_ptr, c, n, fault_at)) { + klog() << "memset(" << dest_ptr << ", " << n << ") failed at " << VirtualAddress(fault_at); + return false; + } + return true; } void* memset(void* dest_ptr, int c, size_t n) diff --git a/Kernel/StdLib.h b/Kernel/StdLib.h index 1a9398700f2122..00b891298749d9 100644 --- a/Kernel/StdLib.h +++ b/Kernel/StdLib.h @@ -26,6 +26,7 @@ #pragma once +#include #include #include @@ -34,12 +35,13 @@ struct StringArgument; } String copy_string_from_user(const char*, size_t); +String copy_string_from_user(Userspace, size_t); extern "C" { -void copy_to_user(void*, const void*, size_t); -void copy_from_user(void*, const void*, size_t); -void memset_user(void*, int, size_t); +[[nodiscard]] bool copy_to_user(void*, const void*, size_t); +[[nodiscard]] bool copy_from_user(void*, const void*, size_t); +[[nodiscard]] bool memset_user(void*, int, size_t); void* memcpy(void*, const void*, size_t); int strncmp(const char* s1, const char* s2, size_t n); @@ -57,37 +59,77 @@ inline u16 htons(u16 w) { return (w & 0xff) << 8 | ((w >> 8) & 0xff); } } template -inline void copy_from_user(T* dest, const T* src) +[[nodiscard]] inline bool copy_from_user(T* dest, const T* src) { - copy_from_user(dest, src, sizeof(T)); + return copy_from_user(dest, src, sizeof(T)); } template -inline void copy_to_user(T* dest, const T* src) +[[nodiscard]] inline bool copy_to_user(T* dest, const T* src) { - copy_to_user(dest, src, sizeof(T)); + return copy_to_user(dest, src, sizeof(T)); } template -inline void copy_from_user(T* dest, Userspace src) +[[nodiscard]] inline bool copy_from_user(T* dest, Userspace src) { - copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T)); + return copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T)); } template -inline void copy_to_user(Userspace dest, const T* src) +[[nodiscard]] inline bool copy_to_user(Userspace dest, const T* src) { - copy_to_user(dest.unsafe_userspace_ptr(), src, sizeof(T)); + return copy_to_user(dest.unsafe_userspace_ptr(), src, sizeof(T)); } template -inline void copy_to_user(Userspace dest, const void* src, size_t size) +[[nodiscard]] inline bool copy_to_user(Userspace dest, const void* src, size_t size) { - copy_to_user(dest.unsafe_userspace_ptr(), src, size); + return copy_to_user(dest.unsafe_userspace_ptr(), src, size); } template -inline void copy_from_user(void* dest, Userspace src, size_t size) +[[nodiscard]] inline bool copy_from_user(void* dest, Userspace src, size_t size) { - copy_from_user(dest, src.unsafe_userspace_ptr(), size); + return copy_from_user(dest, src.unsafe_userspace_ptr(), size); +} + +template +[[nodiscard]] inline bool copy_n_from_user(T* dest, const T* src, size_t count) +{ + Checked size = sizeof(T); + size *= count; + if (size.has_overflow()) + return false; + return copy_from_user(dest, src, sizeof(T) * count); +} + +template +[[nodiscard]] inline bool copy_n_to_user(T* dest, const T* src, size_t count) +{ + Checked size = sizeof(T); + size *= count; + if (size.has_overflow()) + return false; + return copy_to_user(dest, src, sizeof(T) * count); +} + +template +[[nodiscard]] inline bool copy_n_from_user(T* dest, Userspace src, size_t count) +{ + Checked size = sizeof(T); + size *= count; + if (size.has_overflow()) + return false; + return copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T) * count); +} + +template +[[nodiscard]] inline bool copy_n_to_user(Userspace dest, const T* src, size_t count) +{ + Checked size = sizeof(T); + size *= count; + if (size.has_overflow()) + return false; + return copy_to_user(dest.unsafe_userspace_ptr(), src, sizeof(T) * count); } diff --git a/Kernel/Syscalls/chdir.cpp b/Kernel/Syscalls/chdir.cpp index 68437ab0a9b501..46cf3a2459a3ad 100644 --- a/Kernel/Syscalls/chdir.cpp +++ b/Kernel/Syscalls/chdir.cpp @@ -66,12 +66,11 @@ int Process::sys$getcwd(Userspace 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; } diff --git a/Kernel/Syscalls/chown.cpp b/Kernel/Syscalls/chown.cpp index 8de435c4680d56..e07ab19f8ab675 100644 --- a/Kernel/Syscalls/chown.cpp +++ b/Kernel/Syscalls/chown.cpp @@ -42,7 +42,7 @@ int Process::sys$chown(Userspace user_params) { REQUIRE_PROMISE(chown); Syscall::SC_chown_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; auto path = get_syscall_path_argument(params.path); if (path.is_error()) diff --git a/Kernel/Syscalls/clock.cpp b/Kernel/Syscalls/clock.cpp index de83b63035ef4a..3ee0c85339e12a 100644 --- a/Kernel/Syscalls/clock.cpp +++ b/Kernel/Syscalls/clock.cpp @@ -32,8 +32,6 @@ namespace Kernel { int Process::sys$clock_gettime(clockid_t clock_id, Userspace 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 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 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 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 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; } diff --git a/Kernel/Syscalls/debug.cpp b/Kernel/Syscalls/debug.cpp index bd22e8b4c276e7..8bafe6399d6638 100644 --- a/Kernel/Syscalls/debug.cpp +++ b/Kernel/Syscalls/debug.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace Kernel { @@ -44,13 +45,20 @@ int Process::sys$dbgputch(u8 ch) int Process::sys$dbgputstr(Userspace 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; } diff --git a/Kernel/Syscalls/execve.cpp b/Kernel/Syscalls/execve.cpp index bb0012a8e1e09c..0e95a4c29e06ce 100644 --- a/Kernel/Syscalls/execve.cpp +++ b/Kernel/Syscalls/execve.cpp @@ -423,7 +423,8 @@ KResultOr> 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 arguments, Vector 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 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(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; if (params.arguments.length > ARG_MAX || params.environment.length > ARG_MAX) @@ -574,13 +576,16 @@ int Process::sys$execve(Userspace 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 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)); diff --git a/Kernel/Syscalls/futex.cpp b/Kernel/Syscalls/futex.cpp index 830579934c679f..cd0f8fd21ff598 100644 --- a/Kernel/Syscalls/futex.cpp +++ b/Kernel/Syscalls/futex.cpp @@ -55,21 +55,19 @@ int Process::sys$futex(Userspace user_params) REQUIRE_PROMISE(thread); Syscall::SC_futex_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) - return -EFAULT; - - if (!validate_read_typed(params.userspace_address)) + if (!copy_from_user(¶ms, 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 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 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; } diff --git a/Kernel/Syscalls/get_dir_entries.cpp b/Kernel/Syscalls/get_dir_entries.cpp index 56d98cd9f01881..a94bf584d35554 100644 --- a/Kernel/Syscalls/get_dir_entries.cpp +++ b/Kernel/Syscalls/get_dir_entries.cpp @@ -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); } } diff --git a/Kernel/Syscalls/get_stack_bounds.cpp b/Kernel/Syscalls/get_stack_bounds.cpp index e41bcb5ddb2e0b..a91a401946d1a1 100644 --- a/Kernel/Syscalls/get_stack_bounds.cpp +++ b/Kernel/Syscalls/get_stack_bounds.cpp @@ -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; } diff --git a/Kernel/Syscalls/getrandom.cpp b/Kernel/Syscalls/getrandom.cpp index 53361d92638167..c000c9cf87b2d2 100644 --- a/Kernel/Syscalls/getrandom.cpp +++ b/Kernel/Syscalls/getrandom.cpp @@ -26,6 +26,7 @@ #include #include +#include namespace Kernel { @@ -38,13 +39,15 @@ ssize_t Process::sys$getrandom(Userspace 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 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; } } diff --git a/Kernel/Syscalls/getuid.cpp b/Kernel/Syscalls/getuid.cpp index 699ad9b6ca7eae..73889c8ca61e60 100644 --- a/Kernel/Syscalls/getuid.cpp +++ b/Kernel/Syscalls/getuid.cpp @@ -55,22 +55,16 @@ gid_t Process::sys$getegid() int Process::sys$getresuid(Userspace ruid, Userspace euid, Userspace 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 rgid, Userspace egid, Userspace 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 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; } diff --git a/Kernel/Syscalls/hostname.cpp b/Kernel/Syscalls/hostname.cpp index 490c6a169dcfa2..fa5c992b52ac61 100644 --- a/Kernel/Syscalls/hostname.cpp +++ b/Kernel/Syscalls/hostname.cpp @@ -36,12 +36,11 @@ int Process::sys$gethostname(Userspace 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 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; } diff --git a/Kernel/Syscalls/link.cpp b/Kernel/Syscalls/link.cpp index cbb3185e035e2e..f3bf17ff3e23bf 100644 --- a/Kernel/Syscalls/link.cpp +++ b/Kernel/Syscalls/link.cpp @@ -34,11 +34,13 @@ int Process::sys$link(Userspace user_params) { REQUIRE_PROMISE(cpath); Syscall::SC_link_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, 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 user_param { REQUIRE_PROMISE(cpath); Syscall::SC_symlink_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; auto target = get_syscall_path_argument(params.target); if (target.is_error()) diff --git a/Kernel/Syscalls/mknod.cpp b/Kernel/Syscalls/mknod.cpp index 1a599b138d1815..1b6a4d9444a225 100644 --- a/Kernel/Syscalls/mknod.cpp +++ b/Kernel/Syscalls/mknod.cpp @@ -34,7 +34,7 @@ int Process::sys$mknod(Userspace user_params) { REQUIRE_PROMISE(dpath); Syscall::SC_mknod_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; if (!is_superuser() && !is_regular_file(params.mode) && !is_fifo(params.mode) && !is_socket(params.mode)) return -EPERM; diff --git a/Kernel/Syscalls/mmap.cpp b/Kernel/Syscalls/mmap.cpp index e99be2775a0330..74dbe31edf1a86 100644 --- a/Kernel/Syscalls/mmap.cpp +++ b/Kernel/Syscalls/mmap.cpp @@ -81,7 +81,7 @@ void* Process::sys$mmap(Userspace user_params) REQUIRE_PROMISE(stdio); Syscall::SC_mmap_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return (void*)-EFAULT; void* addr = (void*)params.addr; @@ -102,7 +102,7 @@ void* Process::sys$mmap(Userspace 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 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(Userspaceis_mmap()) return -EPERM; - region->set_name(name); + region->set_name(move(name)); return 0; } diff --git a/Kernel/Syscalls/module.cpp b/Kernel/Syscalls/module.cpp index 6da8ff8144cffc..f8d9e0ac9fa3bc 100644 --- a/Kernel/Syscalls/module.cpp +++ b/Kernel/Syscalls/module.cpp @@ -169,7 +169,7 @@ int Process::sys$module_unload(Userspace 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; diff --git a/Kernel/Syscalls/mount.cpp b/Kernel/Syscalls/mount.cpp index 68f9a3ba89d054..ca95f84636cb50 100644 --- a/Kernel/Syscalls/mount.cpp +++ b/Kernel/Syscalls/mount.cpp @@ -43,15 +43,16 @@ int Process::sys$mount(Userspace user_params) REQUIRE_NO_PROMISES; Syscall::SC_mount_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, 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 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(); diff --git a/Kernel/Syscalls/open.cpp b/Kernel/Syscalls/open.cpp index 0d8a3d5e484751..abcc413f01fcb2 100644 --- a/Kernel/Syscalls/open.cpp +++ b/Kernel/Syscalls/open.cpp @@ -34,7 +34,7 @@ namespace Kernel { int Process::sys$open(Userspace user_params) { Syscall::SC_open_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; int dirfd = params.dirfd; diff --git a/Kernel/Syscalls/pipe.cpp b/Kernel/Syscalls/pipe.cpp index 182c2898575ea6..3d82917c8fea79 100644 --- a/Kernel/Syscalls/pipe.cpp +++ b/Kernel/Syscalls/pipe.cpp @@ -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; } diff --git a/Kernel/Syscalls/pledge.cpp b/Kernel/Syscalls/pledge.cpp index 4cd934523584f8..e90e7eefea62ac 100644 --- a/Kernel/Syscalls/pledge.cpp +++ b/Kernel/Syscalls/pledge.cpp @@ -32,7 +32,7 @@ namespace Kernel { int Process::sys$pledge(Userspace user_params) { Syscall::SC_pledge_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; if (params.promises.length > 1024 || params.execpromises.length > 1024) @@ -40,14 +40,14 @@ int Process::sys$pledge(Userspace 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; } diff --git a/Kernel/Syscalls/process.cpp b/Kernel/Syscalls/process.cpp index 6a58ee33881249..2e166c1967eb84 100644 --- a/Kernel/Syscalls/process.cpp +++ b/Kernel/Syscalls/process.cpp @@ -58,13 +58,11 @@ int Process::sys$set_process_icon(int icon_id) int Process::sys$get_process_name(Userspace 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 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); diff --git a/Kernel/Syscalls/ptrace.cpp b/Kernel/Syscalls/ptrace.cpp index 2f19df305691e1..8ca71c9860de73 100644 --- a/Kernel/Syscalls/ptrace.cpp +++ b/Kernel/Syscalls/ptrace.cpp @@ -24,6 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -38,7 +39,7 @@ int Process::sys$ptrace(Userspace user_params) { REQUIRE_PROMISE(proc); Syscall::SC_ptrace_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, 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 Process::peek_user_data(Userspace 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 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 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); } diff --git a/Kernel/Syscalls/read.cpp b/Kernel/Syscalls/read.cpp index 82a774b75ba36d..df2873a8fe1b51 100644 --- a/Kernel/Syscalls/read.cpp +++ b/Kernel/Syscalls/read.cpp @@ -38,8 +38,6 @@ ssize_t Process::sys$read(int fd, Userspace 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 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(); diff --git a/Kernel/Syscalls/readlink.cpp b/Kernel/Syscalls/readlink.cpp index d2b35733a98d65..b9da03db878e0b 100644 --- a/Kernel/Syscalls/readlink.cpp +++ b/Kernel/Syscalls/readlink.cpp @@ -36,10 +36,7 @@ int Process::sys$readlink(Userspace user_par REQUIRE_PROMISE(rpath); Syscall::SC_readlink_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) - return -EFAULT; - - if (!validate(params.buffer)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; auto path = get_syscall_path_argument(params.path); @@ -60,7 +57,8 @@ int Process::sys$readlink(Userspace 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(); } diff --git a/Kernel/Syscalls/realpath.cpp b/Kernel/Syscalls/realpath.cpp index 4728115a72399a..83f6cba8396ac2 100644 --- a/Kernel/Syscalls/realpath.cpp +++ b/Kernel/Syscalls/realpath.cpp @@ -36,10 +36,7 @@ int Process::sys$realpath(Userspace user_par REQUIRE_PROMISE(rpath); Syscall::SC_realpath_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) - return -EFAULT; - - if (!validate_write(params.buffer.data, params.buffer.size)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; auto path = get_syscall_path_argument(params.path); @@ -55,7 +52,8 @@ int Process::sys$realpath(Userspace 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; }; diff --git a/Kernel/Syscalls/rename.cpp b/Kernel/Syscalls/rename.cpp index 0a06eaceb80114..ef9463c86099aa 100644 --- a/Kernel/Syscalls/rename.cpp +++ b/Kernel/Syscalls/rename.cpp @@ -34,7 +34,7 @@ int Process::sys$rename(Userspace user_params) { REQUIRE_PROMISE(cpath); Syscall::SC_rename_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; auto old_path = get_syscall_path_argument(params.old_path); if (old_path.is_error()) diff --git a/Kernel/Syscalls/sched.cpp b/Kernel/Syscalls/sched.cpp index 7729a817b01e2d..91ca08884ebf01 100644 --- a/Kernel/Syscalls/sched.cpp +++ b/Kernel/Syscalls/sched.cpp @@ -51,11 +51,9 @@ int Process::sys$donate(pid_t tid) int Process::sys$sched_setparam(int pid, Userspace 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 us int Process::sys$sched_getparam(pid_t pid, Userspace 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 user_p struct sched_param param { (int)peer->priority() }; - copy_to_user(user_param, ¶m); + if (!copy_to_user(user_param, ¶m)) + return -EFAULT; return 0; } diff --git a/Kernel/Syscalls/select.cpp b/Kernel/Syscalls/select.cpp index ccf2110a3ab8b1..c518ae7acb41b8 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(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 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 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 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 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; } diff --git a/Kernel/Syscalls/setkeymap.cpp b/Kernel/Syscalls/setkeymap.cpp index 992043513559cb..8f9b441dd200a4 100644 --- a/Kernel/Syscalls/setkeymap.cpp +++ b/Kernel/Syscalls/setkeymap.cpp @@ -37,25 +37,20 @@ int Process::sys$setkeymap(Userspace user_p return -EPERM; Syscall::SC_setkeymap_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, 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(); diff --git a/Kernel/Syscalls/setuid.cpp b/Kernel/Syscalls/setuid.cpp index 3cedc049ec72d6..8ea014039dcb89 100644 --- a/Kernel/Syscalls/setuid.cpp +++ b/Kernel/Syscalls/setuid.cpp @@ -125,8 +125,6 @@ int Process::sys$setgroups(ssize_t count, Userspace 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 user_gids) Vector 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 unique_extra_gids; for (auto& gid : gids) { diff --git a/Kernel/Syscalls/shbuf.cpp b/Kernel/Syscalls/shbuf.cpp index 8f925626420388..6d85c2d50af562 100644 --- a/Kernel/Syscalls/shbuf.cpp +++ b/Kernel/Syscalls/shbuf.cpp @@ -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 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 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); } diff --git a/Kernel/Syscalls/sigaction.cpp b/Kernel/Syscalls/sigaction.cpp index c735b34eca9185..a3151c6d1e7e89 100644 --- a/Kernel/Syscalls/sigaction.cpp +++ b/Kernel/Syscalls/sigaction.cpp @@ -34,10 +34,9 @@ int Process::sys$sigprocmask(int how, Userspace 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 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 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; } diff --git a/Kernel/Syscalls/socket.cpp b/Kernel/Syscalls/socket.cpp index a32d87e23d1faa..70d30765dc9087 100644 --- a/Kernel/Syscalls/socket.cpp +++ b/Kernel/Syscalls/socket.cpp @@ -65,8 +65,6 @@ int Process::sys$socket(int domain, int type, int protocol) int Process::sys$bind(int sockfd, Userspace 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 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(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 user_addre u8 address_buffer[sizeof(sockaddr_un)]; address_size = min(sizeof(sockaddr_un), static_cast(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 user_addre int Process::sys$connect(int sockfd, Userspace 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 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(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 user_par { REQUIRE_PROMISE(stdio); Syscall::SC_sendto_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; int flags = params.flags; - Userspace addr = params.addr; + Userspace 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 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((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 user REQUIRE_PROMISE(stdio); Syscall::SC_recvfrom_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; int flags = params.flags; - Userspace user_addr = params.addr; - Userspace user_addr_length = params.addr_length; + Userspace user_addr((FlatPtr)params.addr); + Userspace 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 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((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 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 user_params) { Syscall::SC_getsockname_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; return get_sock_or_peer_name(params); } @@ -315,7 +291,7 @@ int Process::sys$getsockname(Userspace us int Process::sys$getpeername(Userspace user_params) { Syscall::SC_getpeername_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; return get_sock_or_peer_name(params); } @@ -323,22 +299,19 @@ int Process::sys$getpeername(Userspace us int Process::sys$getsockopt(Userspace user_params) { Syscall::SC_getsockopt_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; int sockfd = params.sockfd; int level = params.level; int option = params.option; - Userspace user_value = params.value; - Userspace user_value_size = params.value_size; + Userspace user_value((FlatPtr)params.value); + Userspace 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 user int Process::sys$setsockopt(Userspace user_params) { Syscall::SC_setsockopt_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) - return -EFAULT; - if (!validate_read(params.value, params.value_size)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; + Userspace user_value((FlatPtr)params.value); auto description = file_description(params.sockfd); if (!description) return -EBADF; @@ -368,7 +340,7 @@ int Process::sys$setsockopt(Userspace 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); } } diff --git a/Kernel/Syscalls/stat.cpp b/Kernel/Syscalls/stat.cpp index a5ad5882c5e946..19cdb14f787fd2 100644 --- a/Kernel/Syscalls/stat.cpp +++ b/Kernel/Syscalls/stat.cpp @@ -34,14 +34,13 @@ namespace Kernel { int Process::sys$fstat(int fd, Userspace 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 user_params) { REQUIRE_PROMISE(rpath); Syscall::SC_stat_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) - return -EFAULT; - if (!validate_write_typed(params.statbuf)) + if (!copy_from_user(¶ms, 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 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; } diff --git a/Kernel/Syscalls/thread.cpp b/Kernel/Syscalls/thread.cpp index 39e59baff90e42..1f13f90ce3c59c 100644 --- a/Kernel/Syscalls/thread.cpp +++ b/Kernel/Syscalls/thread.cpp @@ -36,22 +36,16 @@ namespace Kernel { int Process::sys$create_thread(void* (*entry)(void*), Userspace 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(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; unsigned detach_state = params.m_detach_state; int schedule_priority = params.m_schedule_priority; - Userspace 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(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*), Userspacemake_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 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 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 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 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 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 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; } diff --git a/Kernel/Syscalls/times.cpp b/Kernel/Syscalls/times.cpp index 61355595cf1012..f769fee590a753 100644 --- a/Kernel/Syscalls/times.cpp +++ b/Kernel/Syscalls/times.cpp @@ -31,16 +31,14 @@ namespace Kernel { clock_t Process::sys$times(Userspace 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, ×); + if (!copy_to_user(user_times, ×)) + return -EFAULT; return g_uptime & 0x7fffffff; } diff --git a/Kernel/Syscalls/ttyname.cpp b/Kernel/Syscalls/ttyname.cpp index 544135cb5ee8f0..9f37a94768aa81 100644 --- a/Kernel/Syscalls/ttyname.cpp +++ b/Kernel/Syscalls/ttyname.cpp @@ -34,8 +34,6 @@ namespace Kernel { int Process::sys$ttyname(int fd, Userspace 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 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 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 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; } diff --git a/Kernel/Syscalls/uname.cpp b/Kernel/Syscalls/uname.cpp index 33172798dd8a46..6f5638839610f0 100644 --- a/Kernel/Syscalls/uname.cpp +++ b/Kernel/Syscalls/uname.cpp @@ -34,9 +34,6 @@ int Process::sys$uname(Userspace 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 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; } diff --git a/Kernel/Syscalls/unlink.cpp b/Kernel/Syscalls/unlink.cpp index 7c2ac59193834d..f7cf1faf2280b9 100644 --- a/Kernel/Syscalls/unlink.cpp +++ b/Kernel/Syscalls/unlink.cpp @@ -33,8 +33,6 @@ namespace Kernel { int Process::sys$unlink(Userspace 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(); diff --git a/Kernel/Syscalls/unveil.cpp b/Kernel/Syscalls/unveil.cpp index 466d50ed7977a9..dd13d13057a2a6 100644 --- a/Kernel/Syscalls/unveil.cpp +++ b/Kernel/Syscalls/unveil.cpp @@ -34,7 +34,7 @@ namespace Kernel { int Process::sys$unveil(Userspace user_params) { Syscall::SC_unveil_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; if (!params.path.characters && !params.permissions.characters) { @@ -66,7 +66,7 @@ int Process::sys$unveil(Userspace 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; diff --git a/Kernel/Syscalls/utime.cpp b/Kernel/Syscalls/utime.cpp index c7134eb330970e..ae848b906cd733 100644 --- a/Kernel/Syscalls/utime.cpp +++ b/Kernel/Syscalls/utime.cpp @@ -33,14 +33,13 @@ namespace Kernel { int Process::sys$utime(Userspace user_path, size_t path_length, Userspace 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 }; diff --git a/Kernel/Syscalls/waitid.cpp b/Kernel/Syscalls/waitid.cpp index 41a0d7c12ae5d1..1b12c2a744f899 100644 --- a/Kernel/Syscalls/waitid.cpp +++ b/Kernel/Syscalls/waitid.cpp @@ -104,27 +104,19 @@ pid_t Process::sys$waitid(Userspace user_param REQUIRE_PROMISE(proc); Syscall::SC_waitid_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) - return -EFAULT; - - if (!validate_write_typed(params.infop)) + if (!copy_from_user(¶ms, 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(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; } diff --git a/Kernel/Syscalls/write.cpp b/Kernel/Syscalls/write.cpp index 62a421b2d31428..0385bcbb8e1fdb 100644 --- a/Kernel/Syscalls/write.cpp +++ b/Kernel/Syscalls/write.cpp @@ -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 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::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(data), (size_t)size); + if (!buffer.has_value()) + return -EFAULT; + return do_write(*description, buffer.value(), size); } } diff --git a/Kernel/TTY/MasterPTY.cpp b/Kernel/TTY/MasterPTY.cpp index 27c251158ae68b..6bbcfb92b2c803 100644 --- a/Kernel/TTY/MasterPTY.cpp +++ b/Kernel/TTY/MasterPTY.cpp @@ -60,14 +60,14 @@ String MasterPTY::pts_name() const return m_pts_name; } -KResultOr MasterPTY::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr MasterPTY::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { if (!m_slave && m_buffer.is_empty()) return 0; return m_buffer.read(buffer, size); } -KResultOr MasterPTY::write(FileDescription&, size_t, const u8* buffer, size_t size) +KResultOr MasterPTY::write(FileDescription&, size_t, const UserOrKernelBuffer& buffer, size_t size) { if (!m_slave) return KResult(-EIO); @@ -98,12 +98,11 @@ void MasterPTY::notify_slave_closed(Badge) m_slave = nullptr; } -ssize_t MasterPTY::on_slave_write(const u8* data, ssize_t size) +ssize_t MasterPTY::on_slave_write(const UserOrKernelBuffer& data, ssize_t size) { if (m_closed) return -EIO; - m_buffer.write(data, size); - return size; + return m_buffer.write(data, size); } bool MasterPTY::can_write_from_slave() const diff --git a/Kernel/TTY/MasterPTY.h b/Kernel/TTY/MasterPTY.h index 08e48907e63ae1..69ba0a16dc9fa7 100644 --- a/Kernel/TTY/MasterPTY.h +++ b/Kernel/TTY/MasterPTY.h @@ -41,7 +41,7 @@ class MasterPTY final : public CharacterDevice { unsigned index() const { return m_index; } String pts_name() const; - ssize_t on_slave_write(const u8*, ssize_t); + ssize_t on_slave_write(const UserOrKernelBuffer&, ssize_t); bool can_write_from_slave() const; void notify_slave_closed(Badge); bool is_closed() const { return m_closed; } @@ -50,8 +50,8 @@ class MasterPTY final : public CharacterDevice { private: // ^CharacterDevice - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; virtual bool can_write(const FileDescription&, size_t) const override; virtual KResult close() override; diff --git a/Kernel/TTY/PTYMultiplexer.h b/Kernel/TTY/PTYMultiplexer.h index 17698009c57191..bb20469f38696c 100644 --- a/Kernel/TTY/PTYMultiplexer.h +++ b/Kernel/TTY/PTYMultiplexer.h @@ -48,8 +48,8 @@ class PTYMultiplexer final : public CharacterDevice { // ^CharacterDevice virtual KResultOr> open(int options) override; - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override { return 0; } - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override { return 0; } + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return 0; } + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override { return 0; } virtual bool can_read(const FileDescription&, size_t) const override { return true; } virtual bool can_write(const FileDescription&, size_t) const override { return true; } diff --git a/Kernel/TTY/SlavePTY.cpp b/Kernel/TTY/SlavePTY.cpp index 6a5106df381ace..e7daab0a39995e 100644 --- a/Kernel/TTY/SlavePTY.cpp +++ b/Kernel/TTY/SlavePTY.cpp @@ -62,17 +62,22 @@ String SlavePTY::tty_name() const void SlavePTY::echo(u8 ch) { if (should_echo_input()) { - m_master->on_slave_write(&ch, 1); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(&ch); + m_master->on_slave_write(buffer, 1); } } -void SlavePTY::on_master_write(const u8* buffer, ssize_t size) +void SlavePTY::on_master_write(const UserOrKernelBuffer& buffer, ssize_t size) { - for (ssize_t i = 0; i < size; ++i) - emit(buffer[i]); + ssize_t nread = buffer.read_buffered<128>(size, [&](const u8* data, size_t data_size) { + for (size_t i = 0; i < data_size; ++i) + emit(data[i]); + return (ssize_t)data_size; + }); + (void)nread; } -ssize_t SlavePTY::on_tty_write(const u8* data, ssize_t size) +ssize_t SlavePTY::on_tty_write(const UserOrKernelBuffer& data, ssize_t size) { m_time_of_last_write = kgettimeofday().tv_sec; return m_master->on_slave_write(data, size); @@ -90,7 +95,7 @@ bool SlavePTY::can_read(const FileDescription& description, size_t offset) const return TTY::can_read(description, offset); } -KResultOr SlavePTY::read(FileDescription& description, size_t offset, u8* buffer, size_t size) +KResultOr SlavePTY::read(FileDescription& description, size_t offset, UserOrKernelBuffer& buffer, size_t size) { if (m_master->is_closed()) return 0; diff --git a/Kernel/TTY/SlavePTY.h b/Kernel/TTY/SlavePTY.h index ff86f0c04d4e39..2f1d0d5e76691b 100644 --- a/Kernel/TTY/SlavePTY.h +++ b/Kernel/TTY/SlavePTY.h @@ -37,7 +37,7 @@ class SlavePTY final : public TTY { public: virtual ~SlavePTY() override; - void on_master_write(const u8*, ssize_t); + void on_master_write(const UserOrKernelBuffer&, ssize_t); unsigned index() const { return m_index; } time_t time_of_last_write() const { return m_time_of_last_write; } @@ -45,12 +45,12 @@ class SlavePTY final : public TTY { private: // ^TTY virtual String tty_name() const override; - virtual ssize_t on_tty_write(const u8*, ssize_t) override; + virtual ssize_t on_tty_write(const UserOrKernelBuffer&, ssize_t) override; virtual void echo(u8) override; // ^CharacterDevice virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual bool can_write(const FileDescription&, size_t) const override; virtual const char* class_name() const override { return "SlavePTY"; } virtual KResult close() override; diff --git a/Kernel/TTY/TTY.cpp b/Kernel/TTY/TTY.cpp index 672e1885d2e7dc..3130b3ccebd592 100644 --- a/Kernel/TTY/TTY.cpp +++ b/Kernel/TTY/TTY.cpp @@ -52,7 +52,7 @@ void TTY::set_default_termios() memcpy(m_termios.c_cc, default_cc, sizeof(default_cc)); } -KResultOr TTY::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr TTY::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { if (Process::current()->pgid() != pgid()) { // FIXME: Should we propigate this error path somehow? @@ -63,33 +63,40 @@ KResultOr TTY::read(FileDescription&, size_t, u8* buffer, size_t size) if (m_input_buffer.size() < static_cast(size)) size = m_input_buffer.size(); + ssize_t nwritten; if (in_canonical_mode()) { - size_t i = 0; - for (; i < size; i++) { - u8 ch = m_input_buffer.dequeue(); - if (ch == '\0') { - //Here we handle a ^D line, so we don't add the - //character to the output. - m_available_lines--; - break; - } else if (ch == '\n' || is_eol(ch)) { - buffer[i] = ch; - i++; - m_available_lines--; - break; + nwritten = buffer.write_buffered<512>(size, [&](u8* data, size_t data_size) { + size_t i = 0; + for (; i < data_size; i++) { + u8 ch = m_input_buffer.dequeue(); + if (ch == '\0') { + //Here we handle a ^D line, so we don't add the + //character to the output. + m_available_lines--; + break; + } else if (ch == '\n' || is_eol(ch)) { + data[i] = ch; + i++; + m_available_lines--; + break; + } + data[i] = ch; } - buffer[i] = ch; - } - return i; + return (ssize_t)i; + }); + } else { + nwritten = buffer.write_buffered<512>(size, [&](u8* data, size_t data_size) { + for (size_t i = 0; i < data_size; i++) + data[i] = m_input_buffer.dequeue(); + return (ssize_t)data_size; + }); } - - for (size_t i = 0; i < size; i++) - buffer[i] = m_input_buffer.dequeue(); - - return size; + if (nwritten < 0) + return KResult(nwritten); + return (size_t)nwritten; } -KResultOr TTY::write(FileDescription&, size_t, const u8* buffer, size_t size) +KResultOr TTY::write(FileDescription&, size_t, const UserOrKernelBuffer& buffer, size_t size) { if (Process::current()->pgid() != pgid()) { (void)Process::current()->send_signal(SIGTTOU, nullptr); @@ -337,19 +344,17 @@ int TTY::ioctl(FileDescription&, unsigned request, FlatPtr arg) } case TCGETS: { user_termios = reinterpret_cast(arg); - if (!current_process.validate_write(user_termios, sizeof(termios))) + if (!copy_to_user(user_termios, &m_termios)) return -EFAULT; - copy_to_user(user_termios, &m_termios); return 0; } case TCSETS: case TCSETSF: case TCSETSW: { user_termios = reinterpret_cast(arg); - if (!current_process.validate_read(user_termios, sizeof(termios))) - return -EFAULT; termios termios; - copy_from_user(&termios, user_termios); + if (!copy_from_user(&termios, user_termios)) + return -EFAULT; set_termios(termios); if (request == TCSETSF) flush_input(); @@ -365,21 +370,19 @@ int TTY::ioctl(FileDescription&, unsigned request, FlatPtr arg) return 0; case TIOCGWINSZ: user_winsize = reinterpret_cast(arg); - if (!current_process.validate_write(user_winsize, sizeof(winsize))) - return -EFAULT; winsize ws; ws.ws_row = m_rows; ws.ws_col = m_columns; ws.ws_xpixel = 0; ws.ws_ypixel = 0; - copy_to_user(user_winsize, &ws); + if (!copy_to_user(user_winsize, &ws)) + return -EFAULT; return 0; case TIOCSWINSZ: { user_winsize = reinterpret_cast(arg); - if (!current_process.validate_read(user_winsize, sizeof(winsize))) - return -EFAULT; winsize ws; - copy_from_user(&ws, user_winsize); + if (!copy_from_user(&ws, user_winsize)) + return -EFAULT; if (ws.ws_col == m_columns && ws.ws_row == m_rows) return 0; m_rows = ws.ws_row; diff --git a/Kernel/TTY/TTY.h b/Kernel/TTY/TTY.h index d858aa68a6ab3d..6b9d5713360e4d 100644 --- a/Kernel/TTY/TTY.h +++ b/Kernel/TTY/TTY.h @@ -39,8 +39,8 @@ class TTY : public CharacterDevice { public: virtual ~TTY() override; - virtual KResultOr read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; virtual bool can_write(const FileDescription&, size_t) const override; virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override final; @@ -63,7 +63,7 @@ class TTY : public CharacterDevice { void hang_up(); protected: - virtual ssize_t on_tty_write(const u8*, ssize_t) = 0; + virtual ssize_t on_tty_write(const UserOrKernelBuffer&, ssize_t) = 0; void set_size(unsigned short columns, unsigned short rows); TTY(unsigned major, unsigned minor); diff --git a/Kernel/TTY/VirtualConsole.cpp b/Kernel/TTY/VirtualConsole.cpp index 29742a82221ad9..f73e07bd4f9e3d 100644 --- a/Kernel/TTY/VirtualConsole.cpp +++ b/Kernel/TTY/VirtualConsole.cpp @@ -241,14 +241,17 @@ void VirtualConsole::on_key_pressed(KeyboardDevice::Event event) m_terminal.handle_key_press(event.key, event.code_point, event.flags); } -ssize_t VirtualConsole::on_tty_write(const u8* data, ssize_t size) +ssize_t VirtualConsole::on_tty_write(const UserOrKernelBuffer& data, ssize_t size) { ScopedSpinLock lock(s_lock); - for (ssize_t i = 0; i < size; ++i) - m_terminal.on_input(data[i]); + ssize_t nread = data.read_buffered<512>((size_t)size, [&](const u8* buffer, size_t buffer_bytes) { + for (size_t i = 0; i < buffer_bytes; ++i) + m_terminal.on_input(buffer[i]); + return (ssize_t)buffer_bytes; + }); if (m_active) flush_dirty_lines(); - return size; + return nread; } void VirtualConsole::set_vga_start_row(u16 row) @@ -336,7 +339,8 @@ void VirtualConsole::emit(const u8* data, size_t size) void VirtualConsole::echo(u8 ch) { if (should_echo_input()) { - on_tty_write(&ch, 1); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(&ch); + on_tty_write(buffer, 1); } } diff --git a/Kernel/TTY/VirtualConsole.h b/Kernel/TTY/VirtualConsole.h index b6a04587fb6c57..f9f4665be1897c 100644 --- a/Kernel/TTY/VirtualConsole.h +++ b/Kernel/TTY/VirtualConsole.h @@ -54,7 +54,7 @@ class VirtualConsole final : public TTY virtual void on_key_pressed(KeyboardDevice::Event) override; // ^TTY - virtual ssize_t on_tty_write(const u8*, ssize_t) override; + virtual ssize_t on_tty_write(const UserOrKernelBuffer&, ssize_t) override; virtual String tty_name() const override { return m_tty_name; } virtual void echo(u8) override; diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index 40b82bd2e6b874..07b7a8112f263e 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -479,10 +479,10 @@ bool Thread::has_signal_handler(u8 signal) const return !action.handler_or_sigaction.is_null(); } -static void push_value_on_user_stack(u32* stack, u32 data) +static bool push_value_on_user_stack(u32* stack, u32 data) { *stack -= 4; - copy_to_user((u32*)*stack, &data); + return copy_to_user((u32*)*stack, &data); } void Thread::resume_from_stopped() @@ -659,11 +659,11 @@ void Thread::set_default_signal_dispositions() m_signal_action_data[SIGWINCH].handler_or_sigaction = VirtualAddress(SIG_IGN); } -void Thread::push_value_on_stack(FlatPtr value) +bool Thread::push_value_on_stack(FlatPtr value) { m_tss.esp -= 4; FlatPtr* stack_ptr = (FlatPtr*)m_tss.esp; - copy_to_user(stack_ptr, &value); + return copy_to_user(stack_ptr, &value); } RegisterState& Thread::get_register_dump_from_stack() @@ -682,19 +682,19 @@ u32 Thread::make_userspace_stack_for_main_thread(Vector arguments, Vecto auto push_on_new_stack = [&new_esp](u32 value) { new_esp -= 4; Userspace stack_ptr = new_esp; - copy_to_user(stack_ptr, &value); + return copy_to_user(stack_ptr, &value); }; auto push_aux_value_on_new_stack = [&new_esp](auxv_t value) { new_esp -= sizeof(auxv_t); Userspace stack_ptr = new_esp; - copy_to_user(stack_ptr, &value); + return copy_to_user(stack_ptr, &value); }; auto push_string_on_new_stack = [&new_esp](const String& string) { new_esp -= round_up_to_power_of_two(string.length() + 1, 4); Userspace stack_ptr = new_esp; - copy_to_user(stack_ptr, string.characters(), string.length() + 1); + return copy_to_user(stack_ptr, string.characters(), string.length() + 1); }; Vector argv_entries; @@ -869,18 +869,21 @@ String Thread::backtrace_impl() if (Processor::get_context_frame_ptr(*this, stack_ptr, eip)) { recognized_symbols.append({ eip, symbolicate_kernel_address(eip) }); for (;;) { - if (!process.validate_read_from_kernel(VirtualAddress(stack_ptr), sizeof(void*) * 2)) - break; FlatPtr retaddr; if (is_user_range(VirtualAddress(stack_ptr), sizeof(FlatPtr) * 2)) { - copy_from_user(&retaddr, &((FlatPtr*)stack_ptr)[1]); + if (!copy_from_user(&retaddr, &((FlatPtr*)stack_ptr)[1])) + break; recognized_symbols.append({ retaddr, symbolicate_kernel_address(retaddr) }); - copy_from_user(&stack_ptr, (FlatPtr*)stack_ptr); + if (!copy_from_user(&stack_ptr, (FlatPtr*)stack_ptr)) + break; } else { - memcpy(&retaddr, &((FlatPtr*)stack_ptr)[1], sizeof(FlatPtr)); + void* fault_at; + if (!safe_memcpy(&retaddr, &((FlatPtr*)stack_ptr)[1], sizeof(FlatPtr), fault_at)) + break; recognized_symbols.append({ retaddr, symbolicate_kernel_address(retaddr) }); - memcpy(&stack_ptr, (FlatPtr*)stack_ptr, sizeof(FlatPtr)); + if (!safe_memcpy(&stack_ptr, (FlatPtr*)stack_ptr, sizeof(FlatPtr), fault_at)) + break; } } } @@ -901,11 +904,19 @@ Vector Thread::raw_backtrace(FlatPtr ebp, FlatPtr eip) const ProcessPagingScope paging_scope(process); Vector backtrace; backtrace.append(eip); - for (FlatPtr* stack_ptr = (FlatPtr*)ebp; process.validate_read_from_kernel(VirtualAddress(stack_ptr), sizeof(FlatPtr) * 2) && MM.can_read_without_faulting(process, VirtualAddress(stack_ptr), sizeof(FlatPtr) * 2); stack_ptr = (FlatPtr*)*stack_ptr) { - FlatPtr retaddr = stack_ptr[1]; + FlatPtr stack_ptr_copy; + FlatPtr stack_ptr = (FlatPtr)ebp; + for (;;) { + void* fault_at; + if (!safe_memcpy(&stack_ptr_copy, (void*)stack_ptr, sizeof(FlatPtr), fault_at)) + break; + FlatPtr retaddr; + if (!safe_memcpy(&retaddr, (void*)(stack_ptr + sizeof(FlatPtr)), sizeof(FlatPtr), fault_at)) + break; backtrace.append(retaddr); if (backtrace.size() == Profiling::max_stack_frame_count) break; + stack_ptr = stack_ptr_copy; } return backtrace; } diff --git a/Kernel/Thread.h b/Kernel/Thread.h index f8f9d1ded0d0cc..5e766643d6fa31 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -107,6 +107,7 @@ class Thread { const String& name() const { return m_name; } void set_name(const StringView& s) { m_name = s; } + void set_name(String&& name) { m_name = move(name); } void finalize(); @@ -430,7 +431,7 @@ class Thread { FPUState& fpu_state() { return *m_fpu_state; } void set_default_signal_dispositions(); - void push_value_on_stack(FlatPtr); + bool push_value_on_stack(FlatPtr); u32 make_userspace_stack_for_main_thread(Vector arguments, Vector environment, Vector); diff --git a/Kernel/UserOrKernelBuffer.cpp b/Kernel/UserOrKernelBuffer.cpp new file mode 100644 index 00000000000000..8d311b5a629fe6 --- /dev/null +++ b/Kernel/UserOrKernelBuffer.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace Kernel { + +bool UserOrKernelBuffer::is_kernel_buffer() const +{ + return !is_user_address(VirtualAddress(m_buffer)); +} + +String UserOrKernelBuffer::copy_into_string(size_t size) const +{ + if (!m_buffer) + return {}; + if (is_user_address(VirtualAddress(m_buffer))) { + char* buffer; + auto data_copy = StringImpl::create_uninitialized(size, buffer); + if (!copy_from_user(buffer, m_buffer, size)) + return {}; + return data_copy; + } + + return String({ m_buffer, size }); +} + +bool UserOrKernelBuffer::write(const void* src, size_t offset, size_t len) +{ + if (!m_buffer) + return false; + + if (is_user_address(VirtualAddress(m_buffer))) + return copy_to_user(m_buffer + offset, src, len); + + memcpy(m_buffer + offset, src, len); + return true; +} + +bool UserOrKernelBuffer::read(void* dest, size_t offset, size_t len) const +{ + if (!m_buffer) + return false; + + if (is_user_address(VirtualAddress(m_buffer))) + return copy_from_user(dest, m_buffer + offset, len); + + memcpy(dest, m_buffer + offset, len); + return true; +} + +bool UserOrKernelBuffer::memset(int value, size_t offset, size_t len) +{ + if (!m_buffer) + return false; + + if (is_user_address(VirtualAddress(m_buffer))) + return memset_user(m_buffer + offset, value, len); + + ::memset(m_buffer + offset, value, len); + return true; +} + +} diff --git a/Kernel/UserOrKernelBuffer.h b/Kernel/UserOrKernelBuffer.h new file mode 100644 index 00000000000000..08e277341a073b --- /dev/null +++ b/Kernel/UserOrKernelBuffer.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class UserOrKernelBuffer { +public: + UserOrKernelBuffer() = delete; + + static UserOrKernelBuffer for_kernel_buffer(u8* kernel_buffer) + { + ASSERT(!kernel_buffer || !is_user_address(VirtualAddress(kernel_buffer))); + return UserOrKernelBuffer(kernel_buffer); + } + + static Optional for_user_buffer(u8* user_buffer, size_t size) + { + if (user_buffer && !is_user_range(VirtualAddress(user_buffer), size)) + return {}; + return UserOrKernelBuffer(user_buffer); + } + + template + static Optional for_user_buffer(UserspaceType userspace, size_t size) + { + if (!is_user_range(VirtualAddress(userspace.unsafe_userspace_ptr()), size)) + return {}; + return UserOrKernelBuffer(const_cast((const u8*)userspace.unsafe_userspace_ptr())); + } + + bool is_kernel_buffer() const; + const void* user_or_kernel_ptr() const { return m_buffer; } + + UserOrKernelBuffer offset(ssize_t offset) const + { + if (!m_buffer) + return *this; + UserOrKernelBuffer offset_buffer = *this; + offset_buffer.m_buffer += offset; + ASSERT(offset_buffer.is_kernel_buffer() == is_kernel_buffer()); + return offset_buffer; + } + + String copy_into_string(size_t size) const; + [[nodiscard]] bool write(const void* src, size_t offset, size_t len); + [[nodiscard]] bool write(const void* src, size_t len) + { + return write(src, 0, len); + } + [[nodiscard]] bool read(void* dest, size_t offset, size_t len) const; + [[nodiscard]] bool read(void* dest, size_t len) const + { + return read(dest, 0, len); + } + + [[nodiscard]] bool memset(int value, size_t offset, size_t len); + [[nodiscard]] bool memset(int value, size_t len) + { + return memset(value, 0, len); + } + + template + [[nodiscard]] ssize_t write_buffered(size_t offset, size_t len, F f) + { + if (!m_buffer) + return -EFAULT; + if (is_kernel_buffer()) { + // We're transferring directly to a kernel buffer, bypass + return f(m_buffer + offset, len); + } + + // The purpose of using a buffer on the stack is that we can + // avoid a bunch of small (e.g. 1-byte) copy_to_user calls + u8 buffer[BUFFER_BYTES]; + size_t nwritten = 0; + while (nwritten < len) { + auto to_copy = min(sizeof(buffer), len - nwritten); + ssize_t copied = f(buffer, to_copy); + if (copied < 0) + return copied; + ASSERT((size_t)copied <= to_copy); + if (!write(buffer, nwritten, (size_t)copied)) + return -EFAULT; + nwritten += (size_t)copied; + if ((size_t)copied < to_copy) + break; + } + return (ssize_t)nwritten; + } + template + [[nodiscard]] ssize_t write_buffered(size_t len, F f) + { + return write_buffered(0, len, f); + } + + template + [[nodiscard]] ssize_t read_buffered(size_t offset, size_t len, F f) const + { + if (!m_buffer) + return -EFAULT; + if (is_kernel_buffer()) { + // We're transferring directly from a kernel buffer, bypass + return f(m_buffer + offset, len); + } + + // The purpose of using a buffer on the stack is that we can + // avoid a bunch of small (e.g. 1-byte) copy_from_user calls + u8 buffer[BUFFER_BYTES]; + size_t nread = 0; + while (nread < len) { + auto to_copy = min(sizeof(buffer), len - nread); + if (!read(buffer, nread, to_copy)) + return -EFAULT; + ssize_t copied = f(buffer, to_copy); + if (copied < 0) + return copied; + ASSERT((size_t)copied <= to_copy); + nread += (size_t)copied; + if ((size_t)copied < to_copy) + break; + } + return nread; + } + template + [[nodiscard]] ssize_t read_buffered(size_t len, F f) const + { + return read_buffered(0, len, f); + } + +private: + explicit UserOrKernelBuffer(u8* buffer) + : m_buffer(buffer) + { + } + + u8* m_buffer; +}; + +} diff --git a/Kernel/VM/InodeVMObject.cpp b/Kernel/VM/InodeVMObject.cpp index 8c5b07d413ea38..c63417fab0edc5 100644 --- a/Kernel/VM/InodeVMObject.cpp +++ b/Kernel/VM/InodeVMObject.cpp @@ -89,7 +89,7 @@ void InodeVMObject::inode_size_changed(Badge, size_t old_size, size_t new }); } -void InodeVMObject::inode_contents_changed(Badge, off_t offset, ssize_t size, const u8* data) +void InodeVMObject::inode_contents_changed(Badge, off_t offset, ssize_t size, const UserOrKernelBuffer& data) { (void)size; (void)data; diff --git a/Kernel/VM/InodeVMObject.h b/Kernel/VM/InodeVMObject.h index 727199ff7ec842..94e7073709bd26 100644 --- a/Kernel/VM/InodeVMObject.h +++ b/Kernel/VM/InodeVMObject.h @@ -39,7 +39,7 @@ class InodeVMObject : public VMObject { Inode& inode() { return *m_inode; } const Inode& inode() const { return *m_inode; } - void inode_contents_changed(Badge, off_t, ssize_t, const u8*); + void inode_contents_changed(Badge, off_t, ssize_t, const UserOrKernelBuffer&); void inode_size_changed(Badge, size_t old_size, size_t new_size); size_t amount_dirty() const; diff --git a/Kernel/VM/MemoryManager.cpp b/Kernel/VM/MemoryManager.cpp index 88bb45143595c9..fc5114f29611d1 100644 --- a/Kernel/VM/MemoryManager.cpp +++ b/Kernel/VM/MemoryManager.cpp @@ -767,39 +767,6 @@ bool MemoryManager::validate_user_stack(const Process& process, VirtualAddress v return region && region->is_user_accessible() && region->is_stack(); } -bool MemoryManager::validate_kernel_read(const Process& process, VirtualAddress vaddr, size_t size) const -{ - ScopedSpinLock lock(s_mm_lock); - return validate_range(process, vaddr, size); -} - -bool MemoryManager::can_read_without_faulting(const Process& process, VirtualAddress vaddr, size_t size) const -{ - // FIXME: Use the size argument! - UNUSED_PARAM(size); - ScopedSpinLock lock(s_mm_lock); - auto* pte = const_cast(this)->pte(process.page_directory(), vaddr); - if (!pte) - return false; - return pte->is_present(); -} - -bool MemoryManager::validate_user_read(const Process& process, VirtualAddress vaddr, size_t size) const -{ - if (!is_user_address(vaddr)) - return false; - ScopedSpinLock lock(s_mm_lock); - return validate_range(process, vaddr, size); -} - -bool MemoryManager::validate_user_write(const Process& process, VirtualAddress vaddr, size_t size) const -{ - if (!is_user_address(vaddr)) - return false; - ScopedSpinLock lock(s_mm_lock); - return validate_range(process, vaddr, size); -} - void MemoryManager::register_vmobject(VMObject& vmobject) { ScopedSpinLock lock(s_mm_lock); diff --git a/Kernel/VM/MemoryManager.h b/Kernel/VM/MemoryManager.h index f3f4bbb90c6e76..d9823377abb9c2 100644 --- a/Kernel/VM/MemoryManager.h +++ b/Kernel/VM/MemoryManager.h @@ -100,12 +100,6 @@ class MemoryManager { void enter_process_paging_scope(Process&); bool validate_user_stack(const Process&, VirtualAddress) const; - bool validate_user_read(const Process&, VirtualAddress, size_t) const; - bool validate_user_write(const Process&, VirtualAddress, size_t) const; - - bool validate_kernel_read(const Process&, VirtualAddress, size_t) const; - - bool can_read_without_faulting(const Process&, VirtualAddress, size_t) const; enum class ShouldZeroFill { No, diff --git a/Kernel/VM/Region.cpp b/Kernel/VM/Region.cpp index d3b4d02676d715..a1d39daf240415 100644 --- a/Kernel/VM/Region.cpp +++ b/Kernel/VM/Region.cpp @@ -439,13 +439,24 @@ PageFaultResponse Region::handle_cow_fault(size_t page_index_in_region) klog() << "MM: handle_cow_fault was unable to allocate a physical page"; return PageFaultResponse::OutOfMemory; } - auto physical_page_to_copy = move(page_slot); + u8* dest_ptr = MM.quickmap_page(*page); const u8* src_ptr = vaddr().offset(page_index_in_region * PAGE_SIZE).as_ptr(); #ifdef PAGE_FAULT_DEBUG - dbg() << " >> COW " << page->paddr() << " <- " << physical_page_to_copy->paddr(); + dbg() << " >> COW " << page->paddr() << " <- " << page_slot->paddr(); #endif - copy_from_user(dest_ptr, src_ptr, PAGE_SIZE); + { + SmapDisabler disabler; + void* fault_at; + if (!safe_memcpy(dest_ptr, src_ptr, PAGE_SIZE, fault_at)) { + if ((u8*)fault_at >= dest_ptr && (u8*)fault_at <= dest_ptr + PAGE_SIZE) + dbg() << " >> COW: error copying page " << page_slot->paddr() << "/" << VirtualAddress(src_ptr) << " to " << page->paddr() << "/" << VirtualAddress(dest_ptr) << ": failed to write to page at " << VirtualAddress(fault_at); + else if ((u8*)fault_at >= src_ptr && (u8*)fault_at <= src_ptr + PAGE_SIZE) + dbg() << " >> COW: error copying page " << page_slot->paddr() << "/" << VirtualAddress(src_ptr) << " to " << page->paddr() << "/" << VirtualAddress(dest_ptr) << ": failed to read from page at " << VirtualAddress(fault_at); + else + ASSERT_NOT_REACHED(); + } + } page_slot = move(page); MM.unquickmap_page(); set_should_cow(page_index_in_region, false); @@ -489,7 +500,8 @@ PageFaultResponse Region::handle_inode_fault(size_t page_index_in_region) sti(); u8 page_buffer[PAGE_SIZE]; auto& inode = inode_vmobject.inode(); - auto nread = inode.read_bytes((first_page_index() + page_index_in_region) * PAGE_SIZE, PAGE_SIZE, page_buffer, nullptr); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(page_buffer); + auto nread = inode.read_bytes((first_page_index() + page_index_in_region) * PAGE_SIZE, PAGE_SIZE, buffer, nullptr); if (nread < 0) { klog() << "MM: handle_inode_fault had error (" << nread << ") while reading!"; return PageFaultResponse::ShouldCrash; @@ -506,7 +518,15 @@ PageFaultResponse Region::handle_inode_fault(size_t page_index_in_region) } u8* dest_ptr = MM.quickmap_page(*vmobject_physical_page_entry); - memcpy(dest_ptr, page_buffer, PAGE_SIZE); + { + void* fault_at; + if (!safe_memcpy(dest_ptr, page_buffer, PAGE_SIZE, fault_at)) { + if ((u8*)fault_at >= dest_ptr && (u8*)fault_at <= dest_ptr + PAGE_SIZE) + dbg() << " >> inode fault: error copying data to " << vmobject_physical_page_entry->paddr() << "/" << VirtualAddress(dest_ptr) << ", failed at " << VirtualAddress(fault_at); + else + ASSERT_NOT_REACHED(); + } + } MM.unquickmap_page(); remap_page(page_index_in_region); diff --git a/Kernel/VM/Region.h b/Kernel/VM/Region.h index 5480e186defbe6..68d40e53c308e1 100644 --- a/Kernel/VM/Region.h +++ b/Kernel/VM/Region.h @@ -79,6 +79,7 @@ class Region final unsigned access() const { return m_access; } void set_name(const String& name) { m_name = name; } + void set_name(String&& name) { m_name = move(name); } const VMObject& vmobject() const { return *m_vmobject; } VMObject& vmobject() { return *m_vmobject; } diff --git a/Libraries/LibC/unistd.cpp b/Libraries/LibC/unistd.cpp index f2fe6a379c50eb..cf6643e736c7a0 100644 --- a/Libraries/LibC/unistd.cpp +++ b/Libraries/LibC/unistd.cpp @@ -96,8 +96,8 @@ int execve(const char* filename, char* const argv[], char* const envp[]) auto copy_strings = [&](auto& vec, size_t count, auto& output) { output.length = count; for (size_t i = 0; vec[i]; ++i) { - output.strings.ptr()[i].characters = vec[i]; - output.strings.ptr()[i].length = strlen(vec[i]); + output.strings[i].characters = vec[i]; + output.strings[i].length = strlen(vec[i]); } }; diff --git a/Libraries/LibELF/Loader.cpp b/Libraries/LibELF/Loader.cpp index 42740f95e22a98..8f7ccc722fa73e 100644 --- a/Libraries/LibELF/Loader.cpp +++ b/Libraries/LibELF/Loader.cpp @@ -81,7 +81,10 @@ bool Loader::layout() failed = true; return; } - do_memcpy(tls_image, program_header.raw_data(), program_header.size_in_image()); + if (!do_memcpy(tls_image, program_header.raw_data(), program_header.size_in_image())) { + failed = false; + return; + } #endif return; } @@ -117,7 +120,10 @@ bool Loader::layout() // Accessing it would definitely be a bug. auto page_offset = program_header.vaddr(); page_offset.mask(~PAGE_MASK); - do_memcpy((u8*)allocated_section + page_offset.get(), program_header.raw_data(), program_header.size_in_image()); + if (!do_memcpy((u8*)allocated_section + page_offset.get(), program_header.raw_data(), program_header.size_in_image())) { + failed = false; + return; + } } else { auto* mapped_section = map_section_hook( program_header.vaddr(), diff --git a/Libraries/LibPthread/pthread.cpp b/Libraries/LibPthread/pthread.cpp index 68af2fff21cb45..218c1750393985 100644 --- a/Libraries/LibPthread/pthread.cpp +++ b/Libraries/LibPthread/pthread.cpp @@ -383,7 +383,7 @@ int pthread_attr_getstack(const pthread_attr_t* attributes, void** p_stack_ptr, if (!attributes_impl || !p_stack_ptr || !p_stack_size) return EINVAL; - *p_stack_ptr = attributes_impl->m_stack_location.ptr(); + *p_stack_ptr = attributes_impl->m_stack_location; *p_stack_size = attributes_impl->m_stack_size; return 0;