diff options
author | Tom <tomut@yahoo.com> | 2020-09-11 21:11:07 -0600 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-09-13 21:19:15 +0200 |
commit | c8d9f1b9c920e0314bb9ca67f183c8df743e845a (patch) | |
tree | 773e9fb30d26602598ba309291b8e6d2e80475e6 | |
parent | 7d1b8417bdf5d2818d1c9d310786cdf59650b104 (diff) | |
download | serenity-c8d9f1b9c920e0314bb9ca67f183c8df743e845a.zip |
Kernel: Make copy_to/from_user safe and remove unnecessary checks
Since the CPU already does almost all necessary validation steps
for us, we don't really need to attempt to do this. Doing it
ourselves doesn't really work very reliably, because we'd have to
account for other processors modifying virtual memory, and we'd
have to account for e.g. pages not being able to be allocated
due to insufficient resources.
So change the copy_to/from_user (and associated helper functions)
to use the new safe_memcpy, which will return whether it succeeded
or not. The only manual validation step needed (which the CPU
can't perform for us) is making sure the pointers provided by user
mode aren't pointing to kernel mappings.
To make it easier to read/write from/to either kernel or user mode
data add the UserOrKernelBuffer helper class, which will internally
either use copy_from/to_user or directly memcpy, or pass the data
through directly using a temporary buffer on the stack.
Last but not least we need to keep syscall params trivial as we
need to copy them from/to user mode using copy_from/to_user.
149 files changed, 1585 insertions, 1244 deletions
diff --git a/DevTools/UserspaceEmulator/Emulator.cpp b/DevTools/UserspaceEmulator/Emulator.cpp index 77d92aca67..72b37b1fd3 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 b4822cdcdf..5a95809701 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<const char*> characters; + const char* characters; size_t length { 0 }; }; @@ -239,7 +239,7 @@ struct ImmutableBufferArgument { }; struct StringListArgument { - Userspace<StringArgument*> 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<const struct timespec*> timeout; - Userspace<const u32*> sigmask; + const struct timespec* timeout; + const u32* sigmask; }; struct SC_clock_nanosleep_params { int clock_id; int flags; - Userspace<const struct timespec*> requested_sleep; - Userspace<struct timespec*> remaining_sleep; + const struct timespec* requested_sleep; + struct timespec* remaining_sleep; }; struct SC_sendto_params { int sockfd; ImmutableBufferArgument<void, size_t> data; int flags; - Userspace<const sockaddr*> addr; + const sockaddr* addr; socklen_t addr_length; }; @@ -296,50 +296,50 @@ struct SC_recvfrom_params { int sockfd; MutableBufferArgument<void, size_t> buffer; int flags; - Userspace<sockaddr*> addr; - Userspace<socklen_t*> addr_length; + sockaddr* addr; + socklen_t* addr_length; }; struct SC_getsockopt_params { int sockfd; int level; int option; - Userspace<void*> value; - Userspace<socklen_t*> value_size; + void* value; + socklen_t* value_size; }; struct SC_setsockopt_params { int sockfd; int level; int option; - Userspace<const void*> value; + const void* value; socklen_t value_size; }; struct SC_getsockname_params { int sockfd; - Userspace<sockaddr*> addr; - Userspace<socklen_t*> addrlen; + sockaddr* addr; + socklen_t* addrlen; }; struct SC_getpeername_params { int sockfd; - Userspace<sockaddr*> addr; - Userspace<socklen_t*> addrlen; + sockaddr* addr; + socklen_t* addrlen; }; struct SC_futex_params { - Userspace<const i32*> userspace_address; + const i32* userspace_address; int futex_op; i32 val; - Userspace<const timespec*> timeout; + const timespec* timeout; }; struct SC_setkeymap_params { - Userspace<const u32*> map; - Userspace<const u32*> shift_map; - Userspace<const u32*> alt_map; - Userspace<const u32*> 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<void*> 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<struct siginfo*> infop; + struct siginfo* infop; int options; }; struct SC_stat_params { StringArgument path; - Userspace<struct stat*> statbuf; + struct stat* statbuf; bool follow_symlinks; }; struct SC_ptrace_params { int request; pid_t tid; - Userspace<u8*> addr; + u8* addr; int data; }; struct SC_ptrace_peek_params { - Userspace<const u32*> address; - Userspace<u32*> 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 7fbe6faa35..d1a9ccb136 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 1b7d04e10e..646cf406d1 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 e511a2d876..b919f4fe08 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<size_t> Console::read(Kernel::FileDescription&, size_t, u8*, size_t) +Kernel::KResultOr<size_t> 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<size_t> Console::write(Kernel::FileDescription&, size_t, const u8* data, size_t size) +Kernel::KResultOr<size_t> 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 f06ebf2a6a..c37731e4f7 100644 --- a/Kernel/Console.h +++ b/Kernel/Console.h @@ -43,8 +43,8 @@ public: // ^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<size_t> read(Kernel::FileDescription&, size_t, u8*, size_t) override; - virtual Kernel::KResultOr<size_t> write(Kernel::FileDescription&, size_t, const u8*, size_t) override; + virtual Kernel::KResultOr<size_t> read(Kernel::FileDescription&, size_t, Kernel::UserOrKernelBuffer&, size_t) override; + virtual Kernel::KResultOr<size_t> 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 c9f1732260..85040ed747 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 213fc1617c..6b98db525b 100644 --- a/Kernel/Devices/BXVGADevice.h +++ b/Kernel/Devices/BXVGADevice.h @@ -48,10 +48,10 @@ private: 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<size_t> read(FileDescription&, size_t, u8*, size_t) override { return -EINVAL; } - virtual KResultOr<size_t> 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<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return -EINVAL; } + virtual KResultOr<size_t> 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 2120db0edc..d78388b610 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<BlockDevice*>(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<BlockDevice*>(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 042a2cfce1..69786df3f8 100644 --- a/Kernel/Devices/BlockDevice.h +++ b/Kernel/Devices/BlockDevice.h @@ -37,13 +37,13 @@ public: 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 75f83d3976..d1ccb90d1b 100644 --- a/Kernel/Devices/DiskPartition.cpp +++ b/Kernel/Devices/DiskPartition.cpp @@ -48,7 +48,7 @@ DiskPartition::~DiskPartition() { } -KResultOr<size_t> DiskPartition::read(FileDescription& fd, size_t offset, u8* outbuf, size_t len) +KResultOr<size_t> 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<size_t> DiskPartition::write(FileDescription& fd, size_t offset, const u8* inbuf, size_t len) +KResultOr<size_t> 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 51d3342869..e9551b8097 100644 --- a/Kernel/Devices/DiskPartition.h +++ b/Kernel/Devices/DiskPartition.h @@ -36,13 +36,13 @@ public: static NonnullRefPtr<DiskPartition> 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<size_t> read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> 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 095fc78ecd..48826636de 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<DiskPartition> 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<DiskPartition> 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 bfd2c15a82..0d43d7a950 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<size_t> FullDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr<size_t> FullDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { ssize_t count = min(static_cast<size_t>(PAGE_SIZE), size); - memset(buffer, 0, count); + if (!buffer.memset(0, count)) + return KResult(-EFAULT); return count; } -KResultOr<size_t> FullDevice::write(FileDescription&, size_t, const u8*, size_t size) +KResultOr<size_t> 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 3404ce22aa..43070686c1 100644 --- a/Kernel/Devices/FullDevice.h +++ b/Kernel/Devices/FullDevice.h @@ -38,8 +38,8 @@ public: private: // ^CharacterDevice - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> 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 e5413a0336..f442952cd6 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<DiskPartition> 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 531c488a9f..9f60268986 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<size_t> KeyboardDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr<size_t> KeyboardDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { size_t nread = 0; while (nread < size) { @@ -379,13 +379,19 @@ KResultOr<size_t> 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)>(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<size_t> KeyboardDevice::write(FileDescription&, size_t, const u8*, size_t) +KResultOr<size_t> KeyboardDevice::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) { return 0; } diff --git a/Kernel/Devices/KeyboardDevice.h b/Kernel/Devices/KeyboardDevice.h index 74a53a0da6..1d7f60ab7d 100644 --- a/Kernel/Devices/KeyboardDevice.h +++ b/Kernel/Devices/KeyboardDevice.h @@ -57,9 +57,9 @@ public: const String keymap_name() { return m_character_map.character_map_name(); } // ^CharacterDevice - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> 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 b836a9b479..a57a5db310 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 f19de93e41..84998f14c8 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 8a7d60e54c..09dda1f853 100644 --- a/Kernel/Devices/MBVGADevice.h +++ b/Kernel/Devices/MBVGADevice.h @@ -47,10 +47,10 @@ private: 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<size_t> read(FileDescription&, size_t, u8*, size_t) override { return -EINVAL; } - virtual KResultOr<size_t> 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<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return -EINVAL; } + virtual KResultOr<size_t> 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 5c6a5c59c5..260e8d9aa8 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<size_t> NullDevice::read(FileDescription&, size_t, u8*, size_t) +KResultOr<size_t> NullDevice::read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) { return 0; } -KResultOr<size_t> NullDevice::write(FileDescription&, size_t, const u8*, size_t buffer_size) +KResultOr<size_t> NullDevice::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t buffer_size) { return min(static_cast<size_t>(PAGE_SIZE), buffer_size); } diff --git a/Kernel/Devices/NullDevice.h b/Kernel/Devices/NullDevice.h index c7968258f7..4fcc9c0258 100644 --- a/Kernel/Devices/NullDevice.h +++ b/Kernel/Devices/NullDevice.h @@ -41,8 +41,8 @@ public: private: // ^CharacterDevice - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> 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 172816ea1b..cc47ff532c 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<u8>(m_bus_master_base.offset(2).in<u8>() | 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<u8>() & 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<u8>(); 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<u8>(); 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<u8*>(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<u8>(); ASSERT(!(status & ATA_SR_BSY)); + if (nread < 0) + return false; // TODO: -EFAULT } prepare_for_irq(); m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_CACHE_FLUSH); diff --git a/Kernel/Devices/PATAChannel.h b/Kernel/Devices/PATAChannel.h index 60ed95d6c1..40e331a4e8 100644 --- a/Kernel/Devices/PATAChannel.h +++ b/Kernel/Devices/PATAChannel.h @@ -84,10 +84,10 @@ private: 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 384c0c7bba..a81f7bb557 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<size_t> PATADiskDevice::read(FileDescription&, size_t offset, u8* outbuf, size_t len) +KResultOr<size_t> 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<size_t> 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<size_t> PATADiskDevice::write(FileDescription&, size_t offset, const u8* inbuf, size_t len) +KResultOr<size_t> 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<size_t> 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 d9b7fa9432..eb9377e1bf 100644 --- a/Kernel/Devices/PATADiskDevice.h +++ b/Kernel/Devices/PATADiskDevice.h @@ -55,15 +55,15 @@ public: 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<size_t> read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual bool can_write(const FileDescription&, size_t) const override; protected: @@ -74,10 +74,10 @@ private: 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 65fcc33264..75ae40524e 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<size_t> PS2MouseDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr<size_t> PS2MouseDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { ASSERT(size > 0); size_t nread = 0; @@ -349,14 +349,15 @@ KResultOr<size_t> 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<size_t> PS2MouseDevice::write(FileDescription&, size_t, const u8*, size_t) +KResultOr<size_t> PS2MouseDevice::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) { return 0; } diff --git a/Kernel/Devices/PS2MouseDevice.h b/Kernel/Devices/PS2MouseDevice.h index 10f382f43d..07cfcc7a61 100644 --- a/Kernel/Devices/PS2MouseDevice.h +++ b/Kernel/Devices/PS2MouseDevice.h @@ -45,8 +45,8 @@ public: // ^CharacterDevice virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> 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 5c6b7eb22b..fa625d0ecf 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<size_t> RandomDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr<size_t> 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<size_t> RandomDevice::write(FileDescription&, size_t, const u8*, size_t size) +KResultOr<size_t> 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<size_t>(PAGE_SIZE), size); diff --git a/Kernel/Devices/RandomDevice.h b/Kernel/Devices/RandomDevice.h index d8951297ab..c419934107 100644 --- a/Kernel/Devices/RandomDevice.h +++ b/Kernel/Devices/RandomDevice.h @@ -38,8 +38,8 @@ public: private: // ^CharacterDevice - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> 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 8d7406f39b..eb6d15b993 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<size_t> SB16::read(FileDescription&, size_t, u8*, size_t) +KResultOr<size_t> SB16::read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) { return 0; } @@ -231,7 +231,7 @@ void SB16::wait_for_irq() disable_irq(); } -KResultOr<size_t> SB16::write(FileDescription&, size_t, const u8* data, size_t length) +KResultOr<size_t> 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<size_t> 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 e0d5ba7ca3..cc18fde5ef 100644 --- a/Kernel/Devices/SB16.h +++ b/Kernel/Devices/SB16.h @@ -47,8 +47,8 @@ public: // ^CharacterDevice virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> 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 e286afb7d4..a54a8bed3e 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<size_t> SerialDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr<size_t> SerialDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { if (!size) return 0; @@ -53,9 +53,15 @@ KResultOr<size_t> 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<size_t> SerialDevice::write(FileDescription&, size_t, const u8* buffer, size_t size) +KResultOr<size_t> SerialDevice::write(FileDescription&, size_t, const UserOrKernelBuffer& buffer, size_t size) { if (!size) return 0; @@ -71,9 +77,14 @@ KResultOr<size_t> 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 bac77ce9b1..5c76f5abdf 100644 --- a/Kernel/Devices/SerialDevice.h +++ b/Kernel/Devices/SerialDevice.h @@ -43,9 +43,9 @@ public: // ^CharacterDevice virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual bool can_write(const FileDescription&, size_t) const override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> 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 e568d9a215..ac326434d0 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<size_t> ZeroDevice::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr<size_t> ZeroDevice::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size) { ssize_t count = min(static_cast<size_t>(PAGE_SIZE), size); - memset(buffer, 0, count); + if (!buffer.memset(0, count)) + return KResult(-EFAULT); return count; } -KResultOr<size_t> ZeroDevice::write(FileDescription&, size_t, const u8*, size_t size) +KResultOr<size_t> ZeroDevice::write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t size) { return min(static_cast<size_t>(PAGE_SIZE), size); } diff --git a/Kernel/Devices/ZeroDevice.h b/Kernel/Devices/ZeroDevice.h index 0f3e76e5d6..f307c6f645 100644 --- a/Kernel/Devices/ZeroDevice.h +++ b/Kernel/Devices/ZeroDevice.h @@ -38,8 +38,8 @@ public: private: // ^CharacterDevice - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> 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 6cc74f5ed7..4b9285e327 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 02a8a2bf12..b855fb8853 100644 --- a/Kernel/DoubleBuffer.h +++ b/Kernel/DoubleBuffer.h @@ -29,6 +29,7 @@ #include <AK/Types.h> #include <Kernel/KBuffer.h> #include <Kernel/Lock.h> +#include <Kernel/UserOrKernelBuffer.h> 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<u8*>(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 e5880e0897..2df34a58bb 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<u32>(index) * static_cast<u32>(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<u32>(index) * static_cast<u32>(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<BlockBasedFS*>(this)->flush_specific_block_if_needed(index); u32 base_offset = static_cast<u32>(index) * static_cast<u32>(block_size()) + static_cast<u32>(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<u32>(index) * static_cast<u32>(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<u32>(entry.block_index) * static_cast<u32>(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<u32>(entry.block_index) * static_cast<u32>(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 b9f7bdf601..fcf096aec3 100644 --- a/Kernel/FileSystem/BlockBasedFileSystem.h +++ b/Kernel/FileSystem/BlockBasedFileSystem.h @@ -42,17 +42,17 @@ public: 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 01b78c44c0..d05437591a 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 3c302180fe..8ac7571136 100644 --- a/Kernel/FileSystem/DevPtsFS.h +++ b/Kernel/FileSystem/DevPtsFS.h @@ -67,12 +67,12 @@ private: 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<bool(const FS::DirectoryEntryView&)>) const override; virtual RefPtr<Inode> 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<NonnullRefPtr<Inode>> 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 cbc9a93968..52b3037c68 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::BlockIndex> 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<const __u32*>(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<Inode> Ext2FS::get_inode(InodeIdentifier inode) const return {}; auto new_inode = adopt(*new Ext2FSInode(const_cast<Ext2FS&>(*this), inode.index())); - read_block(block_index, reinterpret_cast<u8*>(&new_inode->m_raw_inode), sizeof(ext2_inode), offset); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast<u8*>(&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<off_t>(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<Ext2FSDirectoryEntry>& 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<const u8*>(&e2inode), inode_size(), offset); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast<u8*>((const u8*)&e2inode)); + return write_block(block_index, buffer, inode_size(), offset) >= 0; } Vector<Ext2FS::BlockIndex> 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<CachedBitmap>(bitmap_block_index, move(block))); return *m_cached_bitmaps.last(); } diff --git a/Kernel/FileSystem/Ext2FileSystem.h b/Kernel/FileSystem/Ext2FileSystem.h index bdaa3255ca..7d28b84ff2 100644 --- a/Kernel/FileSystem/Ext2FileSystem.h +++ b/Kernel/FileSystem/Ext2FileSystem.h @@ -58,12 +58,12 @@ public: 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<bool(const FS::DirectoryEntryView&)>) const override; virtual RefPtr<Inode> 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<NonnullRefPtr<Inode>> 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 3f9c1e33a1..2aa6c55cd9 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<size_t> FIFO::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr<size_t> 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<size_t> FIFO::write(FileDescription&, size_t, const u8* buffer, size_t size) +KResultOr<size_t> 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 81b04597f2..8e3a146012 100644 --- a/Kernel/FileSystem/FIFO.h +++ b/Kernel/FileSystem/FIFO.h @@ -57,8 +57,8 @@ public: private: // ^File - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> 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 150f04f518..b2400b803b 100644 --- a/Kernel/FileSystem/File.h +++ b/Kernel/FileSystem/File.h @@ -34,6 +34,7 @@ #include <Kernel/Forward.h> #include <Kernel/KResult.h> #include <Kernel/UnixTypes.h> +#include <Kernel/UserOrKernelBuffer.h> #include <Kernel/VirtualAddress.h> namespace Kernel { @@ -77,8 +78,8 @@ public: virtual bool can_read(const FileDescription&, size_t) const = 0; virtual bool can_write(const FileDescription&, size_t) const = 0; - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) = 0; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) = 0; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) = 0; + virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) = 0; virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg); virtual KResultOr<Region*> 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 80153e6225..8c88dfc816 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<size_t> FileDescription::read(u8* buffer, size_t count) +KResultOr<size_t> FileDescription::read(UserOrKernelBuffer& buffer, size_t count) { LOCKER(m_lock); Checked<size_t> new_offset = m_current_offset; @@ -130,7 +130,7 @@ KResultOr<size_t> FileDescription::read(u8* buffer, size_t count) return nread_or_error; } -KResultOr<size_t> FileDescription::write(const u8* data, size_t size) +KResultOr<size_t> FileDescription::write(const UserOrKernelBuffer& data, size_t size) { LOCKER(m_lock); Checked<size_t> new_offset = m_current_offset; @@ -162,7 +162,7 @@ KResultOr<KBuffer> 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_t>(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 3427ad1e18..60d85db945 100644 --- a/Kernel/FileSystem/FileDescription.h +++ b/Kernel/FileSystem/FileDescription.h @@ -60,8 +60,8 @@ public: KResult close(); off_t seek(off_t, int whence); - KResultOr<size_t> read(u8*, size_t); - KResultOr<size_t> write(const u8* data, size_t); + KResultOr<size_t> read(UserOrKernelBuffer&, size_t); + KResultOr<size_t> write(const UserOrKernelBuffer& data, size_t); KResult stat(::stat&); KResult chmod(mode_t); @@ -69,7 +69,7 @@ public: 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<KBuffer> read_entire_file(); diff --git a/Kernel/FileSystem/FileSystem.h b/Kernel/FileSystem/FileSystem.h index 19fea4bafd..99a8131ca4 100644 --- a/Kernel/FileSystem/FileSystem.h +++ b/Kernel/FileSystem/FileSystem.h @@ -34,6 +34,7 @@ #include <Kernel/KResult.h> #include <Kernel/Lock.h> #include <Kernel/UnixTypes.h> +#include <Kernel/UserOrKernelBuffer.h> namespace Kernel { diff --git a/Kernel/FileSystem/Inode.cpp b/Kernel/FileSystem/Inode.cpp index e78f87bf59..b0e91bafc5 100644 --- a/Kernel/FileSystem/Inode.cpp +++ b/Kernel/FileSystem/Inode.cpp @@ -73,7 +73,10 @@ KResultOr<KBuffer> 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 f9ed0db158..d506ab0d18 100644 --- a/Kernel/FileSystem/Inode.h +++ b/Kernel/FileSystem/Inode.h @@ -69,10 +69,10 @@ public: KResultOr<KBuffer> 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<bool(const FS::DirectoryEntryView&)>) const = 0; virtual RefPtr<Inode> 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<NonnullRefPtr<Inode>> 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 @@ public: 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 c2657fe969..bc4d243957 100644 --- a/Kernel/FileSystem/InodeFile.cpp +++ b/Kernel/FileSystem/InodeFile.cpp @@ -44,7 +44,7 @@ InodeFile::~InodeFile() { } -KResultOr<size_t> InodeFile::read(FileDescription& description, size_t offset, u8* buffer, size_t count) +KResultOr<size_t> 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<size_t> InodeFile::read(FileDescription& description, size_t offset, u return nread; } -KResultOr<size_t> InodeFile::write(FileDescription& description, size_t offset, const u8* data, size_t count) +KResultOr<size_t> 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 ab64201b3b..e051476b2b 100644 --- a/Kernel/FileSystem/InodeFile.h +++ b/Kernel/FileSystem/InodeFile.h @@ -47,8 +47,8 @@ public: 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<size_t> read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; virtual KResultOr<Region*> 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 a4612a588d..6e76cc69b6 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<size_t> InodeWatcher::read(FileDescription&, size_t, u8* buffer, size_t buffer_size) +KResultOr<size_t> 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<size_t> 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)>(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<size_t> InodeWatcher::write(FileDescription&, size_t, const u8*, size_t) +KResultOr<size_t> 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 31a094b88f..cd371e2f1a 100644 --- a/Kernel/FileSystem/InodeWatcher.h +++ b/Kernel/FileSystem/InodeWatcher.h @@ -55,8 +55,8 @@ public: virtual bool can_read(const FileDescription&, size_t) const override; virtual bool can_write(const FileDescription&, size_t) const override; - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> 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 952a176066..975883df67 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<Thread::WriteBlocker>(nullptr, description).was_interrupted()) return KResult(-EINTR); } - auto nwritten_or_error = description.write(data, size); + auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast<u8*>(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<Thread::ReadBlocker>(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<Plan9FSInode&>(*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 77af39e47a..5851cbe8dd 100644 --- a/Kernel/FileSystem/Plan9FileSystem.h +++ b/Kernel/FileSystem/Plan9FileSystem.h @@ -124,8 +124,8 @@ public: // ^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<bool(const FS::DirectoryEntryView&)>) const override; virtual RefPtr<Inode> lookup(StringView name) override; virtual KResultOr<NonnullRefPtr<Inode>> 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 0fe482cf36..eef030b29f 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<Lockable<bool>*>(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<Lockable<String>*>(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<bool>& var, Function<void()>&& 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<off_t>(data.value().size() - offset), static_cast<off_t>(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<ssize_t(InodeIdentifier, const ByteBuffer&)> callback_tmp; - Function<ssize_t(InodeIdentifier, const ByteBuffer&)>* write_callback { nullptr }; + Function<ssize_t(InodeIdentifier, const UserOrKernelBuffer&, size_t)> callback_tmp; + Function<ssize_t(InodeIdentifier, const UserOrKernelBuffer&, size_t)>* 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<u8*>(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<NonnullRefPtr<Custody>> ProcFSInode::resolve_as_link(Custody& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level) const diff --git a/Kernel/FileSystem/ProcFS.h b/Kernel/FileSystem/ProcFS.h index a68df543f0..5d2d3fb124 100644 --- a/Kernel/FileSystem/ProcFS.h +++ b/Kernel/FileSystem/ProcFS.h @@ -59,7 +59,7 @@ private: struct ProcFSDirectoryEntry { ProcFSDirectoryEntry() { } - ProcFSDirectoryEntry(const char* a_name, unsigned a_proc_file_type, bool a_supervisor_only, Function<Optional<KBuffer>(InodeIdentifier)>&& a_read_callback = nullptr, Function<ssize_t(InodeIdentifier, const ByteBuffer&)>&& a_write_callback = nullptr, RefPtr<ProcFSInode>&& a_inode = nullptr) + ProcFSDirectoryEntry(const char* a_name, unsigned a_proc_file_type, bool a_supervisor_only, Function<Optional<KBuffer>(InodeIdentifier)>&& a_read_callback = nullptr, Function<ssize_t(InodeIdentifier, const UserOrKernelBuffer&, size_t)>&& a_write_callback = nullptr, RefPtr<ProcFSInode>&& a_inode = nullptr) : name(a_name) , proc_file_type(a_proc_file_type) , supervisor_only(a_supervisor_only) @@ -73,7 +73,7 @@ private: unsigned proc_file_type { 0 }; bool supervisor_only { false }; Function<Optional<KBuffer>(InodeIdentifier)> read_callback; - Function<ssize_t(InodeIdentifier, const ByteBuffer&)> write_callback; + Function<ssize_t(InodeIdentifier, const UserOrKernelBuffer&, size_t)> write_callback; RefPtr<ProcFSInode> inode; InodeIdentifier identifier(unsigned fsid) const; }; @@ -96,12 +96,12 @@ public: 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<bool(const FS::DirectoryEntryView&)>) const override; virtual RefPtr<Inode> 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<NonnullRefPtr<Inode>> 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 @@ public: 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<bool(const FS::DirectoryEntryView&)>) const override { ASSERT_NOT_REACHED(); } virtual RefPtr<Inode> 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<NonnullRefPtr<Inode>> 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 04ac98d33d..31222d6e87 100644 --- a/Kernel/FileSystem/TmpFS.cpp +++ b/Kernel/FileSystem/TmpFS.cpp @@ -141,7 +141,7 @@ KResult TmpFSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntry return KSuccess; } -ssize_t TmpFSInode::read_bytes(off_t offset, ssize_t size, u8* buffer, FileDescription*) const +ssize_t TmpFSInode::read_bytes(off_t offset, ssize_t size, UserOrKernelBuffer& buffer, FileDescription*) const { LOCKER(m_lock, Lock::Mode::Shared); ASSERT(!is_directory()); @@ -157,11 +157,12 @@ ssize_t TmpFSInode::read_bytes(off_t offset, ssize_t size, u8* buffer, FileDescr if (static_cast<off_t>(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 bb77e6b946..02eed3f17f 100644 --- a/Kernel/FileSystem/TmpFS.h +++ b/Kernel/FileSystem/TmpFS.h @@ -74,12 +74,12 @@ public: const TmpFS& fs() const { return static_cast<const TmpFS&>(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<bool(const FS::DirectoryEntryView&)>) const override; virtual RefPtr<Inode> 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<NonnullRefPtr<Inode>> 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 ea06d462c3..c0c93dfecb 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<u8*>((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 8a40cdce04..66afa30896 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 5a7d26caac..69c01318e0 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 7ddead3e15..c64c036c4d 100644 --- a/Kernel/Net/IPv4Socket.cpp +++ b/Kernel/Net/IPv4Socket.cpp @@ -105,7 +105,8 @@ KResult IPv4Socket::bind(Userspace<const sockaddr*> 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<const sockaddr*> 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<const sockaddr*>(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<size_t> IPv4Socket::sendto(FileDescription&, const void* data, size_t data_length, int flags, Userspace<const sockaddr*> addr, socklen_t addr_length) +KResultOr<size_t> IPv4Socket::sendto(FileDescription&, const UserOrKernelBuffer& data, size_t data_length, int flags, Userspace<const sockaddr*> addr, socklen_t addr_length) { (void)flags; if (addr && addr_length != sizeof(sockaddr_in)) @@ -201,7 +209,7 @@ KResultOr<size_t> IPv4Socket::sendto(FileDescription&, const void* data, size_t if (addr) { sockaddr_in ia; - if (!Process::current()->validate_read_and_copy_typed(&ia, Userspace<const sockaddr_in*>(addr.ptr()))) + if (!copy_from_user(&ia, Userspace<const sockaddr_in*>(addr.ptr()))) return KResult(-EFAULT); if (ia.sin_family != AF_INET) { @@ -229,7 +237,9 @@ KResultOr<size_t> 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<size_t> IPv4Socket::sendto(FileDescription&, const void* data, size_t return nsent_or_error; } -KResultOr<size_t> IPv4Socket::receive_byte_buffered(FileDescription& description, void* buffer, size_t buffer_length, int, Userspace<sockaddr*>, Userspace<socklen_t*>) +KResultOr<size_t> IPv4Socket::receive_byte_buffered(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int, Userspace<sockaddr*>, Userspace<socklen_t*>) { Locker locker(lock()); if (m_receive_buffer.is_empty()) { @@ -262,7 +272,7 @@ KResultOr<size_t> 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<size_t> IPv4Socket::receive_byte_buffered(FileDescription& description return nreceived; } -KResultOr<size_t> IPv4Socket::receive_packet_buffered(FileDescription& description, void* buffer, size_t buffer_length, int flags, Userspace<sockaddr*> addr, Userspace<socklen_t*> addr_length) +KResultOr<size_t> IPv4Socket::receive_packet_buffered(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*> addr, Userspace<socklen_t*> addr_length) { Locker locker(lock()); ReceivedPacket packet; @@ -330,27 +340,30 @@ KResultOr<size_t> IPv4Socket::receive_packet_buffered(FileDescription& descripti out_addr.sin_port = htons(packet.peer_port); out_addr.sin_family = AF_INET; Userspace<sockaddr_in*> 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<size_t> IPv4Socket::recvfrom(FileDescription& description, void* buffer, size_t buffer_length, int flags, Userspace<sockaddr*> user_addr, Userspace<socklen_t*> user_addr_length) +KResultOr<size_t> IPv4Socket::recvfrom(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*> user_addr, Userspace<socklen_t*> 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<const void*> use if (user_value_size < sizeof(int)) return KResult(-EINVAL); int value; - if (!Process::current()->validate_read_and_copy_typed(&value, static_ptr_cast<const int*>(user_value))) + if (!copy_from_user(&value, static_ptr_cast<const int*>(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<int*>(value), (int*)&m_ttl); + if (!copy_to_user(static_ptr_cast<int*>(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 cb513247ac..675cfbfcd1 100644 --- a/Kernel/Net/IPv4Socket.h +++ b/Kernel/Net/IPv4Socket.h @@ -50,7 +50,7 @@ public: virtual KResult close() override; virtual KResult bind(Userspace<const sockaddr*>, socklen_t) override; - virtual KResult connect(FileDescription&, const sockaddr*, socklen_t, ShouldBlock = ShouldBlock::Yes) override; + virtual KResult connect(FileDescription&, Userspace<const sockaddr*>, 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 @@ public: 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<size_t> sendto(FileDescription&, const void*, size_t, int, Userspace<const sockaddr*>, socklen_t) override; - virtual KResultOr<size_t> recvfrom(FileDescription&, void*, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>) override; + virtual KResultOr<size_t> sendto(FileDescription&, const UserOrKernelBuffer&, size_t, int, Userspace<const sockaddr*>, socklen_t) override; + virtual KResultOr<size_t> recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>) override; virtual KResult setsockopt(int level, int option, Userspace<const void*>, socklen_t) override; virtual KResult getsockopt(FileDescription&, int level, int option, Userspace<void*>, Userspace<socklen_t*>) override; @@ -96,8 +96,8 @@ protected: virtual KResult protocol_bind() { return KSuccess; } virtual KResult protocol_listen() { return KSuccess; } - virtual KResultOr<size_t> protocol_receive(const KBuffer&, void*, size_t, int) { return -ENOTIMPL; } - virtual KResultOr<size_t> protocol_send(const void*, size_t) { return -ENOTIMPL; } + virtual KResultOr<size_t> protocol_receive(const KBuffer&, UserOrKernelBuffer&, size_t, int) { return -ENOTIMPL; } + virtual KResultOr<size_t> 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 @@ protected: private: virtual bool is_ipv4() const override { return true; } - KResultOr<size_t> receive_byte_buffered(FileDescription&, void* buffer, size_t buffer_length, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>); - KResultOr<size_t> receive_packet_buffered(FileDescription&, void* buffer, size_t buffer_length, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>); + KResultOr<size_t> receive_byte_buffered(FileDescription&, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>); + KResultOr<size_t> receive_packet_buffered(FileDescription&, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>); IPv4Address m_local_address; IPv4Address m_peer_address; diff --git a/Kernel/Net/LocalSocket.cpp b/Kernel/Net/LocalSocket.cpp index a401af1e9f..ee08ca0c26 100644 --- a/Kernel/Net/LocalSocket.cpp +++ b/Kernel/Net/LocalSocket.cpp @@ -98,7 +98,8 @@ KResult LocalSocket::bind(Userspace<const sockaddr*> 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<const sockaddr*> 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<const sockaddr*> 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<const sockaddr*>(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<const sockaddr_un*>(address); + const auto& local_address = *reinterpret_cast<const sockaddr_un*>(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<size_t> LocalSocket::sendto(FileDescription& description, const void* data, size_t data_size, int, Userspace<const sockaddr*>, socklen_t) +KResultOr<size_t> LocalSocket::sendto(FileDescription& description, const UserOrKernelBuffer& data, size_t data_size, int, Userspace<const sockaddr*>, 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<size_t> LocalSocket::recvfrom(FileDescription& description, void* buffer, size_t buffer_size, int, Userspace<sockaddr*>, Userspace<socklen_t*>) +KResultOr<size_t> LocalSocket::recvfrom(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_size, int, Userspace<sockaddr*>, Userspace<socklen_t*>) { auto& buffer_for_me = receive_buffer_for(description); if (!description.is_blocking()) { @@ -306,7 +314,7 @@ KResultOr<size_t> 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<ucred*>(value), &m_origin); + if (!copy_to_user(static_ptr_cast<ucred*>(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<ucred*>(value), &m_acceptor); + if (!copy_to_user(static_ptr_cast<ucred*>(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 cafdf599a4..d39e78d20b 100644 --- a/Kernel/Net/LocalSocket.h +++ b/Kernel/Net/LocalSocket.h @@ -52,7 +52,7 @@ public: // ^Socket virtual KResult bind(Userspace<const sockaddr*>, socklen_t) override; - virtual KResult connect(FileDescription&, const sockaddr*, socklen_t, ShouldBlock = ShouldBlock::Yes) override; + virtual KResult connect(FileDescription&, Userspace<const sockaddr*>, 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 @@ public: 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<size_t> sendto(FileDescription&, const void*, size_t, int, Userspace<const sockaddr*>, socklen_t) override; - virtual KResultOr<size_t> recvfrom(FileDescription&, void*, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>) override; + virtual KResultOr<size_t> sendto(FileDescription&, const UserOrKernelBuffer&, size_t, int, Userspace<const sockaddr*>, socklen_t) override; + virtual KResultOr<size_t> recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>) override; virtual KResult getsockopt(FileDescription&, int level, int option, Userspace<void*>, Userspace<socklen_t*>) 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 4cfdd1cd43..4a81fa77af 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<u16>(); @@ -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 7fc178bf7d..619b25f281 100644 --- a/Kernel/Net/NetworkAdapter.h +++ b/Kernel/Net/NetworkAdapter.h @@ -37,6 +37,7 @@ #include <Kernel/Net/ARP.h> #include <Kernel/Net/ICMP.h> #include <Kernel/Net/IPv4.h> +#include <Kernel/UserOrKernelBuffer.h> namespace Kernel { @@ -63,8 +64,8 @@ public: 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 f198bc0607..0098d686fd 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 061068e3ec..1caa3c5328 100644 --- a/Kernel/Net/Socket.cpp +++ b/Kernel/Net/Socket.cpp @@ -108,18 +108,20 @@ KResult Socket::setsockopt(int level, int option, Userspace<const void*> user_va case SO_SNDTIMEO: if (user_value_size != sizeof(timeval)) return KResult(-EINVAL); - copy_from_user(&m_send_timeout, static_ptr_cast<const timeval*>(user_value)); + if (!copy_from_user(&m_send_timeout, static_ptr_cast<const timeval*>(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<const timeval*>(user_value)); + if (!copy_from_user(&m_receive_timeout, static_ptr_cast<const timeval*>(user_value))) + return KResult(-EFAULT); return KSuccess; case SO_BINDTODEVICE: { if (user_value_size != IFNAMSIZ) return KResult(-EINVAL); auto user_string = static_ptr_cast<const char*>(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<const void*> user_va KResult Socket::getsockopt(FileDescription&, int level, int option, Userspace<void*> value, Userspace<socklen_t*> 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<vo case SO_SNDTIMEO: if (size < sizeof(timeval)) return KResult(-EINVAL); - copy_to_user(static_ptr_cast<timeval*>(value), &m_send_timeout); + if (!copy_to_user(static_ptr_cast<timeval*>(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<timeval*>(value), &m_receive_timeout); + if (!copy_to_user(static_ptr_cast<timeval*>(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<int*>(value), &errno); + if (!copy_to_user(static_ptr_cast<int*>(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, Userspace<vo if (m_bound_interface) { const auto& name = m_bound_interface->name(); auto length = name.length() + 1; - copy_to_user(static_ptr_cast<char*>(value), name.characters(), length); + if (!copy_to_user(static_ptr_cast<char*>(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<vo } } -KResultOr<size_t> Socket::read(FileDescription& description, size_t, u8* buffer, size_t size) +KResultOr<size_t> 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<size_t> Socket::write(FileDescription& description, size_t, const u8* data, size_t size) +KResultOr<size_t> 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 b72fa5f837..c768793292 100644 --- a/Kernel/Net/Socket.h +++ b/Kernel/Net/Socket.h @@ -99,7 +99,7 @@ public: KResult shutdown(int how); virtual KResult bind(Userspace<const sockaddr*>, socklen_t) = 0; - virtual KResult connect(FileDescription&, const sockaddr*, socklen_t, ShouldBlock) = 0; + virtual KResult connect(FileDescription&, Userspace<const sockaddr*>, 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 @@ public: virtual bool is_ipv4() const { return false; } virtual void attach(FileDescription&) = 0; virtual void detach(FileDescription&) = 0; - virtual KResultOr<size_t> sendto(FileDescription&, const void*, size_t, int flags, Userspace<const sockaddr*>, socklen_t) = 0; - virtual KResultOr<size_t> recvfrom(FileDescription&, void*, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>) = 0; + virtual KResultOr<size_t> sendto(FileDescription&, const UserOrKernelBuffer&, size_t, int flags, Userspace<const sockaddr*>, socklen_t) = 0; + virtual KResultOr<size_t> recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>) = 0; virtual KResult setsockopt(int level, int option, Userspace<const void*>, socklen_t); virtual KResult getsockopt(FileDescription&, int level, int option, Userspace<void*>, Userspace<socklen_t*>); @@ -124,8 +124,8 @@ public: Lock& lock() { return m_lock; } // ^File - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override final; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override final; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override final; + virtual KResultOr<size_t> 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 b7b615c951..e14ecf2770 100644 --- a/Kernel/Net/TCPSocket.cpp +++ b/Kernel/Net/TCPSocket.cpp @@ -161,7 +161,7 @@ NonnullRefPtr<TCPSocket> TCPSocket::create(int protocol) return adopt(*new TCPSocket(protocol)); } -KResultOr<size_t> TCPSocket::protocol_receive(const KBuffer& packet_buffer, void* buffer, size_t buffer_size, int flags) +KResultOr<size_t> 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<size_t> 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<size_t> TCPSocket::protocol_send(const void* data, size_t data_length) +KResultOr<size_t> 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 bff5ac14c2..0a8e25119d 100644 --- a/Kernel/Net/TCPSocket.h +++ b/Kernel/Net/TCPSocket.h @@ -148,7 +148,7 @@ public: 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 @@ private: virtual void shut_down_for_writing() override; - virtual KResultOr<size_t> protocol_receive(const KBuffer&, void* buffer, size_t buffer_size, int flags) override; - virtual KResultOr<size_t> protocol_send(const void*, size_t) override; + virtual KResultOr<size_t> protocol_receive(const KBuffer&, UserOrKernelBuffer& buffer, size_t buffer_size, int flags) override; + virtual KResultOr<size_t> 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 c770a4e184..22db799af7 100644 --- a/Kernel/Net/UDPSocket.cpp +++ b/Kernel/Net/UDPSocket.cpp @@ -79,18 +79,19 @@ NonnullRefPtr<UDPSocket> UDPSocket::create(int protocol) return adopt(*new UDPSocket(protocol)); } -KResultOr<size_t> UDPSocket::protocol_receive(const KBuffer& packet_buffer, void* buffer, size_t buffer_size, int flags) +KResultOr<size_t> 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<const UDPPacket*>(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<size_t> UDPSocket::protocol_send(const void* data, size_t data_length) +KResultOr<size_t> 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<size_t> 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 ab711a9dc8..d640be316e 100644 --- a/Kernel/Net/UDPSocket.h +++ b/Kernel/Net/UDPSocket.h @@ -43,8 +43,8 @@ private: virtual const char* class_name() const override { return "UDPSocket"; } static Lockable<HashMap<u16, UDPSocket*>>& sockets_by_port(); - virtual KResultOr<size_t> protocol_receive(const KBuffer&, void* buffer, size_t buffer_size, int flags) override; - virtual KResultOr<size_t> protocol_send(const void*, size_t) override; + virtual KResultOr<size_t> protocol_receive(const KBuffer&, UserOrKernelBuffer& buffer, size_t buffer_size, int flags) override; + virtual KResultOr<size_t> 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 2fc9c11e6b..5e1348b07a 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<String> 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<String> 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 6f19527c0a..76bc8c51d7 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -362,111 +362,6 @@ public: 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<typename T> - [[nodiscard]] bool validate_read(Userspace<T*> ptr, size_t size) const - { - return validate_read(ptr.unsafe_userspace_ptr(), size); - } - - template<typename T> - [[nodiscard]] bool validate_write(Userspace<T*> ptr, size_t size) const - { - return validate_write(ptr.unsafe_userspace_ptr(), size); - } - - template<typename T> - [[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<typename T> - [[nodiscard]] bool validate_read_typed(Userspace<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<typename T> - [[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<typename T> - [[nodiscard]] bool validate_read_and_copy_typed(T* dest, Userspace<const T*> src) - { - bool validated = validate_read_typed(src); - if (validated) { - copy_from_user(dest, src); - } - return validated; - } - - template<typename T> - [[nodiscard]] bool validate_read_and_copy_typed(T* dest, Userspace<T*> src) - { - Userspace<const T*> const_src { src.ptr() }; - return validate_read_and_copy_typed(dest, const_src); - } - - template<typename T> - [[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<typename T> - [[nodiscard]] bool validate_write_typed(Userspace<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<typename DataType, typename SizeType> - [[nodiscard]] bool validate(const Syscall::MutableBufferArgument<DataType, SizeType>& buffer) - { - return validate_write(buffer.data, buffer.size); - } - - template<typename DataType, typename SizeType> - [[nodiscard]] bool validate(const Syscall::ImmutableBufferArgument<DataType, SizeType>& 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<const char*> 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 @@ private: void kill_all_threads(); int do_exec(NonnullRefPtr<FileDescription> main_program_description, Vector<String> arguments, Vector<String> environment, RefPtr<FileDescription> 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<NonnullRefPtr<FileDescription>> find_elf_interpreter_for_executable(const String& path, char (&first_page)[PAGE_SIZE], int nread, size_t file_size); Vector<AuxiliaryValue> 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 ab085c3054..8e8bc6c7bf 100644 --- a/Kernel/Ptrace.cpp +++ b/Kernel/Ptrace.cpp @@ -105,10 +105,9 @@ KResultOr<u32> handle_syscall(const Kernel::Syscall::SC_ptrace_params& params, P if (!tracer->has_regs()) return KResult(-EINVAL); - auto* regs = reinterpret_cast<PtraceRegisters*>(params.addr.unsafe_userspace_ptr()); - if (!caller.validate_write_typed(regs)) + auto* regs = reinterpret_cast<PtraceRegisters*>(params.addr); + if (!copy_to_user(regs, &tracer->regs())) return KResult(-EFAULT); - copy_to_user(regs, &tracer->regs()); break; } @@ -117,7 +116,7 @@ KResultOr<u32> 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<u32> 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<Kernel::Syscall::SC_ptrace_peek_params*>(params.addr.unsafe_userspace_ptr()))) + if (!copy_from_user(&peek_params, reinterpret_cast<Kernel::Syscall::SC_ptrace_peek_params*>(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<u32*> addr = reinterpret_cast<FlatPtr>(params.addr.ptr()); + Userspace<u32*> addr = reinterpret_cast<FlatPtr>(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 620558caf7..2a1e6e82a7 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 27dee8d3c4..1bdb4c912b 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<const char*> 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 1a9398700f..00b8912987 100644 --- a/Kernel/StdLib.h +++ b/Kernel/StdLib.h @@ -26,6 +26,7 @@ #pragma once +#include <AK/Checked.h> #include <AK/Forward.h> #include <AK/Userspace.h> @@ -34,12 +35,13 @@ struct StringArgument; } String copy_string_from_user(const char*, size_t); +String copy_string_from_user(Userspace<const char*>, 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<typename T> -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<typename T> -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<typename T> -inline void copy_from_user(T* dest, Userspace<const T*> src) +[[nodiscard]] inline bool copy_from_user(T* dest, Userspace<const T*> src) { - copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T)); + return copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T)); } template<typename T> -inline void copy_to_user(Userspace<T*> dest, const T* src) +[[nodiscard]] inline bool copy_to_user(Userspace<T*> 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<typename T> -inline void copy_to_user(Userspace<T*> dest, const void* src, size_t size) +[[nodiscard]] inline bool copy_to_user(Userspace<T*> 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<typename T> -inline void copy_from_user(void* dest, Userspace<const T*> src, size_t size) +[[nodiscard]] inline bool copy_from_user(void* dest, Userspace<const T*> src, size_t size) { - copy_from_user(dest, src.unsafe_userspace_ptr(), size); + return copy_from_user(dest, src.unsafe_userspace_ptr(), size); +} + +template<typename T> +[[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<typename T> +[[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<typename T> +[[nodiscard]] inline bool copy_n_from_user(T* dest, Userspace<const T*> 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<typename T> +[[nodiscard]] inline bool copy_n_to_user(Userspace<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.unsafe_userspace_ptr(), src, sizeof(T) * count); } diff --git a/Kernel/Syscalls/chdir.cpp b/Kernel/Syscalls/chdir.cpp index 68437ab0a9..46cf3a2459 100644 --- a/Kernel/Syscalls/chdir.cpp +++ b/Kernel/Syscalls/chdir.cpp @@ -66,12 +66,11 @@ int Process::sys$getcwd(Userspace<char*> buffer, ssize_t size) REQUIRE_PROMISE(rpath); if (size < 0) return -EINVAL; - if (!validate_write(buffer, size)) - return -EFAULT; auto path = current_directory().absolute_path(); if ((size_t)size < path.length() + 1) return -ERANGE; - copy_to_user(buffer, path.characters(), path.length() + 1); + if (!copy_to_user(buffer, path.characters(), path.length() + 1)) + return -EFAULT; return 0; } diff --git a/Kernel/Syscalls/chown.cpp b/Kernel/Syscalls/chown.cpp index 8de435c468..e07ab19f8a 100644 --- a/Kernel/Syscalls/chown.cpp +++ b/Kernel/Syscalls/chown.cpp @@ -42,7 +42,7 @@ int Process::sys$chown(Userspace<const Syscall::SC_chown_params*> user_params) { REQUIRE_PROMISE(chown); Syscall::SC_chown_params params; - if (!validate_read_and_copy_typed(¶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 de83b63035..3ee0c85339 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<timespec*> user_ts) { REQUIRE_PROMISE(stdio); - if (!validate_write_typed(user_ts)) - return -EFAULT; timespec ts = {}; @@ -49,7 +47,8 @@ int Process::sys$clock_gettime(clockid_t clock_id, Userspace<timespec*> user_ts) return -EINVAL; } - copy_to_user(user_ts, &ts); + if (!copy_to_user(user_ts, &ts)) + return -EFAULT; return 0; } @@ -61,7 +60,7 @@ int Process::sys$clock_settime(clockid_t clock_id, Userspace<const timespec*> us return -EPERM; timespec ts; - if (!validate_read_and_copy_typed(&ts, user_ts)) + if (!copy_from_user(&ts, user_ts)) return -EFAULT; switch (clock_id) { @@ -79,16 +78,11 @@ int Process::sys$clock_nanosleep(Userspace<const Syscall::SC_clock_nanosleep_par REQUIRE_PROMISE(stdio); Syscall::SC_clock_nanosleep_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) - return -EFAULT; - - if (params.requested_sleep && !validate_read_typed(params.requested_sleep)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; timespec requested_sleep; - copy_from_user(&requested_sleep, params.requested_sleep); - - if (params.remaining_sleep && !validate_write_typed(params.remaining_sleep)) + if (!copy_from_user(&requested_sleep, params.requested_sleep)) return -EFAULT; bool is_absolute = params.flags & TIMER_ABSTIME; @@ -109,18 +103,12 @@ int Process::sys$clock_nanosleep(Userspace<const Syscall::SC_clock_nanosleep_par if (wakeup_time > g_uptime) { u64 ticks_left = wakeup_time - g_uptime; if (!is_absolute && params.remaining_sleep) { - if (!validate_write_typed(params.remaining_sleep)) { - // This can happen because the lock is dropped while - // sleeping, thus giving other threads the opportunity - // to make the region unwritable. - return -EFAULT; - } - timespec remaining_sleep = {}; remaining_sleep.tv_sec = ticks_left / TimeManagement::the().ticks_per_second(); ticks_left -= remaining_sleep.tv_sec * TimeManagement::the().ticks_per_second(); remaining_sleep.tv_nsec = ticks_left * 1000000000 / TimeManagement::the().ticks_per_second(); - copy_to_user(params.remaining_sleep, &remaining_sleep); + if (!copy_to_user(params.remaining_sleep, &remaining_sleep)) + return -EFAULT; } return -EINTR; } @@ -134,10 +122,9 @@ int Process::sys$clock_nanosleep(Userspace<const Syscall::SC_clock_nanosleep_par int Process::sys$gettimeofday(Userspace<timeval*> user_tv) { REQUIRE_PROMISE(stdio); - if (!validate_write_typed(user_tv)) - return -EFAULT; auto tv = kgettimeofday(); - copy_to_user(user_tv, &tv); + if (!copy_to_user(user_tv, &tv)) + return -EFAULT; return 0; } diff --git a/Kernel/Syscalls/debug.cpp b/Kernel/Syscalls/debug.cpp index bd22e8b4c2..8bafe6399d 100644 --- a/Kernel/Syscalls/debug.cpp +++ b/Kernel/Syscalls/debug.cpp @@ -27,6 +27,7 @@ #include <Kernel/IO.h> #include <Kernel/KSyms.h> #include <Kernel/Process.h> +#include <Kernel/UserOrKernelBuffer.h> namespace Kernel { @@ -44,13 +45,20 @@ int Process::sys$dbgputch(u8 ch) int Process::sys$dbgputstr(Userspace<const u8*> characters, int length) { - if (!length) + if (length <= 0) return 0; - if (!validate_read(characters, length)) - return -EFAULT; + SmapDisabler disabler; - for (int i = 0; i < length; ++i) - IO::out8(0xe9, characters.unsafe_userspace_ptr()[i]); + auto buffer = UserOrKernelBuffer::for_user_buffer(characters, length); + if (!buffer.has_value()) + return -EFAULT; + ssize_t nread = buffer.value().read_buffered<1024>(length, [&](const u8* buffer, size_t buffer_size) { + for (size_t i = 0; i < buffer_size; ++i) + IO::out8(0xe9, buffer[i]); + return (ssize_t)buffer_size; + }); + if (nread < 0) + return (int)nread; return 0; } diff --git a/Kernel/Syscalls/execve.cpp b/Kernel/Syscalls/execve.cpp index bb0012a8e1..0e95a4c29e 100644 --- a/Kernel/Syscalls/execve.cpp +++ b/Kernel/Syscalls/execve.cpp @@ -423,7 +423,8 @@ KResultOr<NonnullRefPtr<FileDescription>> Process::find_elf_interpreter_for_exec return KResult(-ENOEXEC); memset(first_page, 0, sizeof(first_page)); - auto nread_or_error = interpreter_description->read((u8*)&first_page, sizeof(first_page)); + auto first_page_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&first_page); + auto nread_or_error = interpreter_description->read(first_page_buffer, sizeof(first_page)); if (nread_or_error.is_error()) return KResult(-ENOEXEC); nread = nread_or_error.value(); @@ -490,7 +491,8 @@ int Process::exec(String path, Vector<String> arguments, Vector<String> environm // Read the first page of the program into memory so we can validate the binfmt of it char first_page[PAGE_SIZE]; - auto nread_or_error = description->read((u8*)&first_page, sizeof(first_page)); + auto first_page_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&first_page); + auto nread_or_error = description->read(first_page_buffer, sizeof(first_page)); if (nread_or_error.is_error()) return -ENOEXEC; @@ -554,7 +556,7 @@ int Process::sys$execve(Userspace<const Syscall::SC_execve_params*> user_params) // NOTE: Be extremely careful with allocating any kernel memory in exec(). // On success, the kernel stack will be lost. Syscall::SC_execve_params params; - if (!validate_read_and_copy_typed(¶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<const Syscall::SC_execve_params*> user_params) auto copy_user_strings = [this](const auto& list, auto& output) { if (!list.length) return true; - if (!validate_read_typed(list.strings, list.length)) + Checked size = sizeof(list.length); + size *= list.length; + if (size.has_overflow()) return false; Vector<Syscall::StringArgument, 32> strings; strings.resize(list.length); - copy_from_user(strings.data(), list.strings.unsafe_userspace_ptr(), list.length * sizeof(Syscall::StringArgument)); + if (!copy_from_user(strings.data(), list.strings, list.length * sizeof(Syscall::StringArgument))) + return false; for (size_t i = 0; i < list.length; ++i) { - auto string = validate_and_copy_string_from_user(strings[i]); + auto string = copy_string_from_user(strings[i]); if (string.is_null()) return false; output.append(move(string)); diff --git a/Kernel/Syscalls/futex.cpp b/Kernel/Syscalls/futex.cpp index 830579934c..cd0f8fd21f 100644 --- a/Kernel/Syscalls/futex.cpp +++ b/Kernel/Syscalls/futex.cpp @@ -55,21 +55,19 @@ int Process::sys$futex(Userspace<const Syscall::SC_futex_params*> user_params) REQUIRE_PROMISE(thread); Syscall::SC_futex_params params; - if (!validate_read_and_copy_typed(¶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<const Syscall::SC_futex_params*> user_params) } // FIXME: This is supposed to be interruptible by a signal, but right now WaitQueue cannot be interrupted. - WaitQueue& wait_queue = futex_queue(params.userspace_address); + WaitQueue& wait_queue = futex_queue((FlatPtr)params.userspace_address); Thread::BlockResult result = Thread::current()->wait_on(wait_queue, "Futex", optional_timeout); if (result == Thread::BlockResult::InterruptedByTimeout) { return -ETIMEDOUT; @@ -92,9 +90,9 @@ int Process::sys$futex(Userspace<const Syscall::SC_futex_params*> user_params) if (params.val == 0) return 0; if (params.val == 1) { - futex_queue(params.userspace_address).wake_one(); + futex_queue((FlatPtr)params.userspace_address).wake_one(); } else { - futex_queue(params.userspace_address).wake_n(params.val); + futex_queue((FlatPtr)params.userspace_address).wake_n(params.val); } break; } diff --git a/Kernel/Syscalls/get_dir_entries.cpp b/Kernel/Syscalls/get_dir_entries.cpp index 56d98cd9f0..a94bf584d3 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 e41bcb5ddb..a91a401946 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 53361d9263..c000c9cf87 100644 --- a/Kernel/Syscalls/getrandom.cpp +++ b/Kernel/Syscalls/getrandom.cpp @@ -26,6 +26,7 @@ #include <Kernel/Process.h> #include <Kernel/Random.h> +#include <Kernel/UserOrKernelBuffer.h> namespace Kernel { @@ -38,13 +39,15 @@ ssize_t Process::sys$getrandom(Userspace<void*> buffer, size_t buffer_size, [[ma if (buffer_size <= 0) return -EINVAL; - if (!validate_write(buffer, buffer_size)) - return -EFAULT; - SmapDisabler disabler; - // FIXME: We should really push Userspace<T> down through the interface. - get_good_random_bytes((u8*)buffer.ptr(), buffer_size); - return 0; + auto data_buffer = UserOrKernelBuffer::for_user_buffer(buffer, buffer_size); + if (!data_buffer.has_value()) + return -EFAULT; + ssize_t nwritten = data_buffer.value().write_buffered<1024>(buffer_size, [&](u8* buffer, size_t buffer_bytes) { + get_good_random_bytes(buffer, buffer_bytes); + return (ssize_t)buffer_bytes; + }); + return nwritten; } } diff --git a/Kernel/Syscalls/getuid.cpp b/Kernel/Syscalls/getuid.cpp index 699ad9b6ca..73889c8ca6 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<uid_t*> ruid, Userspace<uid_t*> euid, Userspace<uid_t*> suid) { REQUIRE_PROMISE(stdio); - if (!validate_write_typed(ruid) || !validate_write_typed(euid) || !validate_write_typed(suid)) + if (!copy_to_user(ruid, &m_uid) || !copy_to_user(euid, &m_euid) || !copy_to_user(suid, &m_suid)) return -EFAULT; - copy_to_user(ruid, &m_uid); - copy_to_user(euid, &m_euid); - copy_to_user(suid, &m_suid); return 0; } int Process::sys$getresgid(Userspace<gid_t*> rgid, Userspace<gid_t*> egid, Userspace<gid_t*> sgid) { REQUIRE_PROMISE(stdio); - if (!validate_write_typed(rgid) || !validate_write_typed(egid) || !validate_write_typed(sgid)) + if (!copy_to_user(rgid, &m_gid) || !copy_to_user(egid, &m_egid) || !copy_to_user(sgid, &m_sgid)) return -EFAULT; - copy_to_user(rgid, &m_gid); - copy_to_user(egid, &m_egid); - copy_to_user(sgid, &m_sgid); return 0; } @@ -83,10 +77,9 @@ int Process::sys$getgroups(ssize_t count, Userspace<gid_t*> user_gids) return m_extra_gids.size(); if (count != (int)m_extra_gids.size()) return -EINVAL; - if (!validate_write_typed(user_gids, m_extra_gids.size())) - return -EFAULT; - copy_to_user(user_gids, m_extra_gids.data(), sizeof(gid_t) * count); + if (!copy_to_user(user_gids, m_extra_gids.data(), sizeof(gid_t) * count)) + return -EFAULT; return 0; } diff --git a/Kernel/Syscalls/hostname.cpp b/Kernel/Syscalls/hostname.cpp index 490c6a169d..fa5c992b52 100644 --- a/Kernel/Syscalls/hostname.cpp +++ b/Kernel/Syscalls/hostname.cpp @@ -36,12 +36,11 @@ int Process::sys$gethostname(Userspace<char*> buffer, ssize_t size) REQUIRE_PROMISE(stdio); if (size < 0) return -EINVAL; - if (!validate_write(buffer, size)) - return -EFAULT; LOCKER(*g_hostname_lock, Lock::Mode::Shared); if ((size_t)size < (g_hostname->length() + 1)) return -ENAMETOOLONG; - copy_to_user(buffer, g_hostname->characters(), g_hostname->length() + 1); + if (!copy_to_user(buffer, g_hostname->characters(), g_hostname->length() + 1)) + return -EFAULT; return 0; } @@ -55,7 +54,10 @@ int Process::sys$sethostname(Userspace<const char*> hostname, ssize_t length) LOCKER(*g_hostname_lock, Lock::Mode::Exclusive); if (length > 64) return -ENAMETOOLONG; - *g_hostname = validate_and_copy_string_from_user(hostname, length); + auto copied_hostname = copy_string_from_user(hostname, length); + if (copied_hostname.is_null()) + return -EFAULT; + *g_hostname = move(copied_hostname); return 0; } diff --git a/Kernel/Syscalls/link.cpp b/Kernel/Syscalls/link.cpp index cbb3185e03..f3bf17ff3e 100644 --- a/Kernel/Syscalls/link.cpp +++ b/Kernel/Syscalls/link.cpp @@ -34,11 +34,13 @@ int Process::sys$link(Userspace<const Syscall::SC_link_params*> user_params) { REQUIRE_PROMISE(cpath); Syscall::SC_link_params params; - if (!validate_read_and_copy_typed(¶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<const Syscall::SC_symlink_params*> 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 1a599b138d..1b6a4d9444 100644 --- a/Kernel/Syscalls/mknod.cpp +++ b/Kernel/Syscalls/mknod.cpp @@ -34,7 +34,7 @@ int Process::sys$mknod(Userspace<const Syscall::SC_mknod_params*> user_params) { REQUIRE_PROMISE(dpath); Syscall::SC_mknod_params params; - if (!validate_read_and_copy_typed(¶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 e99be2775a..74dbe31edf 100644 --- a/Kernel/Syscalls/mmap.cpp +++ b/Kernel/Syscalls/mmap.cpp @@ -81,7 +81,7 @@ void* Process::sys$mmap(Userspace<const Syscall::SC_mmap_params*> user_params) REQUIRE_PROMISE(stdio); Syscall::SC_mmap_params params; - if (!validate_read_and_copy_typed(¶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<const Syscall::SC_mmap_params*> user_params) if (params.name.characters) { if (params.name.length > PATH_MAX) return (void*)-ENAMETOOLONG; - name = validate_and_copy_string_from_user(params.name); + name = copy_string_from_user(params.name); if (name.is_null()) return (void*)-EFAULT; } @@ -336,13 +336,13 @@ int Process::sys$set_mmap_name(Userspace<const Syscall::SC_set_mmap_name_params* REQUIRE_PROMISE(stdio); Syscall::SC_set_mmap_name_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; if (params.name.length > PATH_MAX) return -ENAMETOOLONG; - auto name = validate_and_copy_string_from_user(params.name); + auto name = copy_string_from_user(params.name); if (name.is_null()) return -EFAULT; @@ -351,7 +351,7 @@ int Process::sys$set_mmap_name(Userspace<const Syscall::SC_set_mmap_name_params* return -EINVAL; if (!region->is_mmap()) return -EPERM; - region->set_name(name); + region->set_name(move(name)); return 0; } diff --git a/Kernel/Syscalls/module.cpp b/Kernel/Syscalls/module.cpp index 6da8ff8144..f8d9e0ac9f 100644 --- a/Kernel/Syscalls/module.cpp +++ b/Kernel/Syscalls/module.cpp @@ -169,7 +169,7 @@ int Process::sys$module_unload(Userspace<const char*> user_name, size_t name_len REQUIRE_NO_PROMISES; - auto module_name = validate_and_copy_string_from_user(user_name, name_length); + auto module_name = copy_string_from_user(user_name, name_length); if (module_name.is_null()) return -EFAULT; diff --git a/Kernel/Syscalls/mount.cpp b/Kernel/Syscalls/mount.cpp index 68f9a3ba89..ca95f84636 100644 --- a/Kernel/Syscalls/mount.cpp +++ b/Kernel/Syscalls/mount.cpp @@ -43,15 +43,16 @@ int Process::sys$mount(Userspace<const Syscall::SC_mount_params*> user_params) REQUIRE_NO_PROMISES; Syscall::SC_mount_params params; - if (!validate_read_and_copy_typed(¶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<const char*> user_mountpoint, size_t mountpoin REQUIRE_NO_PROMISES; - if (!validate_read(user_mountpoint, mountpoint_length)) - return -EFAULT; - auto mountpoint = get_syscall_path_argument(user_mountpoint, mountpoint_length); if (mountpoint.is_error()) return mountpoint.error(); diff --git a/Kernel/Syscalls/open.cpp b/Kernel/Syscalls/open.cpp index 0d8a3d5e48..abcc413f01 100644 --- a/Kernel/Syscalls/open.cpp +++ b/Kernel/Syscalls/open.cpp @@ -34,7 +34,7 @@ namespace Kernel { int Process::sys$open(Userspace<const Syscall::SC_open_params*> user_params) { Syscall::SC_open_params params; - if (!validate_read_and_copy_typed(¶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 182c289857..3d82917c8f 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 4cd9345235..e90e7eefea 100644 --- a/Kernel/Syscalls/pledge.cpp +++ b/Kernel/Syscalls/pledge.cpp @@ -32,7 +32,7 @@ namespace Kernel { int Process::sys$pledge(Userspace<const Syscall::SC_pledge_params*> user_params) { Syscall::SC_pledge_params params; - if (!validate_read_and_copy_typed(¶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<const Syscall::SC_pledge_params*> user_params) String promises; if (params.promises.characters) { - promises = validate_and_copy_string_from_user(params.promises); + auto promises = copy_string_from_user(params.promises); if (promises.is_null()) return -EFAULT; } String execpromises; if (params.execpromises.characters) { - execpromises = validate_and_copy_string_from_user(params.execpromises); + execpromises = copy_string_from_user(params.execpromises); if (execpromises.is_null()) return -EFAULT; } diff --git a/Kernel/Syscalls/process.cpp b/Kernel/Syscalls/process.cpp index 6a58ee3388..2e166c1967 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<char*> buffer, size_t buffer_size) { REQUIRE_PROMISE(stdio); - if (!validate_write(buffer, buffer_size)) - return -EFAULT; - if (m_name.length() + 1 > buffer_size) return -ENAMETOOLONG; - copy_to_user(buffer, m_name.characters(), m_name.length() + 1); + if (!copy_to_user(buffer, m_name.characters(), m_name.length() + 1)) + return -EFAULT; return 0; } @@ -73,7 +71,7 @@ int Process::sys$set_process_name(Userspace<const char*> user_name, size_t user_ REQUIRE_PROMISE(proc); if (user_name_length > 256) return -ENAMETOOLONG; - auto name = validate_and_copy_string_from_user(user_name, user_name_length); + auto name = copy_string_from_user(user_name, user_name_length); if (name.is_null()) return -EFAULT; m_name = move(name); diff --git a/Kernel/Syscalls/ptrace.cpp b/Kernel/Syscalls/ptrace.cpp index 2f19df3056..8ca71c9860 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 <AK/ScopeGuard.h> #include <Kernel/Process.h> #include <Kernel/Ptrace.h> #include <Kernel/VM/MemoryManager.h> @@ -38,7 +39,7 @@ int Process::sys$ptrace(Userspace<const Syscall::SC_ptrace_params*> user_params) { REQUIRE_PROMISE(proc); Syscall::SC_ptrace_params params; - if (!validate_read_and_copy_typed(¶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<u32> Process::peek_user_data(Userspace<const u32*> address) { - if (!MM.validate_user_read(*this, VirtualAddress(address), sizeof(u32))) { - dbg() << "Invalid address for peek_user_data: " << address.ptr(); - return KResult(-EFAULT); - } uint32_t result; // This function can be called from the context of another // process that called PT_PEEK ProcessPagingScope scope(*this); - copy_from_user(&result, address); + if (!copy_from_user(&result, address)) { + dbg() << "Invalid address for peek_user_data: " << address.ptr(); + return KResult(-EFAULT); + } return result; } KResult Process::poke_user_data(Userspace<u32*> address, u32 data) { - // We validate for read (rather than write) because PT_POKE can write to readonly pages. - // So we effectively only care that the poke operation is trying to write to user pages. - if (!MM.validate_user_read(*this, VirtualAddress(address), sizeof(u32))) { - dbg() << "Invalid address for poke_user_data: " << address.ptr(); - return KResult(-EFAULT); - } ProcessPagingScope scope(*this); Range range = { VirtualAddress(address), sizeof(u32) }; auto* region = find_region_containing(range); @@ -97,18 +91,23 @@ KResult Process::poke_user_data(Userspace<u32*> address, u32 data) region->set_shared(false); } const bool was_writable = region->is_writable(); - if (!was_writable) //TODO refactor into scopeguard + if (!was_writable) { region->set_writable(true); region->remap(); } + ScopeGuard rollback([&]() { + if (!was_writable) { + region->set_writable(false); + region->remap(); + } + }); - copy_to_user(address, &data); - - if (!was_writable) { - region->set_writable(false); - region->remap(); + if (!copy_to_user(address, &data)) { + dbg() << "Invalid address for poke_user_data: " << address.ptr(); + return KResult(-EFAULT); } + return KResult(KSuccess); } diff --git a/Kernel/Syscalls/read.cpp b/Kernel/Syscalls/read.cpp index 82a774b75b..df2873a8fe 100644 --- a/Kernel/Syscalls/read.cpp +++ b/Kernel/Syscalls/read.cpp @@ -38,8 +38,6 @@ ssize_t Process::sys$read(int fd, Userspace<u8*> buffer, ssize_t size) return -EINVAL; if (size == 0) return 0; - if (!validate_write(buffer, size)) - return -EFAULT; #ifdef DEBUG_IO dbg() << "sys$read(" << fd << ", " << (const void*)buffer.ptr() << ", " << size << ")"; #endif @@ -58,7 +56,10 @@ ssize_t Process::sys$read(int fd, Userspace<u8*> buffer, ssize_t size) return -EAGAIN; } } - auto result = description->read(buffer.unsafe_userspace_ptr(), size); + auto user_buffer = UserOrKernelBuffer::for_user_buffer(buffer, size); + if (!user_buffer.has_value()) + return -EFAULT; + auto result = description->read(user_buffer.value(), size); if (result.is_error()) return result.error(); return result.value(); diff --git a/Kernel/Syscalls/readlink.cpp b/Kernel/Syscalls/readlink.cpp index d2b35733a9..b9da03db87 100644 --- a/Kernel/Syscalls/readlink.cpp +++ b/Kernel/Syscalls/readlink.cpp @@ -36,10 +36,7 @@ int Process::sys$readlink(Userspace<const Syscall::SC_readlink_params*> user_par REQUIRE_PROMISE(rpath); Syscall::SC_readlink_params params; - if (!validate_read_and_copy_typed(¶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<const Syscall::SC_readlink_params*> user_par auto& link_target = contents.value(); auto size_to_copy = min(link_target.size(), params.buffer.size); - copy_to_user(params.buffer.data, link_target.data(), size_to_copy); + if (!copy_to_user(params.buffer.data, link_target.data(), size_to_copy)) + return -EFAULT; // Note: we return the whole size here, not the copied size. return link_target.size(); } diff --git a/Kernel/Syscalls/realpath.cpp b/Kernel/Syscalls/realpath.cpp index 4728115a72..83f6cba839 100644 --- a/Kernel/Syscalls/realpath.cpp +++ b/Kernel/Syscalls/realpath.cpp @@ -36,10 +36,7 @@ int Process::sys$realpath(Userspace<const Syscall::SC_realpath_params*> user_par REQUIRE_PROMISE(rpath); Syscall::SC_realpath_params params; - if (!validate_read_and_copy_typed(¶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<const Syscall::SC_realpath_params*> user_par if (absolute_path.length() + 1 > params.buffer.size) return -ENAMETOOLONG; - copy_to_user(params.buffer.data, absolute_path.characters(), absolute_path.length() + 1); + if (!copy_to_user(params.buffer.data, absolute_path.characters(), absolute_path.length() + 1)) + return -EFAULT; return 0; }; diff --git a/Kernel/Syscalls/rename.cpp b/Kernel/Syscalls/rename.cpp index 0a06eaceb8..ef9463c860 100644 --- a/Kernel/Syscalls/rename.cpp +++ b/Kernel/Syscalls/rename.cpp @@ -34,7 +34,7 @@ int Process::sys$rename(Userspace<const Syscall::SC_rename_params*> user_params) { REQUIRE_PROMISE(cpath); Syscall::SC_rename_params params; - if (!validate_read_and_copy_typed(¶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 7729a817b0..91ca08884e 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<const struct sched_param*> user_param) { REQUIRE_PROMISE(proc); - if (!validate_read_typed(user_param)) - return -EFAULT; - struct sched_param desired_param; - copy_from_user(&desired_param, user_param); + if (!copy_from_user(&desired_param, user_param)) + return -EFAULT; InterruptDisabler disabler; auto* peer = Thread::current(); @@ -78,9 +76,6 @@ int Process::sys$sched_setparam(int pid, Userspace<const struct sched_param*> us int Process::sys$sched_getparam(pid_t pid, Userspace<struct sched_param*> user_param) { REQUIRE_PROMISE(proc); - if (!validate_write_typed(user_param)) - return -EFAULT; - InterruptDisabler disabler; auto* peer = Thread::current(); if (pid != 0) { @@ -98,7 +93,8 @@ int Process::sys$sched_getparam(pid_t pid, Userspace<struct sched_param*> user_p struct sched_param param { (int)peer->priority() }; - copy_to_user(user_param, ¶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 ccf2110a3a..c518ae7acb 100644 --- a/Kernel/Syscalls/select.cpp +++ b/Kernel/Syscalls/select.cpp @@ -34,51 +34,43 @@ namespace Kernel { -int Process::sys$select(const Syscall::SC_select_params* params) +int Process::sys$select(const Syscall::SC_select_params* user_params) { REQUIRE_PROMISE(stdio); - // FIXME: Return -EINVAL if timeout is invalid. - if (!validate_read_typed(params)) - return -EFAULT; + Syscall::SC_select_params params; SmapDisabler disabler; - - int nfds = params->nfds; - fd_set* readfds = params->readfds; - fd_set* writefds = params->writefds; - fd_set* exceptfds = params->exceptfds; - const timespec* timeout = params->timeout; - const sigset_t* sigmask = params->sigmask; - - if (writefds && !validate_write_typed(writefds)) - return -EFAULT; - if (readfds && !validate_write_typed(readfds)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; - if (exceptfds && !validate_write_typed(exceptfds)) - return -EFAULT; - if (timeout && !validate_read_typed(timeout)) - return -EFAULT; - if (sigmask && !validate_read_typed(sigmask)) - return -EFAULT; - if (nfds < 0) + + if (params.nfds < 0) return -EINVAL; timespec computed_timeout; bool select_has_timeout = false; - if (timeout && (timeout->tv_sec || timeout->tv_nsec)) { - timespec ts_since_boot; - timeval_to_timespec(Scheduler::time_since_boot(), ts_since_boot); - timespec_add(ts_since_boot, *timeout, computed_timeout); - select_has_timeout = true; + if (params.timeout) { + timespec timeout_copy; + if (!copy_from_user(&timeout_copy, params.timeout)) + return -EFAULT; + if (timeout_copy.tv_sec || timeout_copy.tv_nsec) { + timespec ts_since_boot; + timeval_to_timespec(Scheduler::time_since_boot(), ts_since_boot); + timespec_add(ts_since_boot, timeout_copy, computed_timeout); + select_has_timeout = true; + } } auto current_thread = Thread::current(); u32 previous_signal_mask = 0; - if (sigmask) - previous_signal_mask = current_thread->update_signal_mask(*sigmask); + if (params.sigmask) { + sigset_t sigmask_copy; + if (!copy_from_user(&sigmask_copy, params.sigmask)) + return -EFAULT; + previous_signal_mask = current_thread->update_signal_mask(sigmask_copy); + } ScopeGuard rollback_signal_mask([&]() { - if (sigmask) + if (params.sigmask) current_thread->update_signal_mask(previous_signal_mask); }); @@ -86,12 +78,15 @@ int Process::sys$select(const Syscall::SC_select_params* params) Thread::SelectBlocker::FDVector wfds; Thread::SelectBlocker::FDVector efds; - auto transfer_fds = [&](auto* fds, auto& vector) -> int { + auto transfer_fds = [&](auto* fds_unsafe, auto& vector) -> int { vector.clear_with_capacity(); - if (!fds) + if (!fds_unsafe) return 0; - for (int fd = 0; fd < nfds; ++fd) { - if (FD_ISSET(fd, fds)) { + fd_set fds; + if (!copy_from_user(&fds, fds_unsafe)) + return -EFAULT; + for (int fd = 0; fd < params.nfds; ++fd) { + if (FD_ISSET(fd, &fds)) { if (!file_description(fd)) { dbg() << "sys$select: Bad fd number " << fd; return -EBADF; @@ -101,47 +96,42 @@ int Process::sys$select(const Syscall::SC_select_params* params) } return 0; }; - if (int error = transfer_fds(writefds, wfds)) + if (int error = transfer_fds(params.writefds, wfds)) return error; - if (int error = transfer_fds(readfds, rfds)) + if (int error = transfer_fds(params.readfds, rfds)) return error; - if (int error = transfer_fds(exceptfds, efds)) + if (int error = transfer_fds(params.exceptfds, efds)) return error; #if defined(DEBUG_IO) || defined(DEBUG_POLL_SELECT) - dbg() << "selecting on (read:" << rfds.size() << ", write:" << wfds.size() << "), timeout=" << timeout; + dbg() << "selecting on (read:" << rfds.size() << ", write:" << wfds.size() << "), timeout=" << params.timeout; #endif - if (!timeout || select_has_timeout) { + if (!params.timeout || select_has_timeout) { if (current_thread->block<Thread::SelectBlocker>(select_has_timeout ? &computed_timeout : nullptr, rfds, wfds, efds).was_interrupted()) return -EINTR; - // While we blocked, the process lock was dropped. This gave other threads - // the opportunity to mess with the memory. For example, it could free the - // region, and map it to a region to which it has no write permissions. - // Therefore, we need to re-validate all pointers. - if (writefds && !validate_write_typed(writefds)) - return -EFAULT; - if (readfds && !validate_write_typed(readfds)) - return -EFAULT; - // See the fixme below. - if (exceptfds && !validate_write_typed(exceptfds)) - return -EFAULT; } int marked_fd_count = 0; - auto mark_fds = [&](auto* fds, auto& vector, auto should_mark) { - if (!fds) - return; - FD_ZERO(fds); + auto mark_fds = [&](auto* fds_unsafe, auto& vector, auto should_mark) { + if (!fds_unsafe) + return 0; + fd_set fds; + FD_ZERO(&fds); for (int fd : vector) { if (auto description = file_description(fd); description && should_mark(*description)) { - FD_SET(fd, fds); + FD_SET(fd, &fds); ++marked_fd_count; } } + if (!copy_to_user(fds_unsafe, &fds)) + return -EFAULT; + return 0; }; - mark_fds(readfds, rfds, [](auto& description) { return description.can_read(); }); - mark_fds(writefds, wfds, [](auto& description) { return description.can_write(); }); + if (int error = mark_fds(params.readfds, rfds, [](auto& description) { return description.can_read(); })) + return error; + if (int error = mark_fds(params.writefds, wfds, [](auto& description) { return description.can_write(); })) + return error; // FIXME: We should also mark exceptfds as appropriate. return marked_fd_count; @@ -153,33 +143,39 @@ int Process::sys$poll(Userspace<const Syscall::SC_poll_params*> user_params) // FIXME: Return -EINVAL if timeout is invalid. Syscall::SC_poll_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; SmapDisabler disabler; - pollfd* fds = params.fds; - unsigned nfds = params.nfds; - - if (fds && !validate_read_typed(fds, nfds)) - return -EFAULT; - timespec timeout = {}; - if (params.timeout && !validate_read_and_copy_typed(&timeout, params.timeout)) + if (params.timeout && !copy_from_user(&timeout, params.timeout)) return -EFAULT; sigset_t sigmask = {}; - if (params.sigmask && !validate_read_and_copy_typed(&sigmask, params.sigmask)) + if (params.sigmask && !copy_from_user(&sigmask, params.sigmask)) return -EFAULT; + Vector<pollfd> fds_copy; + if (params.nfds > 0) { + Checked nfds_checked = sizeof(pollfd); + nfds_checked *= params.nfds; + if (nfds_checked.has_overflow()) + return -EFAULT; + fds_copy.resize(params.nfds); + if (!copy_from_user(&fds_copy[0], ¶ms.fds[0], params.nfds * sizeof(pollfd))) + return -EFAULT; + } + Thread::SelectBlocker::FDVector rfds; Thread::SelectBlocker::FDVector wfds; - for (unsigned i = 0; i < nfds; ++i) { - if (fds[i].events & POLLIN) - rfds.append(fds[i].fd); - if (fds[i].events & POLLOUT) - wfds.append(fds[i].fd); + for (unsigned i = 0; i < params.nfds; ++i) { + auto& pfd = fds_copy[i]; + if (pfd.events & POLLIN) + rfds.append(pfd.fd); + if (pfd.events & POLLOUT) + wfds.append(pfd.fd); } timespec actual_timeout; @@ -195,7 +191,7 @@ int Process::sys$poll(Userspace<const Syscall::SC_poll_params*> user_params) u32 previous_signal_mask = 0; if (params.sigmask) - previous_signal_mask = current_thread->update_signal_mask(params.sigmask); + previous_signal_mask = current_thread->update_signal_mask(sigmask); ScopeGuard rollback_signal_mask([&]() { if (params.sigmask) current_thread->update_signal_mask(previous_signal_mask); @@ -210,28 +206,28 @@ int Process::sys$poll(Userspace<const Syscall::SC_poll_params*> user_params) return -EINTR; } - // Validate we can still write after waking up. - if (fds && !validate_write_typed(fds, nfds)) - return -EFAULT; - int fds_with_revents = 0; - for (unsigned i = 0; i < nfds; ++i) { - auto description = file_description(fds[i].fd); + for (unsigned i = 0; i < params.nfds; ++i) { + auto& pfd = fds_copy[i]; + auto description = file_description(pfd.fd); if (!description) { - fds[i].revents = POLLNVAL; - continue; + pfd.revents = POLLNVAL; + } else { + pfd.revents = 0; + if (pfd.events & POLLIN && description->can_read()) + pfd.revents |= POLLIN; + if (pfd.events & POLLOUT && description->can_write()) + pfd.revents |= POLLOUT; + + if (pfd.revents) + ++fds_with_revents; } - fds[i].revents = 0; - if (fds[i].events & POLLIN && description->can_read()) - fds[i].revents |= POLLIN; - if (fds[i].events & POLLOUT && description->can_write()) - fds[i].revents |= POLLOUT; - - if (fds[i].revents) - ++fds_with_revents; } + if (params.nfds > 0 && !copy_to_user(¶ms.fds[0], &fds_copy[0], params.nfds * sizeof(pollfd))) + return -EFAULT; + return fds_with_revents; } diff --git a/Kernel/Syscalls/setkeymap.cpp b/Kernel/Syscalls/setkeymap.cpp index 9920435135..8f9b441dd2 100644 --- a/Kernel/Syscalls/setkeymap.cpp +++ b/Kernel/Syscalls/setkeymap.cpp @@ -37,25 +37,20 @@ int Process::sys$setkeymap(Userspace<const Syscall::SC_setkeymap_params*> user_p return -EPERM; Syscall::SC_setkeymap_params params; - if (!validate_read_and_copy_typed(¶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 3cedc049ec..8ea014039d 100644 --- a/Kernel/Syscalls/setuid.cpp +++ b/Kernel/Syscalls/setuid.cpp @@ -125,8 +125,6 @@ int Process::sys$setgroups(ssize_t count, Userspace<const gid_t*> user_gids) return -EINVAL; if (!is_superuser()) return -EPERM; - if (count && !validate_read(user_gids, count)) - return -EFAULT; if (!count) { m_extra_gids.clear(); @@ -135,7 +133,8 @@ int Process::sys$setgroups(ssize_t count, Userspace<const gid_t*> user_gids) Vector<gid_t> gids; gids.resize(count); - copy_from_user(gids.data(), user_gids, sizeof(gid_t) * count); + if (!copy_from_user(gids.data(), user_gids.unsafe_userspace_ptr(), sizeof(gid_t) * count)) + return -EFAULT; HashTable<gid_t> unique_extra_gids; for (auto& gid : gids) { diff --git a/Kernel/Syscalls/shbuf.cpp b/Kernel/Syscalls/shbuf.cpp index 8f92562642..6d85c2d50a 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<size_t*> user_size) { REQUIRE_PROMISE(shared_buffer); - if (user_size && !validate_write_typed(user_size)) - return (void*)-EFAULT; LOCKER(shared_buffers().lock()); auto it = shared_buffers().resource().find(shbuf_id); if (it == shared_buffers().resource().end()) @@ -137,7 +134,8 @@ void* Process::sys$shbuf_get(int shbuf_id, Userspace<size_t*> user_size) #endif if (user_size) { size_t size = shared_buffer.size(); - copy_to_user(user_size, &size); + if (!copy_to_user(user_size, &size)) + return (void*)-EFAULT; } return shared_buffer.ref_for_process_and_get_address(*this); } diff --git a/Kernel/Syscalls/sigaction.cpp b/Kernel/Syscalls/sigaction.cpp index c735b34eca..a3151c6d1e 100644 --- a/Kernel/Syscalls/sigaction.cpp +++ b/Kernel/Syscalls/sigaction.cpp @@ -34,10 +34,9 @@ int Process::sys$sigprocmask(int how, Userspace<const sigset_t*> set, Userspace< auto current_thread = Thread::current(); u32 previous_signal_mask; if (set) { - if (!validate_read_typed(set)) - return -EFAULT; sigset_t set_value; - copy_from_user(&set_value, set); + if (!copy_from_user(&set_value, set)) + return -EFAULT; switch (how) { case SIG_BLOCK: previous_signal_mask = current_thread->signal_mask_block(set_value, true); @@ -54,21 +53,17 @@ int Process::sys$sigprocmask(int how, Userspace<const sigset_t*> set, Userspace< } else { previous_signal_mask = current_thread->signal_mask(); } - if (old_set) { - if (!validate_write_typed(old_set)) - return -EFAULT; - copy_to_user(old_set, &previous_signal_mask); - } + if (old_set && !copy_to_user(old_set, &previous_signal_mask)) + return -EFAULT; return 0; } int Process::sys$sigpending(Userspace<sigset_t*> set) { REQUIRE_PROMISE(stdio); - if (!validate_write_typed(set)) - return -EFAULT; auto pending_signals = Thread::current()->pending_signals(); - copy_to_user(set, &pending_signals); + if (!copy_to_user(set, &pending_signals)) + return -EFAULT; return 0; } @@ -77,18 +72,18 @@ int Process::sys$sigaction(int signum, const sigaction* act, sigaction* old_act) REQUIRE_PROMISE(sigaction); if (signum < 1 || signum >= 32 || signum == SIGKILL || signum == SIGSTOP) return -EINVAL; - if (!validate_read_typed(act)) - return -EFAULT; InterruptDisabler disabler; // FIXME: This should use a narrower lock. Maybe a way to ignore signals temporarily? auto& action = Thread::current()->m_signal_action_data[signum]; if (old_act) { - if (!validate_write_typed(old_act)) + if (!copy_to_user(&old_act->sa_flags, &action.flags)) + return -EFAULT; + if (!copy_to_user(&old_act->sa_sigaction, &action.handler_or_sigaction, sizeof(action.handler_or_sigaction))) return -EFAULT; - copy_to_user(&old_act->sa_flags, &action.flags); - copy_to_user(&old_act->sa_sigaction, &action.handler_or_sigaction, sizeof(action.handler_or_sigaction)); } - copy_from_user(&action.flags, &act->sa_flags); - copy_from_user(&action.handler_or_sigaction, &act->sa_sigaction, sizeof(action.handler_or_sigaction)); + if (!copy_from_user(&action.flags, &act->sa_flags)) + return -EFAULT; + if (!copy_from_user(&action.handler_or_sigaction, &act->sa_sigaction, sizeof(action.handler_or_sigaction))) + return -EFAULT; return 0; } diff --git a/Kernel/Syscalls/socket.cpp b/Kernel/Syscalls/socket.cpp index a32d87e23d..70d30765dc 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<const sockaddr*> address, socklen_t address_length) { - if (!validate_read(address, address_length)) - return -EFAULT; auto description = file_description(sockfd); if (!description) return -EBADF; @@ -98,14 +96,8 @@ int Process::sys$accept(int accepting_socket_fd, Userspace<sockaddr*> user_addre REQUIRE_PROMISE(accept); socklen_t address_size = 0; - if (user_address) { - if (!validate_write_typed(user_address_size)) - return -EFAULT; - if (!validate_read_and_copy_typed(&address_size, user_address_size)) - return -EFAULT; - if (!validate_write(user_address, address_size)) - return -EFAULT; - } + if (user_address && !copy_from_user(&address_size, static_ptr_cast<const socklen_t*>(user_address_size))) + return -EFAULT; int accepted_socket_fd = alloc_fd(); if (accepted_socket_fd < 0) @@ -132,8 +124,10 @@ int Process::sys$accept(int accepting_socket_fd, Userspace<sockaddr*> user_addre u8 address_buffer[sizeof(sockaddr_un)]; address_size = min(sizeof(sockaddr_un), static_cast<size_t>(address_size)); accepted_socket->get_peer_address((sockaddr*)address_buffer, &address_size); - copy_to_user(user_address, address_buffer, address_size); - copy_to_user(user_address_size, &address_size); + if (!copy_to_user(user_address, address_buffer, address_size)) + return -EFAULT; + if (!copy_to_user(user_address_size, &address_size)) + return -EFAULT; } auto accepted_socket_description = FileDescription::create(*accepted_socket); @@ -151,8 +145,6 @@ int Process::sys$accept(int accepting_socket_fd, Userspace<sockaddr*> user_addre int Process::sys$connect(int sockfd, Userspace<const sockaddr*> user_address, socklen_t user_address_size) { - if (!validate_read(user_address, user_address_size)) - return -EFAULT; int fd = alloc_fd(); if (fd < 0) return fd; @@ -165,11 +157,7 @@ int Process::sys$connect(int sockfd, Userspace<const sockaddr*> user_address, so auto& socket = *description->socket(); REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain()); - u8 address[sizeof(sockaddr_un)]; - size_t address_size = min(sizeof(address), static_cast<size_t>(user_address_size)); - copy_from_user(address, user_address, address_size); - - return socket.connect(*description, (const sockaddr*)address, address_size, description->is_blocking() ? ShouldBlock::Yes : ShouldBlock::No); + return socket.connect(*description, user_address, user_address_size, description->is_blocking() ? ShouldBlock::Yes : ShouldBlock::No); } int Process::sys$shutdown(int sockfd, int how) @@ -192,17 +180,13 @@ ssize_t Process::sys$sendto(Userspace<const Syscall::SC_sendto_params*> user_par { REQUIRE_PROMISE(stdio); Syscall::SC_sendto_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; int flags = params.flags; - Userspace<const sockaddr*> addr = params.addr; + Userspace<const sockaddr*> addr((FlatPtr)params.addr); socklen_t addr_length = params.addr_length; - if (!validate(params.data)) - return -EFAULT; - if (addr && !validate_read(addr, addr_length)) - return -EFAULT; auto description = file_description(params.sockfd); if (!description) return -EBADF; @@ -212,7 +196,10 @@ ssize_t Process::sys$sendto(Userspace<const Syscall::SC_sendto_params*> user_par if (socket.is_shut_down_for_writing()) return -EPIPE; SmapDisabler disabler; - auto result = socket.sendto(*description, params.data.data, params.data.size, flags, addr, addr_length); + auto data_buffer = UserOrKernelBuffer::for_user_buffer(const_cast<u8*>((const u8*)params.data.data), params.data.size); + if (!data_buffer.has_value()) + return -EFAULT; + auto result = socket.sendto(*description, data_buffer.value(), params.data.size, flags, addr, addr_length); if (result.is_error()) return result.error(); return result.value(); @@ -223,27 +210,17 @@ ssize_t Process::sys$recvfrom(Userspace<const Syscall::SC_recvfrom_params*> user REQUIRE_PROMISE(stdio); Syscall::SC_recvfrom_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; int flags = params.flags; - Userspace<sockaddr*> user_addr = params.addr; - Userspace<socklen_t*> user_addr_length = params.addr_length; + Userspace<sockaddr*> user_addr((FlatPtr)params.addr); + Userspace<socklen_t*> user_addr_length((FlatPtr)params.addr_length); SmapDisabler disabler; - if (!validate(params.buffer)) - return -EFAULT; - if (user_addr_length) { - socklen_t addr_length; - if (!validate_read_and_copy_typed(&addr_length, user_addr_length)) - return -EFAULT; - if (!validate_write_typed(user_addr_length)) - return -EFAULT; - if (!validate_write(user_addr, addr_length)) - return -EFAULT; - } else if (user_addr) { + if (!user_addr_length && user_addr) return -EINVAL; - } + auto description = file_description(params.sockfd); if (!description) return -EBADF; @@ -258,7 +235,10 @@ ssize_t Process::sys$recvfrom(Userspace<const Syscall::SC_recvfrom_params*> user if (flags & MSG_DONTWAIT) description->set_blocking(false); - auto result = socket.recvfrom(*description, params.buffer.data, params.buffer.size, flags, user_addr, user_addr_length); + auto data_buffer = UserOrKernelBuffer::for_user_buffer(const_cast<u8*>((const u8*)params.buffer.data), params.buffer.size); + if (!data_buffer.has_value()) + return -EFAULT; + auto result = socket.recvfrom(*description, data_buffer.value(), params.buffer.size, flags, user_addr, user_addr_length); if (flags & MSG_DONTWAIT) description->set_blocking(original_blocking); @@ -271,18 +251,12 @@ template<bool sockname, typename Params> int Process::get_sock_or_peer_name(const Params& params) { socklen_t addrlen_value; - if (!validate_read_and_copy_typed(&addrlen_value, params.addrlen)) + if (!copy_from_user(&addrlen_value, params.addrlen, sizeof(socklen_t))) return -EFAULT; if (addrlen_value <= 0) return -EINVAL; - if (!validate_write(params.addr, addrlen_value)) - return -EFAULT; - - if (!validate_write_typed(params.addrlen)) - return -EFAULT; - auto description = file_description(params.sockfd); if (!description) return -EBADF; @@ -299,15 +273,17 @@ int Process::get_sock_or_peer_name(const Params& params) socket.get_local_address((sockaddr*)address_buffer, &addrlen_value); else socket.get_peer_address((sockaddr*)address_buffer, &addrlen_value); - copy_to_user(params.addr, address_buffer, addrlen_value); - copy_to_user(params.addrlen, &addrlen_value); + if (!copy_to_user(params.addr, address_buffer, addrlen_value)) + return -EFAULT; + if (!copy_to_user(params.addrlen, &addrlen_value)) + return -EFAULT; return 0; } int Process::sys$getsockname(Userspace<const Syscall::SC_getsockname_params*> user_params) { Syscall::SC_getsockname_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; return get_sock_or_peer_name<true>(params); } @@ -315,7 +291,7 @@ int Process::sys$getsockname(Userspace<const Syscall::SC_getsockname_params*> us int Process::sys$getpeername(Userspace<const Syscall::SC_getpeername_params*> user_params) { Syscall::SC_getpeername_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; return get_sock_or_peer_name<false>(params); } @@ -323,22 +299,19 @@ int Process::sys$getpeername(Userspace<const Syscall::SC_getpeername_params*> us int Process::sys$getsockopt(Userspace<const Syscall::SC_getsockopt_params*> user_params) { Syscall::SC_getsockopt_params params; - if (!validate_read_and_copy_typed(¶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<void*> user_value = params.value; - Userspace<socklen_t*> user_value_size = params.value_size; + Userspace<void*> user_value((FlatPtr)params.value); + Userspace<socklen_t*> user_value_size((FlatPtr)params.value_size); - if (!validate_write_typed(user_value_size)) - return -EFAULT; socklen_t value_size; - if (!validate_read_and_copy_typed(&value_size, user_value_size)) - return -EFAULT; - if (!validate_write(user_value, value_size)) + if (!copy_from_user(&value_size, params.value_size, sizeof(socklen_t))) return -EFAULT; + auto description = file_description(sockfd); if (!description) return -EBADF; @@ -357,10 +330,9 @@ int Process::sys$getsockopt(Userspace<const Syscall::SC_getsockopt_params*> user int Process::sys$setsockopt(Userspace<const Syscall::SC_setsockopt_params*> user_params) { Syscall::SC_setsockopt_params params; - if (!validate_read_and_copy_typed(¶ms, user_params)) - return -EFAULT; - if (!validate_read(params.value, params.value_size)) + if (!copy_from_user(¶ms, user_params)) return -EFAULT; + Userspace<const void*> user_value((FlatPtr)params.value); auto description = file_description(params.sockfd); if (!description) return -EBADF; @@ -368,7 +340,7 @@ int Process::sys$setsockopt(Userspace<const Syscall::SC_setsockopt_params*> user return -ENOTSOCK; auto& socket = *description->socket(); REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain()); - return socket.setsockopt(params.level, params.option, params.value, params.value_size); + return socket.setsockopt(params.level, params.option, user_value, params.value_size); } } diff --git a/Kernel/Syscalls/stat.cpp b/Kernel/Syscalls/stat.cpp index a5ad5882c5..19cdb14f78 100644 --- a/Kernel/Syscalls/stat.cpp +++ b/Kernel/Syscalls/stat.cpp @@ -34,14 +34,13 @@ namespace Kernel { int Process::sys$fstat(int fd, Userspace<stat*> user_statbuf) { REQUIRE_PROMISE(stdio); - if (!validate_write_typed(user_statbuf)) - return -EFAULT; auto description = file_description(fd); if (!description) return -EBADF; stat buffer = {}; int rc = description->stat(buffer); - copy_to_user(user_statbuf, &buffer); + if (!copy_to_user(user_statbuf, &buffer)) + return -EFAULT; return rc; } @@ -49,9 +48,7 @@ int Process::sys$stat(Userspace<const Syscall::SC_stat_params*> user_params) { REQUIRE_PROMISE(rpath); Syscall::SC_stat_params params; - if (!validate_read_and_copy_typed(¶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<const Syscall::SC_stat_params*> user_params) auto result = metadata_or_error.value().stat(statbuf); if (result.is_error()) return result; - copy_to_user(params.statbuf, &statbuf); + if (!copy_to_user(params.statbuf, &statbuf)) + return -EFAULT; return 0; } diff --git a/Kernel/Syscalls/thread.cpp b/Kernel/Syscalls/thread.cpp index 39e59baff9..1f13f90ce3 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<const Syscall::SC_create_thread_params*> user_params) { REQUIRE_PROMISE(thread); - if (!validate_read((const void*)entry, sizeof(void*))) - return -EFAULT; Syscall::SC_create_thread_params params; - if (!validate_read_and_copy_typed(¶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<void*> stack_location = params.m_stack_location; unsigned stack_size = params.m_stack_size; - if (!validate_write(stack_location, stack_size)) - return -EFAULT; - - u32 user_stack_address = reinterpret_cast<u32>(stack_location.ptr()) + stack_size; + auto user_stack_address = (u8*)params.m_stack_location + stack_size; if (!MM.validate_user_stack(*this, VirtualAddress(user_stack_address - 4))) return -EFAULT; @@ -83,7 +77,7 @@ int Process::sys$create_thread(void* (*entry)(void*), Userspace<const Syscall::S tss.eip = (FlatPtr)entry; tss.eflags = 0x0202; tss.cr3 = page_directory().cr3(); - tss.esp = user_stack_address; + tss.esp = (u32)user_stack_address; thread->make_thread_specific_region({}); thread->set_state(Thread::State::Runnable); @@ -120,8 +114,6 @@ int Process::sys$detach_thread(pid_t tid) int Process::sys$join_thread(pid_t tid, Userspace<void**> exit_value) { REQUIRE_PROMISE(thread); - if (exit_value && !validate_write_typed(exit_value)) - return -EFAULT; InterruptDisabler disabler; auto* thread = Thread::from_tid(tid); @@ -164,15 +156,15 @@ int Process::sys$join_thread(pid_t tid, Userspace<void**> exit_value) // NOTE: 'thread' is very possibly deleted at this point. Clear it just to be safe. thread = nullptr; - if (exit_value) - copy_to_user(exit_value, &joinee_exit_value); + if (exit_value && !copy_to_user(exit_value, &joinee_exit_value)) + return -EFAULT; return 0; } int Process::sys$set_thread_name(pid_t tid, Userspace<const char*> user_name, size_t user_name_length) { REQUIRE_PROMISE(thread); - auto name = validate_and_copy_string_from_user(user_name, user_name_length); + auto name = copy_string_from_user(user_name, user_name_length); if (name.is_null()) return -EFAULT; @@ -185,7 +177,7 @@ int Process::sys$set_thread_name(pid_t tid, Userspace<const char*> user_name, si if (!thread || thread->pid() != pid()) return -ESRCH; - thread->set_name(name); + thread->set_name(move(name)); return 0; } @@ -195,9 +187,6 @@ int Process::sys$get_thread_name(pid_t tid, Userspace<char*> buffer, size_t buff if (buffer_size == 0) return -EINVAL; - if (!validate_write(buffer, buffer_size)) - return -EFAULT; - InterruptDisabler disabler; auto* thread = Thread::from_tid(tid); if (!thread || thread->pid() != pid()) @@ -206,7 +195,8 @@ int Process::sys$get_thread_name(pid_t tid, Userspace<char*> buffer, size_t buff if (thread->name().length() + 1 > (size_t)buffer_size) return -ENAMETOOLONG; - copy_to_user(buffer, thread->name().characters(), thread->name().length() + 1); + if (!copy_to_user(buffer, thread->name().characters(), thread->name().length() + 1)) + return -EFAULT; return 0; } diff --git a/Kernel/Syscalls/times.cpp b/Kernel/Syscalls/times.cpp index 61355595cf..f769fee590 100644 --- a/Kernel/Syscalls/times.cpp +++ b/Kernel/Syscalls/times.cpp @@ -31,16 +31,14 @@ namespace Kernel { clock_t Process::sys$times(Userspace<tms*> user_times) { REQUIRE_PROMISE(stdio); - if (!validate_write_typed(user_times)) - return -EFAULT; - tms times = {}; times.tms_utime = m_ticks_in_user; times.tms_stime = m_ticks_in_kernel; times.tms_cutime = m_ticks_in_user_for_dead_children; times.tms_cstime = m_ticks_in_kernel_for_dead_children; - copy_to_user(user_times, ×); + 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 544135cb5e..9f37a94768 100644 --- a/Kernel/Syscalls/ttyname.cpp +++ b/Kernel/Syscalls/ttyname.cpp @@ -34,8 +34,6 @@ namespace Kernel { int Process::sys$ttyname(int fd, Userspace<char*> buffer, size_t size) { REQUIRE_PROMISE(tty); - if (!validate_write(buffer, size)) - return -EFAULT; auto description = file_description(fd); if (!description) return -EBADF; @@ -44,15 +42,14 @@ int Process::sys$ttyname(int fd, Userspace<char*> buffer, size_t size) auto tty_name = description->tty()->tty_name(); if (size < tty_name.length() + 1) return -ERANGE; - copy_to_user(buffer, tty_name.characters(), tty_name.length() + 1); + if (!copy_to_user(buffer, tty_name.characters(), tty_name.length() + 1)) + return -EFAULT; return 0; } int Process::sys$ptsname(int fd, Userspace<char*> buffer, size_t size) { REQUIRE_PROMISE(tty); - if (!validate_write(buffer, size)) - return -EFAULT; auto description = file_description(fd); if (!description) return -EBADF; @@ -62,7 +59,8 @@ int Process::sys$ptsname(int fd, Userspace<char*> buffer, size_t size) auto pts_name = master_pty->pts_name(); if (size < pts_name.length() + 1) return -ERANGE; - copy_to_user(buffer, pts_name.characters(), pts_name.length() + 1); + if (!copy_to_user(buffer, pts_name.characters(), pts_name.length() + 1)) + return -EFAULT; return 0; } diff --git a/Kernel/Syscalls/uname.cpp b/Kernel/Syscalls/uname.cpp index 33172798dd..6f56388396 100644 --- a/Kernel/Syscalls/uname.cpp +++ b/Kernel/Syscalls/uname.cpp @@ -34,9 +34,6 @@ int Process::sys$uname(Userspace<utsname*> buf) extern Lock* g_hostname_lock; REQUIRE_PROMISE(stdio); - if (!validate_write_typed(buf)) - return -EFAULT; - LOCKER(*g_hostname_lock, Lock::Mode::Shared); if (g_hostname->length() + 1 > sizeof(utsname::nodename)) @@ -45,11 +42,16 @@ int Process::sys$uname(Userspace<utsname*> buf) // We have already validated the entire utsname struct at this // point, there is no need to re-validate every write to the struct. utsname* user_buf = buf.unsafe_userspace_ptr(); - copy_to_user(user_buf->sysname, "SerenityOS", 11); - copy_to_user(user_buf->release, "1.0-dev", 8); - copy_to_user(user_buf->version, "FIXME", 6); - copy_to_user(user_buf->machine, "i686", 5); - copy_to_user(user_buf->nodename, g_hostname->characters(), g_hostname->length() + 1); + if (!copy_to_user(user_buf->sysname, "SerenityOS", 11)) + return -EFAULT; + if (!copy_to_user(user_buf->release, "1.0-dev", 8)) + return -EFAULT; + if (!copy_to_user(user_buf->version, "FIXME", 6)) + return -EFAULT; + if (!copy_to_user(user_buf->machine, "i686", 5)) + return -EFAULT; + if (!copy_to_user(user_buf->nodename, g_hostname->characters(), g_hostname->length() + 1)) + return -EFAULT; return 0; } diff --git a/Kernel/Syscalls/unlink.cpp b/Kernel/Syscalls/unlink.cpp index 7c2ac59193..f7cf1faf22 100644 --- a/Kernel/Syscalls/unlink.cpp +++ b/Kernel/Syscalls/unlink.cpp @@ -33,8 +33,6 @@ namespace Kernel { int Process::sys$unlink(Userspace<const char*> user_path, size_t path_length) { REQUIRE_PROMISE(cpath); - if (!validate_read(user_path, path_length)) - return -EFAULT; auto path = get_syscall_path_argument(user_path, path_length); if (path.is_error()) return path.error(); diff --git a/Kernel/Syscalls/unveil.cpp b/Kernel/Syscalls/unveil.cpp index 466d50ed79..dd13d13057 100644 --- a/Kernel/Syscalls/unveil.cpp +++ b/Kernel/Syscalls/unveil.cpp @@ -34,7 +34,7 @@ namespace Kernel { int Process::sys$unveil(Userspace<const Syscall::SC_unveil_params*> user_params) { Syscall::SC_unveil_params params; - if (!validate_read_and_copy_typed(¶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<const Syscall::SC_unveil_params*> user_params) auto& custody = custody_or_error.value(); auto new_unveiled_path = custody->absolute_path(); - auto permissions = validate_and_copy_string_from_user(params.permissions); + auto permissions = copy_string_from_user(params.permissions); if (permissions.is_null()) return -EFAULT; diff --git a/Kernel/Syscalls/utime.cpp b/Kernel/Syscalls/utime.cpp index c7134eb330..ae848b906c 100644 --- a/Kernel/Syscalls/utime.cpp +++ b/Kernel/Syscalls/utime.cpp @@ -33,14 +33,13 @@ namespace Kernel { int Process::sys$utime(Userspace<const char*> user_path, size_t path_length, Userspace<const struct utimbuf*> user_buf) { REQUIRE_PROMISE(fattr); - if (user_buf && !validate_read_typed(user_buf)) - return -EFAULT; auto path = get_syscall_path_argument(user_path, path_length); if (path.is_error()) return path.error(); utimbuf buf; if (user_buf) { - copy_from_user(&buf, user_buf); + if (!copy_from_user(&buf, user_buf)) + return -EFAULT; } else { auto now = kgettimeofday(); buf = { now.tv_sec, now.tv_sec }; diff --git a/Kernel/Syscalls/waitid.cpp b/Kernel/Syscalls/waitid.cpp index 41a0d7c12a..1b12c2a744 100644 --- a/Kernel/Syscalls/waitid.cpp +++ b/Kernel/Syscalls/waitid.cpp @@ -104,27 +104,19 @@ pid_t Process::sys$waitid(Userspace<const Syscall::SC_waitid_params*> user_param REQUIRE_PROMISE(proc); Syscall::SC_waitid_params params; - if (!validate_read_and_copy_typed(¶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<idtype_t>(params.idtype), params.id, params.options); if (siginfo_or_error.is_error()) return siginfo_or_error.error(); - // While we waited, the process lock was dropped. This gave other threads - // the opportunity to mess with the memory. For example, it could free the - // region, and map it to a region to which it has no write permissions. - // Therefore, we need to re-validate the pointer. - if (!validate_write_typed(params.infop)) - return -EFAULT; - copy_to_user(params.infop, &siginfo_or_error.value()); + if (!copy_to_user(params.infop, &siginfo_or_error.value())) + return -EFAULT; return 0; } diff --git a/Kernel/Syscalls/write.cpp b/Kernel/Syscalls/write.cpp index 62a421b2d3..0385bcbb8e 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<iovec, 32> vecs; vecs.resize(iov_count); - copy_from_user(vecs.data(), iov, iov_count * sizeof(iovec)); + if (!copy_n_from_user(vecs.data(), iov, iov_count)) + return -EFAULT; for (auto& vec : vecs) { - if (!validate_read(vec.iov_base, vec.iov_len)) - return -EFAULT; total_length += vec.iov_len; if (total_length > NumericLimits<i32>::max()) return -EINVAL; @@ -60,7 +63,10 @@ ssize_t Process::sys$writev(int fd, const struct iovec* iov, int iov_count) int nwritten = 0; for (auto& vec : vecs) { - int rc = do_write(*description, (const u8*)vec.iov_base, vec.iov_len); + auto buffer = UserOrKernelBuffer::for_user_buffer((u8*)vec.iov_base, vec.iov_len); + if (!buffer.has_value()) + return -EFAULT; + int rc = do_write(*description, buffer.value(), vec.iov_len); if (rc < 0) { if (nwritten == 0) return rc; @@ -72,7 +78,7 @@ ssize_t Process::sys$writev(int fd, const struct iovec* iov, int iov_count) return nwritten; } -ssize_t Process::do_write(FileDescription& description, const u8* data, int data_size) +ssize_t Process::do_write(FileDescription& description, const UserOrKernelBuffer& data, size_t data_size) { ssize_t total_nwritten = 0; if (!description.is_blocking()) { @@ -83,7 +89,7 @@ ssize_t Process::do_write(FileDescription& description, const u8* data, int data if (description.should_append()) description.seek(0, SEEK_END); - while (total_nwritten < data_size) { + while ((size_t)total_nwritten < data_size) { if (!description.can_write()) { if (!description.is_blocking()) { // Short write: We can no longer write to this non-blocking description. @@ -95,7 +101,7 @@ ssize_t Process::do_write(FileDescription& description, const u8* data, int data return -EINTR; } } - auto nwritten_or_error = description.write(data + total_nwritten, data_size - total_nwritten); + auto nwritten_or_error = description.write(data.offset(total_nwritten), data_size - total_nwritten); if (nwritten_or_error.is_error()) { if (total_nwritten) return total_nwritten; @@ -115,8 +121,7 @@ ssize_t Process::sys$write(int fd, const u8* data, ssize_t size) return -EINVAL; if (size == 0) return 0; - if (!validate_read(data, size)) - return -EFAULT; + #ifdef DEBUG_IO dbg() << "sys$write(" << fd << ", " << (const void*)(data) << ", " << size << ")"; #endif @@ -126,7 +131,10 @@ ssize_t Process::sys$write(int fd, const u8* data, ssize_t size) if (!description->is_writable()) return -EBADF; - return do_write(*description, data, size); + auto buffer = UserOrKernelBuffer::for_user_buffer(const_cast<u8*>(data), (size_t)size); + if (!buffer.has_value()) + return -EFAULT; + return do_write(*description, buffer.value(), size); } } diff --git a/Kernel/TTY/MasterPTY.cpp b/Kernel/TTY/MasterPTY.cpp index 27c251158a..6bbcfb92b2 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<size_t> MasterPTY::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr<size_t> 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<size_t> MasterPTY::write(FileDescription&, size_t, const u8* buffer, size_t size) +KResultOr<size_t> 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<SlavePTY>) 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 08e48907e6..69ba0a16dc 100644 --- a/Kernel/TTY/MasterPTY.h +++ b/Kernel/TTY/MasterPTY.h @@ -41,7 +41,7 @@ public: 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<SlavePTY>); bool is_closed() const { return m_closed; } @@ -50,8 +50,8 @@ public: private: // ^CharacterDevice - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> 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 17698009c5..bb20469f38 100644 --- a/Kernel/TTY/PTYMultiplexer.h +++ b/Kernel/TTY/PTYMultiplexer.h @@ -48,8 +48,8 @@ public: // ^CharacterDevice virtual KResultOr<NonnullRefPtr<FileDescription>> open(int options) override; - virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override { return 0; } - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override { return 0; } + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return 0; } + virtual KResultOr<size_t> 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 6a5106df38..e7daab0a39 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<size_t> SlavePTY::read(FileDescription& description, size_t offset, u8* buffer, size_t size) +KResultOr<size_t> 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 ff86f0c04d..2f1d0d5e76 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 @@ public: 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<size_t> read(FileDescription&, size_t, u8*, size_t) override; + virtual KResultOr<size_t> 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 672e1885d2..3130b3cceb 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<size_t> TTY::read(FileDescription&, size_t, u8* buffer, size_t size) +KResultOr<size_t> 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<size_t> TTY::read(FileDescription&, size_t, u8* buffer, size_t size) if (m_input_buffer.size() < static_cast<size_t>(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<size_t> TTY::write(FileDescription&, size_t, const u8* buffer, size_t size) +KResultOr<size_t> 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<termios*>(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<termios*>(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<winsize*>(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<winsize*>(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 d858aa68a6..6b9d571336 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<size_t> read(FileDescription&, size_t, u8*, size_t) override; - virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override; + virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; + virtual KResultOr<size_t> 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 @@ public: 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 29742a8222..f73e07bd4f 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 b6a04587fb..f9f4665be1 100644 --- a/Kernel/TTY/VirtualConsole.h +++ b/Kernel/TTY/VirtualConsole.h @@ -54,7 +54,7 @@ private: 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 40b82bd2e6..07b7a8112f 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<String> arguments, Vecto auto push_on_new_stack = [&new_esp](u32 value) { new_esp -= 4; Userspace<u32*> 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<auxv_t*> 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<u32*> 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<FlatPtr> 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<FlatPtr> Thread::raw_backtrace(FlatPtr ebp, FlatPtr eip) const ProcessPagingScope paging_scope(process); Vector<FlatPtr, Profiling::max_stack_frame_count> 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 f8f9d1ded0..5e766643d6 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -107,6 +107,7 @@ public: 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 @@ public: 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<String> arguments, Vector<String> environment, Vector<AuxiliaryValue>); diff --git a/Kernel/UserOrKernelBuffer.cpp b/Kernel/UserOrKernelBuffer.cpp new file mode 100644 index 0000000000..8d311b5a62 --- /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 <Kernel/UserOrKernelBuffer.h> +#include <Kernel/VM/MemoryManager.h> + +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 0000000000..08e277341a --- /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 <AK/String.h> +#include <AK/Types.h> +#include <AK/Userspace.h> +#include <Kernel/StdLib.h> +#include <Kernel/UnixTypes.h> +#include <Kernel/VM/MemoryManager.h> +#include <LibC/errno_numbers.h> + +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<UserOrKernelBuffer> 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<typename UserspaceType> + static Optional<UserOrKernelBuffer> for_user_buffer(UserspaceType userspace, size_t size) + { + if (!is_user_range(VirtualAddress(userspace.unsafe_userspace_ptr()), size)) + return {}; + return UserOrKernelBuffer(const_cast<u8*>((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<size_t BUFFER_BYTES, typename F> + [[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<size_t BUFFER_BYTES, typename F> + [[nodiscard]] ssize_t write_buffered(size_t len, F f) + { + return write_buffered<BUFFER_BYTES, F>(0, len, f); + } + + template<size_t BUFFER_BYTES, typename F> + [[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<size_t BUFFER_BYTES, typename F> + [[nodiscard]] ssize_t read_buffered(size_t len, F f) const + { + return read_buffered<BUFFER_BYTES, F>(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 8c5b07d413..c63417fab0 100644 --- a/Kernel/VM/InodeVMObject.cpp +++ b/Kernel/VM/InodeVMObject.cpp @@ -89,7 +89,7 @@ void InodeVMObject::inode_size_changed(Badge<Inode>, size_t old_size, size_t new }); } -void InodeVMObject::inode_contents_changed(Badge<Inode>, off_t offset, ssize_t size, const u8* data) +void InodeVMObject::inode_contents_changed(Badge<Inode>, 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 727199ff7e..94e7073709 100644 --- a/Kernel/VM/InodeVMObject.h +++ b/Kernel/VM/InodeVMObject.h @@ -39,7 +39,7 @@ public: Inode& inode() { return *m_inode; } const Inode& inode() const { return *m_inode; } - void inode_contents_changed(Badge<Inode>, off_t, ssize_t, const u8*); + void inode_contents_changed(Badge<Inode>, off_t, ssize_t, const UserOrKernelBuffer&); void inode_size_changed(Badge<Inode>, 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 88bb451435..fc5114f296 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<AccessSpace::Kernel, AccessType::Read>(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<MemoryManager*>(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<AccessSpace::User, AccessType::Read>(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<AccessSpace::User, AccessType::Write>(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 f3f4bbb90c..d9823377ab 100644 --- a/Kernel/VM/MemoryManager.h +++ b/Kernel/VM/MemoryManager.h @@ -100,12 +100,6 @@ public: 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 d3b4d02676..a1d39daf24 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 5480e186de..68d40e53c3 100644 --- a/Kernel/VM/Region.h +++ b/Kernel/VM/Region.h @@ -79,6 +79,7 @@ public: 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 f2fe6a379c..cf6643e736 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 42740f95e2..8f7ccc722f 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 68af2fff21..218c175039 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; |