From cf16b2c8e64709d570c5f54a981017d217e95ed0 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 23 Aug 2022 17:58:05 +0200 Subject: Kernel: Wrap process address spaces in SpinlockProtected This forces anyone who wants to look into and/or manipulate an address space to lock it. And this replaces the previous, more flimsy, manual spinlock use. Note that pointers *into* the address space are not safe to use after you unlock the space. We've got many issues like this, and we'll have to track those down as wlel. --- Kernel/Arch/x86/common/CrashHandler.cpp | 2 +- Kernel/Arch/x86/common/Interrupts.cpp | 12 +- Kernel/Arch/x86/common/Spinlock.cpp | 1 + Kernel/Coredump.cpp | 161 +++++----- Kernel/Devices/KCOVDevice.cpp | 5 +- Kernel/Devices/KCOVDevice.h | 2 +- Kernel/Devices/MemoryDevice.cpp | 4 +- Kernel/Devices/MemoryDevice.h | 2 +- Kernel/FileSystem/AnonymousFile.cpp | 4 +- Kernel/FileSystem/AnonymousFile.h | 2 +- Kernel/FileSystem/File.cpp | 2 +- Kernel/FileSystem/File.h | 2 +- Kernel/FileSystem/InodeFile.cpp | 4 +- Kernel/FileSystem/InodeFile.h | 2 +- Kernel/FileSystem/OpenFileDescription.cpp | 4 +- Kernel/FileSystem/OpenFileDescription.h | 2 +- Kernel/GlobalProcessExposed.cpp | 34 ++- Kernel/Graphics/DisplayConnector.cpp | 4 +- Kernel/Graphics/DisplayConnector.h | 2 +- Kernel/Memory/AddressSpace.h | 4 - Kernel/Memory/MemoryManager.cpp | 70 +++-- Kernel/Memory/MemoryManager.h | 4 +- Kernel/Memory/RegionTree.h | 6 - Kernel/PerformanceEventBuffer.cpp | 16 +- Kernel/Process.cpp | 21 +- Kernel/Process.h | 6 +- Kernel/ProcessSpecificExposed.cpp | 78 ++--- Kernel/Syscall.cpp | 2 +- Kernel/Syscalls/clock.cpp | 6 +- Kernel/Syscalls/execve.cpp | 4 +- Kernel/Syscalls/fork.cpp | 36 +-- Kernel/Syscalls/futex.cpp | 66 +++-- Kernel/Syscalls/get_stack_bounds.cpp | 18 +- Kernel/Syscalls/mmap.cpp | 468 ++++++++++++++++-------------- Kernel/Syscalls/ptrace.cpp | 45 +-- Kernel/Syscalls/sigaction.cpp | 178 ++++++------ Kernel/Syscalls/thread.cpp | 13 +- Kernel/Thread.cpp | 51 ++-- 38 files changed, 712 insertions(+), 631 deletions(-) (limited to 'Kernel') diff --git a/Kernel/Arch/x86/common/CrashHandler.cpp b/Kernel/Arch/x86/common/CrashHandler.cpp index df0ba7c214..c33276717a 100644 --- a/Kernel/Arch/x86/common/CrashHandler.cpp +++ b/Kernel/Arch/x86/common/CrashHandler.cpp @@ -35,7 +35,7 @@ void handle_crash(Kernel::RegisterState const& regs, char const* description, in dump_registers(regs); if (crashed_in_kernel) { - process.address_space().dump_regions(); + process.address_space().with([&](auto& space) { space->dump_regions(); }); PANIC("Crash in ring 0"); } diff --git a/Kernel/Arch/x86/common/Interrupts.cpp b/Kernel/Arch/x86/common/Interrupts.cpp index e91de65693..604a74da33 100644 --- a/Kernel/Arch/x86/common/Interrupts.cpp +++ b/Kernel/Arch/x86/common/Interrupts.cpp @@ -303,9 +303,15 @@ void page_fault_handler(TrapFrame* trap) }; VirtualAddress userspace_sp = VirtualAddress { regs.userspace_sp() }; - if (!faulted_in_kernel && !MM.validate_user_stack(current_thread->process().address_space(), userspace_sp)) { - dbgln("Invalid stack pointer: {}", userspace_sp); - return handle_crash(regs, "Bad stack on page fault", SIGSEGV); + + if (!faulted_in_kernel) { + bool has_valid_stack_pointer = current_thread->process().address_space().with([&](auto& space) { + return MM.validate_user_stack(*space, userspace_sp); + }); + if (!has_valid_stack_pointer) { + dbgln("Invalid stack pointer: {}", userspace_sp); + return handle_crash(regs, "Bad stack on page fault", SIGSEGV); + } } PageFault fault { regs.exception_code, VirtualAddress { fault_address } }; diff --git a/Kernel/Arch/x86/common/Spinlock.cpp b/Kernel/Arch/x86/common/Spinlock.cpp index 4de2eecfc0..5af0b61495 100644 --- a/Kernel/Arch/x86/common/Spinlock.cpp +++ b/Kernel/Arch/x86/common/Spinlock.cpp @@ -55,6 +55,7 @@ u32 RecursiveSpinlock::lock() void RecursiveSpinlock::unlock(u32 prev_flags) { + VERIFY_INTERRUPTS_DISABLED(); VERIFY(m_recursions > 0); VERIFY(m_lock.load(AK::memory_order_relaxed) == FlatPtr(&Processor::current())); if (--m_recursions == 0) { diff --git a/Kernel/Coredump.cpp b/Kernel/Coredump.cpp index 1c7f3add12..a57ac038c0 100644 --- a/Kernel/Coredump.cpp +++ b/Kernel/Coredump.cpp @@ -46,17 +46,19 @@ Coredump::Coredump(NonnullLockRefPtr process, NonnullLockRefPtraddress_space().region_tree().with([&](auto& region_tree) { - for (auto& region : region_tree.regions()) { + m_process->address_space().with([&](auto& space) { + space->region_tree().with([&](auto& region_tree) { + for (auto& region : region_tree.regions()) { #if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS - if (looks_like_userspace_heap_region(region)) - continue; + if (looks_like_userspace_heap_region(region)) + continue; #endif - if (region.access() == Memory::Region::Access::None) - continue; - ++m_num_program_headers; - } + if (region.access() == Memory::Region::Access::None) + continue; + ++m_num_program_headers; + } + }); }); ++m_num_program_headers; // +1 for NOTE segment } @@ -135,38 +137,40 @@ ErrorOr Coredump::write_elf_header() ErrorOr Coredump::write_program_headers(size_t notes_size) { size_t offset = sizeof(ElfW(Ehdr)) + m_num_program_headers * sizeof(ElfW(Phdr)); - m_process->address_space().region_tree().with([&](auto& region_tree) { - for (auto& region : region_tree.regions()) { + m_process->address_space().with([&](auto& space) { + space->region_tree().with([&](auto& region_tree) { + for (auto& region : region_tree.regions()) { #if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS - if (looks_like_userspace_heap_region(region)) - continue; + if (looks_like_userspace_heap_region(region)) + continue; #endif - if (region.access() == Memory::Region::Access::None) - continue; + if (region.access() == Memory::Region::Access::None) + continue; - ElfW(Phdr) phdr {}; + ElfW(Phdr) phdr {}; - phdr.p_type = PT_LOAD; - phdr.p_offset = offset; - phdr.p_vaddr = region.vaddr().get(); - phdr.p_paddr = 0; + phdr.p_type = PT_LOAD; + phdr.p_offset = offset; + phdr.p_vaddr = region.vaddr().get(); + phdr.p_paddr = 0; - phdr.p_filesz = region.page_count() * PAGE_SIZE; - phdr.p_memsz = region.page_count() * PAGE_SIZE; - phdr.p_align = 0; + phdr.p_filesz = region.page_count() * PAGE_SIZE; + phdr.p_memsz = region.page_count() * PAGE_SIZE; + phdr.p_align = 0; - phdr.p_flags = region.is_readable() ? PF_R : 0; - if (region.is_writable()) - phdr.p_flags |= PF_W; - if (region.is_executable()) - phdr.p_flags |= PF_X; + phdr.p_flags = region.is_readable() ? PF_R : 0; + if (region.is_writable()) + phdr.p_flags |= PF_W; + if (region.is_executable()) + phdr.p_flags |= PF_X; - offset += phdr.p_filesz; + offset += phdr.p_filesz; - [[maybe_unused]] auto rc = m_description->write(UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast(&phdr)), sizeof(ElfW(Phdr))); - } + [[maybe_unused]] auto rc = m_description->write(UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast(&phdr)), sizeof(ElfW(Phdr))); + } + }); }); ElfW(Phdr) notes_pheader {}; @@ -188,37 +192,39 @@ ErrorOr Coredump::write_regions() { u8 zero_buffer[PAGE_SIZE] = {}; - return m_process->address_space().region_tree().with([&](auto& region_tree) -> ErrorOr { - for (auto& region : region_tree.regions()) { - VERIFY(!region.is_kernel()); + return m_process->address_space().with([&](auto& space) { + return space->region_tree().with([&](auto& region_tree) -> ErrorOr { + for (auto& region : region_tree.regions()) { + VERIFY(!region.is_kernel()); #if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS - if (looks_like_userspace_heap_region(region)) - continue; + if (looks_like_userspace_heap_region(region)) + continue; #endif - if (region.access() == Memory::Region::Access::None) - continue; - - // If we crashed in the middle of mapping in Regions, they do not have a page directory yet, and will crash on a remap() call - if (!region.is_mapped()) - continue; - - region.set_readable(true); - region.remap(); - - for (size_t i = 0; i < region.page_count(); i++) { - auto page = region.physical_page(i); - auto src_buffer = [&]() -> ErrorOr { - if (page) - return UserOrKernelBuffer::for_user_buffer(reinterpret_cast((region.vaddr().as_ptr() + (i * PAGE_SIZE))), PAGE_SIZE); - // If the current page is not backed by a physical page, we zero it in the coredump file. - return UserOrKernelBuffer::for_kernel_buffer(zero_buffer); - }(); - TRY(m_description->write(src_buffer.value(), PAGE_SIZE)); + if (region.access() == Memory::Region::Access::None) + continue; + + // If we crashed in the middle of mapping in Regions, they do not have a page directory yet, and will crash on a remap() call + if (!region.is_mapped()) + continue; + + region.set_readable(true); + region.remap(); + + for (size_t i = 0; i < region.page_count(); i++) { + auto page = region.physical_page(i); + auto src_buffer = [&]() -> ErrorOr { + if (page) + return UserOrKernelBuffer::for_user_buffer(reinterpret_cast((region.vaddr().as_ptr() + (i * PAGE_SIZE))), PAGE_SIZE); + // If the current page is not backed by a physical page, we zero it in the coredump file. + return UserOrKernelBuffer::for_kernel_buffer(zero_buffer); + }(); + TRY(m_description->write(src_buffer.value(), PAGE_SIZE)); + } } - } - return {}; + return {}; + }); }); } @@ -279,34 +285,36 @@ ErrorOr Coredump::create_notes_threads_data(auto& builder) const ErrorOr Coredump::create_notes_regions_data(auto& builder) const { size_t region_index = 0; - return m_process->address_space().region_tree().with([&](auto& region_tree) -> ErrorOr { - for (auto const& region : region_tree.regions()) { + return m_process->address_space().with([&](auto& space) { + return space->region_tree().with([&](auto& region_tree) -> ErrorOr { + for (auto const& region : region_tree.regions()) { #if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS - if (looks_like_userspace_heap_region(region)) - continue; + if (looks_like_userspace_heap_region(region)) + continue; #endif - if (region.access() == Memory::Region::Access::None) - continue; + if (region.access() == Memory::Region::Access::None) + continue; - ELF::Core::MemoryRegionInfo info {}; - info.header.type = ELF::Core::NotesEntryHeader::Type::MemoryRegionInfo; + ELF::Core::MemoryRegionInfo info {}; + info.header.type = ELF::Core::NotesEntryHeader::Type::MemoryRegionInfo; - info.region_start = region.vaddr().get(); - info.region_end = region.vaddr().offset(region.size()).get(); - info.program_header_index = region_index++; + info.region_start = region.vaddr().get(); + info.region_end = region.vaddr().offset(region.size()).get(); + info.program_header_index = region_index++; - TRY(builder.append_bytes(ReadonlyBytes { (void*)&info, sizeof(info) })); + TRY(builder.append_bytes(ReadonlyBytes { (void*)&info, sizeof(info) })); - // NOTE: The region name *is* null-terminated, so the following is ok: - auto name = region.name(); - if (name.is_empty()) - TRY(builder.append('\0')); - else - TRY(builder.append(name.characters_without_null_termination(), name.length() + 1)); - } - return {}; + // NOTE: The region name *is* null-terminated, so the following is ok: + auto name = region.name(); + if (name.is_empty()) + TRY(builder.append('\0')); + else + TRY(builder.append(name.characters_without_null_termination(), name.length() + 1)); + } + return {}; + }); }); } @@ -344,7 +352,6 @@ ErrorOr Coredump::create_notes_segment_data(auto& builder) const ErrorOr Coredump::write() { - SpinlockLocker lock(m_process->address_space().get_lock()); ScopedAddressSpaceSwitcher switcher(m_process); auto builder = TRY(KBufferBuilder::try_create()); diff --git a/Kernel/Devices/KCOVDevice.cpp b/Kernel/Devices/KCOVDevice.cpp index 7c326e7e70..4d38ce1505 100644 --- a/Kernel/Devices/KCOVDevice.cpp +++ b/Kernel/Devices/KCOVDevice.cpp @@ -116,7 +116,7 @@ ErrorOr KCOVDevice::ioctl(OpenFileDescription&, unsigned request, Userspac } } -ErrorOr KCOVDevice::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) +ErrorOr KCOVDevice::mmap(Process& process, Memory::AddressSpace& address_space, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) { auto pid = process.pid(); auto maybe_kcov_instance = proc_instance->get(pid); @@ -126,8 +126,7 @@ ErrorOr KCOVDevice::mmap(Process& process, OpenFileDescription& if (!kcov_instance->vmobject()) return ENOBUFS; // mmaped, before KCOV_SETBUFSIZE - return process.address_space().allocate_region_with_vmobject( - range, *kcov_instance->vmobject(), offset, {}, prot, shared); + return address_space.allocate_region_with_vmobject(range, *kcov_instance->vmobject(), offset, {}, prot, shared); } } diff --git a/Kernel/Devices/KCOVDevice.h b/Kernel/Devices/KCOVDevice.h index f4860fc2ab..bc5883c61f 100644 --- a/Kernel/Devices/KCOVDevice.h +++ b/Kernel/Devices/KCOVDevice.h @@ -22,7 +22,7 @@ public: static void free_process(); // ^File - ErrorOr mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; + ErrorOr mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; ErrorOr> open(int options) override; protected: diff --git a/Kernel/Devices/MemoryDevice.cpp b/Kernel/Devices/MemoryDevice.cpp index 6f9de0206e..b823498706 100644 --- a/Kernel/Devices/MemoryDevice.cpp +++ b/Kernel/Devices/MemoryDevice.cpp @@ -42,7 +42,7 @@ ErrorOr MemoryDevice::read(OpenFileDescription&, u64 offset, UserOrKerne return length; } -ErrorOr MemoryDevice::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) +ErrorOr MemoryDevice::mmap(Process&, Memory::AddressSpace& address_space, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) { auto viewed_address = PhysicalAddress(offset); @@ -63,7 +63,7 @@ ErrorOr MemoryDevice::mmap(Process& process, OpenFileDescriptio auto vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(viewed_address, range.size())); - return process.address_space().allocate_region_with_vmobject( + return address_space.allocate_region_with_vmobject( range, move(vmobject), 0, diff --git a/Kernel/Devices/MemoryDevice.h b/Kernel/Devices/MemoryDevice.h index 0ca5226887..274438c3de 100644 --- a/Kernel/Devices/MemoryDevice.h +++ b/Kernel/Devices/MemoryDevice.h @@ -19,7 +19,7 @@ public: static NonnullLockRefPtr must_create(); ~MemoryDevice(); - virtual ErrorOr mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; + virtual ErrorOr mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; private: MemoryDevice(); diff --git a/Kernel/FileSystem/AnonymousFile.cpp b/Kernel/FileSystem/AnonymousFile.cpp index 40ad6cc19d..850782495f 100644 --- a/Kernel/FileSystem/AnonymousFile.cpp +++ b/Kernel/FileSystem/AnonymousFile.cpp @@ -17,12 +17,12 @@ AnonymousFile::AnonymousFile(NonnullLockRefPtr vmobje AnonymousFile::~AnonymousFile() = default; -ErrorOr AnonymousFile::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) +ErrorOr AnonymousFile::mmap(Process&, Memory::AddressSpace& address_space, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) { if (offset != 0) return EINVAL; - return process.address_space().allocate_region_with_vmobject(range, m_vmobject, offset, {}, prot, shared); + return address_space.allocate_region_with_vmobject(range, m_vmobject, offset, {}, prot, shared); } ErrorOr> AnonymousFile::pseudo_path(OpenFileDescription const&) const diff --git a/Kernel/FileSystem/AnonymousFile.h b/Kernel/FileSystem/AnonymousFile.h index 5391d82f28..45500671f9 100644 --- a/Kernel/FileSystem/AnonymousFile.h +++ b/Kernel/FileSystem/AnonymousFile.h @@ -20,7 +20,7 @@ public: virtual ~AnonymousFile() override; - virtual ErrorOr mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; + virtual ErrorOr mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; private: virtual StringView class_name() const override { return "AnonymousFile"sv; } diff --git a/Kernel/FileSystem/File.cpp b/Kernel/FileSystem/File.cpp index 07bb430ae1..fa15688e03 100644 --- a/Kernel/FileSystem/File.cpp +++ b/Kernel/FileSystem/File.cpp @@ -35,7 +35,7 @@ ErrorOr File::ioctl(OpenFileDescription&, unsigned, Userspace) return ENOTTY; } -ErrorOr File::mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) +ErrorOr File::mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) { return ENODEV; } diff --git a/Kernel/FileSystem/File.h b/Kernel/FileSystem/File.h index 0679639326..456a6757f6 100644 --- a/Kernel/FileSystem/File.h +++ b/Kernel/FileSystem/File.h @@ -90,7 +90,7 @@ public: virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) = 0; virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) = 0; virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg); - virtual ErrorOr mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared); + virtual ErrorOr mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared); virtual ErrorOr stat() const { return EBADF; } // Although this might be better described "name" or "description", these terms already have other meanings. diff --git a/Kernel/FileSystem/InodeFile.cpp b/Kernel/FileSystem/InodeFile.cpp index 9a805774bf..8d69cd787f 100644 --- a/Kernel/FileSystem/InodeFile.cpp +++ b/Kernel/FileSystem/InodeFile.cpp @@ -85,7 +85,7 @@ ErrorOr InodeFile::ioctl(OpenFileDescription& description, unsigned reques } } -ErrorOr InodeFile::mmap(Process& process, OpenFileDescription& description, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) +ErrorOr InodeFile::mmap(Process&, Memory::AddressSpace& address_space, OpenFileDescription& description, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) { // FIXME: If PROT_EXEC, check that the underlying file system isn't mounted noexec. LockRefPtr vmobject; @@ -94,7 +94,7 @@ ErrorOr InodeFile::mmap(Process& process, OpenFileDescription& else vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(inode())); auto path = TRY(description.pseudo_path()); - return process.address_space().allocate_region_with_vmobject(range, vmobject.release_nonnull(), offset, path->view(), prot, shared); + return address_space.allocate_region_with_vmobject(range, vmobject.release_nonnull(), offset, path->view(), prot, shared); } ErrorOr> InodeFile::pseudo_path(OpenFileDescription const&) const diff --git a/Kernel/FileSystem/InodeFile.h b/Kernel/FileSystem/InodeFile.h index 0b5b6f9c69..c05177362e 100644 --- a/Kernel/FileSystem/InodeFile.h +++ b/Kernel/FileSystem/InodeFile.h @@ -33,7 +33,7 @@ public: virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; - virtual ErrorOr mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; + virtual ErrorOr mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; virtual ErrorOr stat() const override { return inode().metadata().stat(); } virtual ErrorOr> pseudo_path(OpenFileDescription const&) const override; diff --git a/Kernel/FileSystem/OpenFileDescription.cpp b/Kernel/FileSystem/OpenFileDescription.cpp index 4331a5ec8c..153a1baeb3 100644 --- a/Kernel/FileSystem/OpenFileDescription.cpp +++ b/Kernel/FileSystem/OpenFileDescription.cpp @@ -374,9 +374,9 @@ InodeMetadata OpenFileDescription::metadata() const return {}; } -ErrorOr OpenFileDescription::mmap(Process& process, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) +ErrorOr OpenFileDescription::mmap(Process& process, Memory::AddressSpace& address_space, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) { - return m_file->mmap(process, *this, range, offset, prot, shared); + return m_file->mmap(process, address_space, *this, range, offset, prot, shared); } ErrorOr OpenFileDescription::truncate(u64 length) diff --git a/Kernel/FileSystem/OpenFileDescription.h b/Kernel/FileSystem/OpenFileDescription.h index 951ea3f21a..c280ecf71f 100644 --- a/Kernel/FileSystem/OpenFileDescription.h +++ b/Kernel/FileSystem/OpenFileDescription.h @@ -92,7 +92,7 @@ public: RefPtr custody(); RefPtr custody() const; - ErrorOr mmap(Process&, Memory::VirtualRange const&, u64 offset, int prot, bool shared); + ErrorOr mmap(Process&, Memory::AddressSpace&, Memory::VirtualRange const&, u64 offset, int prot, bool shared); bool is_blocking() const; void set_blocking(bool b); diff --git a/Kernel/GlobalProcessExposed.cpp b/Kernel/GlobalProcessExposed.cpp index 356f1c7dc2..dcbd29fdf3 100644 --- a/Kernel/GlobalProcessExposed.cpp +++ b/Kernel/GlobalProcessExposed.cpp @@ -540,13 +540,33 @@ private: TRY(process_object.add("nfds"sv, process.fds().with_shared([](auto& fds) { return fds.open_count(); }))); TRY(process_object.add("name"sv, process.name())); TRY(process_object.add("executable"sv, process.executable() ? TRY(process.executable()->try_serialize_absolute_path())->view() : ""sv)); - TRY(process_object.add("amount_virtual"sv, process.address_space().amount_virtual())); - TRY(process_object.add("amount_resident"sv, process.address_space().amount_resident())); - TRY(process_object.add("amount_dirty_private"sv, process.address_space().amount_dirty_private())); - TRY(process_object.add("amount_clean_inode"sv, TRY(process.address_space().amount_clean_inode()))); - TRY(process_object.add("amount_shared"sv, process.address_space().amount_shared())); - TRY(process_object.add("amount_purgeable_volatile"sv, process.address_space().amount_purgeable_volatile())); - TRY(process_object.add("amount_purgeable_nonvolatile"sv, process.address_space().amount_purgeable_nonvolatile())); + + size_t amount_virtual = 0; + size_t amount_resident = 0; + size_t amount_dirty_private = 0; + size_t amount_clean_inode = 0; + size_t amount_shared = 0; + size_t amount_purgeable_volatile = 0; + size_t amount_purgeable_nonvolatile = 0; + + TRY(process.address_space().with([&](auto& space) -> ErrorOr { + amount_virtual = space->amount_virtual(); + amount_resident = space->amount_resident(); + amount_dirty_private = space->amount_dirty_private(); + amount_clean_inode = TRY(space->amount_clean_inode()); + amount_shared = space->amount_shared(); + amount_purgeable_volatile = space->amount_purgeable_volatile(); + amount_purgeable_nonvolatile = space->amount_purgeable_nonvolatile(); + return {}; + })); + + TRY(process_object.add("amount_virtual"sv, amount_virtual)); + TRY(process_object.add("amount_resident"sv, amount_resident)); + TRY(process_object.add("amount_dirty_private"sv, amount_dirty_private)); + TRY(process_object.add("amount_clean_inode"sv, amount_clean_inode)); + TRY(process_object.add("amount_shared"sv, amount_shared)); + TRY(process_object.add("amount_purgeable_volatile"sv, amount_purgeable_volatile)); + TRY(process_object.add("amount_purgeable_nonvolatile"sv, amount_purgeable_nonvolatile)); TRY(process_object.add("dumpable"sv, process.is_dumpable())); TRY(process_object.add("kernel"sv, process.is_kernel_process())); auto thread_array = TRY(process_object.add_array("threads"sv)); diff --git a/Kernel/Graphics/DisplayConnector.cpp b/Kernel/Graphics/DisplayConnector.cpp index ffb47e792e..de02a4ed6f 100644 --- a/Kernel/Graphics/DisplayConnector.cpp +++ b/Kernel/Graphics/DisplayConnector.cpp @@ -32,13 +32,13 @@ DisplayConnector::DisplayConnector(size_t framebuffer_resource_size, bool enable { } -ErrorOr DisplayConnector::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) +ErrorOr DisplayConnector::mmap(Process&, Memory::AddressSpace& address_space, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) { VERIFY(m_shared_framebuffer_vmobject); if (offset != 0) return Error::from_errno(ENOTSUP); - return process.address_space().allocate_region_with_vmobject( + return address_space.allocate_region_with_vmobject( range, *m_shared_framebuffer_vmobject, 0, diff --git a/Kernel/Graphics/DisplayConnector.h b/Kernel/Graphics/DisplayConnector.h index 240684bd77..5dc0dc0cd8 100644 --- a/Kernel/Graphics/DisplayConnector.h +++ b/Kernel/Graphics/DisplayConnector.h @@ -137,7 +137,7 @@ private: virtual bool can_write(OpenFileDescription const&, u64) const final override { return true; } virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override final; virtual ErrorOr write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override final; - virtual ErrorOr mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) override final; + virtual ErrorOr mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) override final; virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override final; virtual StringView class_name() const override final { return "DisplayConnector"sv; } diff --git a/Kernel/Memory/AddressSpace.h b/Kernel/Memory/AddressSpace.h index 23f7c7a76b..e2df65e0b6 100644 --- a/Kernel/Memory/AddressSpace.h +++ b/Kernel/Memory/AddressSpace.h @@ -53,8 +53,6 @@ public: void remove_all_regions(Badge); - RecursiveSpinlock& get_lock() const { return m_lock; } - ErrorOr amount_clean_inode() const; size_t amount_dirty_private() const; size_t amount_virtual() const; @@ -66,8 +64,6 @@ public: private: AddressSpace(NonnullLockRefPtr, VirtualRange total_range); - mutable RecursiveSpinlock m_lock { LockRank::None }; - LockRefPtr m_page_directory; // NOTE: The total range is also in the RegionTree, but since it never changes, diff --git a/Kernel/Memory/MemoryManager.cpp b/Kernel/Memory/MemoryManager.cpp index 01d73bc959..3ab26a5731 100644 --- a/Kernel/Memory/MemoryManager.cpp +++ b/Kernel/Memory/MemoryManager.cpp @@ -646,40 +646,32 @@ Region* MemoryManager::kernel_region_from_vaddr(VirtualAddress address) return MM.m_region_tree.with([&](auto& region_tree) { return region_tree.find_region_containing(address); }); } -Region* MemoryManager::find_user_region_from_vaddr_no_lock(AddressSpace& space, VirtualAddress vaddr) -{ - VERIFY(space.get_lock().is_locked_by_current_processor()); - return space.find_region_containing({ vaddr, 1 }); -} - Region* MemoryManager::find_user_region_from_vaddr(AddressSpace& space, VirtualAddress vaddr) { - SpinlockLocker lock(space.get_lock()); - return find_user_region_from_vaddr_no_lock(space, vaddr); + return space.find_region_containing({ vaddr, 1 }); } -void MemoryManager::validate_syscall_preconditions(AddressSpace& space, RegisterState const& regs) +void MemoryManager::validate_syscall_preconditions(Process& process, RegisterState const& regs) { - // We take the space lock once here and then use the no_lock variants - // to avoid excessive spinlock recursion in this extremely common path. - SpinlockLocker lock(space.get_lock()); - - auto unlock_and_handle_crash = [&lock, ®s](char const* description, int signal) { - lock.unlock(); - handle_crash(regs, description, signal); + bool should_crash = false; + char const* crash_description = nullptr; + int crash_signal = 0; + + auto unlock_and_handle_crash = [&](char const* description, int signal) { + should_crash = true; + crash_description = description; + crash_signal = signal; }; - { + process.address_space().with([&](auto& space) -> void { VirtualAddress userspace_sp = VirtualAddress { regs.userspace_sp() }; - if (!MM.validate_user_stack_no_lock(space, userspace_sp)) { + if (!MM.validate_user_stack(*space, userspace_sp)) { dbgln("Invalid stack pointer: {}", userspace_sp); return unlock_and_handle_crash("Bad stack on syscall entry", SIGSEGV); } - } - { VirtualAddress ip = VirtualAddress { regs.ip() }; - auto* calling_region = MM.find_user_region_from_vaddr_no_lock(space, ip); + auto* calling_region = MM.find_user_region_from_vaddr(*space, ip); if (!calling_region) { dbgln("Syscall from {:p} which has no associated region", ip); return unlock_and_handle_crash("Syscall from unknown region", SIGSEGV); @@ -690,10 +682,14 @@ void MemoryManager::validate_syscall_preconditions(AddressSpace& space, Register return unlock_and_handle_crash("Syscall from writable memory", SIGSEGV); } - if (space.enforces_syscall_regions() && !calling_region->is_syscall_region()) { + if (space->enforces_syscall_regions() && !calling_region->is_syscall_region()) { dbgln("Syscall from non-syscall region"); return unlock_and_handle_crash("Syscall from non-syscall region", SIGSEGV); } + }); + + if (should_crash) { + handle_crash(regs, crash_description, crash_signal); } } @@ -830,12 +826,20 @@ ErrorOr MemoryManager::commit_physical_pages(size_t pa dbgln("MM: Unable to commit {} pages, have only {}", page_count, m_system_memory_info.physical_pages_uncommitted); Process::for_each([&](Process const& process) { + size_t amount_resident = 0; + size_t amount_shared = 0; + size_t amount_virtual = 0; + process.address_space().with([&](auto& space) { + amount_resident = space->amount_resident(); + amount_shared = space->amount_shared(); + amount_virtual = space->amount_virtual(); + }); dbgln("{}({}) resident:{}, shared:{}, virtual:{}", process.name(), process.pid(), - process.address_space().amount_resident() / PAGE_SIZE, - process.address_space().amount_shared() / PAGE_SIZE, - process.address_space().amount_virtual() / PAGE_SIZE); + amount_resident / PAGE_SIZE, + amount_shared / PAGE_SIZE, + amount_virtual / PAGE_SIZE); return IterationDecision::Continue; }); @@ -1007,7 +1011,9 @@ ErrorOr> MemoryManager::allocate_contiguou void MemoryManager::enter_process_address_space(Process& process) { - enter_address_space(process.address_space()); + process.address_space().with([](auto& space) { + enter_address_space(*space); + }); } void MemoryManager::enter_address_space(AddressSpace& space) @@ -1100,23 +1106,15 @@ void MemoryManager::unquickmap_page() mm_data.m_quickmap_in_use.unlock(mm_data.m_quickmap_prev_flags); } -bool MemoryManager::validate_user_stack_no_lock(AddressSpace& space, VirtualAddress vaddr) const +bool MemoryManager::validate_user_stack(AddressSpace& space, VirtualAddress vaddr) const { - VERIFY(space.get_lock().is_locked_by_current_processor()); - if (!is_user_address(vaddr)) return false; - auto* region = find_user_region_from_vaddr_no_lock(space, vaddr); + auto* region = find_user_region_from_vaddr(space, vaddr); return region && region->is_user() && region->is_stack(); } -bool MemoryManager::validate_user_stack(AddressSpace& space, VirtualAddress vaddr) const -{ - SpinlockLocker lock(space.get_lock()); - return validate_user_stack_no_lock(space, vaddr); -} - void MemoryManager::unregister_kernel_region(Region& region) { VERIFY(region.is_kernel()); diff --git a/Kernel/Memory/MemoryManager.h b/Kernel/Memory/MemoryManager.h index 6bf827773c..4b62ed4e45 100644 --- a/Kernel/Memory/MemoryManager.h +++ b/Kernel/Memory/MemoryManager.h @@ -159,7 +159,6 @@ public: static void enter_process_address_space(Process&); static void enter_address_space(AddressSpace&); - bool validate_user_stack_no_lock(AddressSpace&, VirtualAddress) const; bool validate_user_stack(AddressSpace&, VirtualAddress) const; enum class ShouldZeroFill { @@ -222,8 +221,7 @@ public: } static Region* find_user_region_from_vaddr(AddressSpace&, VirtualAddress); - static Region* find_user_region_from_vaddr_no_lock(AddressSpace&, VirtualAddress); - static void validate_syscall_preconditions(AddressSpace&, RegisterState const&); + static void validate_syscall_preconditions(Process&, RegisterState const&); void dump_kernel_regions(); diff --git a/Kernel/Memory/RegionTree.h b/Kernel/Memory/RegionTree.h index aa5226d448..e028bb3130 100644 --- a/Kernel/Memory/RegionTree.h +++ b/Kernel/Memory/RegionTree.h @@ -45,9 +45,6 @@ public: void delete_all_regions_assuming_they_are_unmapped(); - // FIXME: Access the region tree through a SpinlockProtected or similar. - RecursiveSpinlock& get_lock() const { return m_lock; } - bool remove(Region&); Region* find_region_containing(VirtualAddress); @@ -58,9 +55,6 @@ private: ErrorOr allocate_range_specific(VirtualAddress base, size_t size); ErrorOr allocate_range_randomized(size_t size, size_t alignment = PAGE_SIZE); - // FIXME: We need a Region rank, but we don't know where to put it. - RecursiveSpinlock mutable m_lock { LockRank::None }; - IntrusiveRedBlackTree<&Region::m_tree_node> m_regions; VirtualRange const m_total_range; }; diff --git a/Kernel/PerformanceEventBuffer.cpp b/Kernel/PerformanceEventBuffer.cpp index 4852c4c8cf..191a50daab 100644 --- a/Kernel/PerformanceEventBuffer.cpp +++ b/Kernel/PerformanceEventBuffer.cpp @@ -334,8 +334,6 @@ OwnPtr PerformanceEventBuffer::try_create_with_size(size ErrorOr PerformanceEventBuffer::add_process(Process const& process, ProcessEventType event_type) { - SpinlockLocker locker(process.address_space().get_lock()); - OwnPtr executable; if (process.executable()) executable = TRY(process.executable()->try_serialize_absolute_path()); @@ -354,12 +352,14 @@ ErrorOr PerformanceEventBuffer::add_process(Process const& process, Proces }); TRY(result); - return process.address_space().region_tree().with([&](auto& region_tree) -> ErrorOr { - for (auto const& region : region_tree.regions()) { - TRY(append_with_ip_and_bp(process.pid(), 0, - 0, 0, PERF_EVENT_MMAP, 0, region.range().base().get(), region.range().size(), region.name())); - } - return {}; + return process.address_space().with([&](auto& space) { + return space->region_tree().with([&](auto& region_tree) -> ErrorOr { + for (auto const& region : region_tree.regions()) { + TRY(append_with_ip_and_bp(process.pid(), 0, + 0, 0, PERF_EVENT_MMAP, 0, region.range().base().get(), region.range().size(), region.name())); + } + return {}; + }); }); } diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index b4ae1bcbe8..19227ea138 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -218,16 +218,25 @@ void Process::unprotect_data() ErrorOr> Process::try_create(LockRefPtr& first_thread, NonnullOwnPtr name, UserID uid, GroupID gid, ProcessID ppid, bool is_kernel_process, RefPtr current_directory, RefPtr executable, TTY* tty, Process* fork_parent) { - auto space = TRY(Memory::AddressSpace::try_create(fork_parent ? &fork_parent->address_space() : nullptr)); + OwnPtr new_address_space; + if (fork_parent) { + TRY(fork_parent->address_space().with([&](auto& parent_address_space) -> ErrorOr { + new_address_space = TRY(Memory::AddressSpace::try_create(parent_address_space.ptr())); + return {}; + })); + } else { + new_address_space = TRY(Memory::AddressSpace::try_create(nullptr)); + } auto unveil_tree = UnveilNode { TRY(KString::try_create("/"sv)), UnveilMetadata(TRY(KString::try_create("/"sv))) }; auto credentials = TRY(Credentials::create(uid, gid, uid, gid, uid, gid, {})); auto process = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Process(move(name), move(credentials), ppid, is_kernel_process, move(current_directory), move(executable), tty, move(unveil_tree)))); - TRY(process->attach_resources(move(space), first_thread, fork_parent)); + TRY(process->attach_resources(new_address_space.release_nonnull(), first_thread, fork_parent)); return process; } Process::Process(NonnullOwnPtr name, NonnullRefPtr credentials, ProcessID ppid, bool is_kernel_process, RefPtr current_directory, RefPtr executable, TTY* tty, UnveilNode unveil_tree) : m_name(move(name)) + , m_space(LockRank::None) , m_protected_data_lock(LockRank::None) , m_is_kernel_process(is_kernel_process) , m_executable(LockRank::None, move(executable)) @@ -248,7 +257,9 @@ Process::Process(NonnullOwnPtr name, NonnullRefPtr credent ErrorOr Process::attach_resources(NonnullOwnPtr&& preallocated_space, LockRefPtr& first_thread, Process* fork_parent) { - m_space = move(preallocated_space); + m_space.with([&](auto& space) { + space = move(preallocated_space); + }); auto create_first_thread = [&] { if (fork_parent) { @@ -401,7 +412,7 @@ void Process::crash(int signal, FlatPtr ip, bool out_of_memory) protected_data.termination_signal = signal; }); set_should_generate_coredump(!out_of_memory); - address_space().dump_regions(); + address_space().with([](auto& space) { space->dump_regions(); }); VERIFY(is_user_process()); die(); // We can not return from here, as there is nowhere @@ -662,7 +673,7 @@ void Process::finalize() unblock_waiters(Thread::WaitBlocker::UnblockFlags::Terminated); - m_space->remove_all_regions({}); + m_space.with([](auto& space) { space->remove_all_regions({}); }); VERIFY(ref_count() > 0); // WaitBlockerSet::finalize will be in charge of dropping the last diff --git a/Kernel/Process.h b/Kernel/Process.h index 8186aa64f5..7c5024f178 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -558,8 +558,8 @@ public: PerformanceEventBuffer* perf_events() { return m_perf_event_buffer; } PerformanceEventBuffer const* perf_events() const { return m_perf_event_buffer; } - Memory::AddressSpace& address_space() { return *m_space; } - Memory::AddressSpace const& address_space() const { return *m_space; } + SpinlockProtected>& address_space() { return m_space; } + SpinlockProtected> const& address_space() const { return m_space; } VirtualAddress signal_trampoline() const { @@ -656,7 +656,7 @@ private: NonnullOwnPtr m_name; - OwnPtr m_space; + SpinlockProtected> m_space; LockRefPtr m_pg; diff --git a/Kernel/ProcessSpecificExposed.cpp b/Kernel/ProcessSpecificExposed.cpp index 1bda213e5c..2e4cea6c29 100644 --- a/Kernel/ProcessSpecificExposed.cpp +++ b/Kernel/ProcessSpecificExposed.cpp @@ -267,45 +267,47 @@ ErrorOr Process::procfs_get_fds_stats(KBufferBuilder& builder) const ErrorOr Process::procfs_get_virtual_memory_stats(KBufferBuilder& builder) const { auto array = TRY(JsonArraySerializer<>::try_create(builder)); - TRY(address_space().region_tree().with([&](auto& region_tree) -> ErrorOr { - for (auto const& region : region_tree.regions()) { - auto current_process_credentials = Process::current().credentials(); - if (!region.is_user() && !current_process_credentials->is_superuser()) - continue; - auto region_object = TRY(array.add_object()); - TRY(region_object.add("readable"sv, region.is_readable())); - TRY(region_object.add("writable"sv, region.is_writable())); - TRY(region_object.add("executable"sv, region.is_executable())); - TRY(region_object.add("stack"sv, region.is_stack())); - TRY(region_object.add("shared"sv, region.is_shared())); - TRY(region_object.add("syscall"sv, region.is_syscall_region())); - TRY(region_object.add("purgeable"sv, region.vmobject().is_anonymous())); - if (region.vmobject().is_anonymous()) { - TRY(region_object.add("volatile"sv, static_cast(region.vmobject()).is_volatile())); - } - TRY(region_object.add("cacheable"sv, region.is_cacheable())); - TRY(region_object.add("address"sv, region.vaddr().get())); - TRY(region_object.add("size"sv, region.size())); - TRY(region_object.add("amount_resident"sv, region.amount_resident())); - TRY(region_object.add("amount_dirty"sv, region.amount_dirty())); - TRY(region_object.add("cow_pages"sv, region.cow_pages())); - TRY(region_object.add("name"sv, region.name())); - TRY(region_object.add("vmobject"sv, region.vmobject().class_name())); - - StringBuilder pagemap_builder; - for (size_t i = 0; i < region.page_count(); ++i) { - auto page = region.physical_page(i); - if (!page) - pagemap_builder.append('N'); - else if (page->is_shared_zero_page() || page->is_lazy_committed_page()) - pagemap_builder.append('Z'); - else - pagemap_builder.append('P'); + TRY(address_space().with([&](auto& space) { + return space->region_tree().with([&](auto& region_tree) -> ErrorOr { + for (auto const& region : region_tree.regions()) { + auto current_process_credentials = Process::current().credentials(); + if (!region.is_user() && !current_process_credentials->is_superuser()) + continue; + auto region_object = TRY(array.add_object()); + TRY(region_object.add("readable"sv, region.is_readable())); + TRY(region_object.add("writable"sv, region.is_writable())); + TRY(region_object.add("executable"sv, region.is_executable())); + TRY(region_object.add("stack"sv, region.is_stack())); + TRY(region_object.add("shared"sv, region.is_shared())); + TRY(region_object.add("syscall"sv, region.is_syscall_region())); + TRY(region_object.add("purgeable"sv, region.vmobject().is_anonymous())); + if (region.vmobject().is_anonymous()) { + TRY(region_object.add("volatile"sv, static_cast(region.vmobject()).is_volatile())); + } + TRY(region_object.add("cacheable"sv, region.is_cacheable())); + TRY(region_object.add("address"sv, region.vaddr().get())); + TRY(region_object.add("size"sv, region.size())); + TRY(region_object.add("amount_resident"sv, region.amount_resident())); + TRY(region_object.add("amount_dirty"sv, region.amount_dirty())); + TRY(region_object.add("cow_pages"sv, region.cow_pages())); + TRY(region_object.add("name"sv, region.name())); + TRY(region_object.add("vmobject"sv, region.vmobject().class_name())); + + StringBuilder pagemap_builder; + for (size_t i = 0; i < region.page_count(); ++i) { + auto page = region.physical_page(i); + if (!page) + pagemap_builder.append('N'); + else if (page->is_shared_zero_page() || page->is_lazy_committed_page()) + pagemap_builder.append('Z'); + else + pagemap_builder.append('P'); + } + TRY(region_object.add("pagemap"sv, pagemap_builder.string_view())); + TRY(region_object.finish()); } - TRY(region_object.add("pagemap"sv, pagemap_builder.string_view())); - TRY(region_object.finish()); - } - return {}; + return {}; + }); })); TRY(array.finish()); return {}; diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index cd1d46c3c2..9fcd0700b8 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -204,7 +204,7 @@ NEVER_INLINE void syscall_handler(TrapFrame* trap) PANIC("Syscall from process with IOPL != 0"); } - Memory::MemoryManager::validate_syscall_preconditions(process.address_space(), regs); + Memory::MemoryManager::validate_syscall_preconditions(process, regs); FlatPtr function; FlatPtr arg1; diff --git a/Kernel/Syscalls/clock.cpp b/Kernel/Syscalls/clock.cpp index ed80b8964b..0f0f5d4d7e 100644 --- a/Kernel/Syscalls/clock.cpp +++ b/Kernel/Syscalls/clock.cpp @@ -17,8 +17,10 @@ ErrorOr Process::sys$map_time_page() auto& vmobject = TimeManagement::the().time_page_vmobject(); - auto* region = TRY(address_space().allocate_region_with_vmobject(Memory::RandomizeVirtualAddress::Yes, {}, PAGE_SIZE, PAGE_SIZE, vmobject, 0, "Kernel time page"sv, PROT_READ, true)); - return region->vaddr().get(); + return address_space().with([&](auto& space) -> ErrorOr { + auto* region = TRY(space->allocate_region_with_vmobject(Memory::RandomizeVirtualAddress::Yes, {}, PAGE_SIZE, PAGE_SIZE, vmobject, 0, "Kernel time page"sv, PROT_READ, true)); + return region->vaddr().get(); + }); } ErrorOr Process::sys$clock_gettime(clockid_t clock_id, Userspace user_ts) diff --git a/Kernel/Syscalls/execve.cpp b/Kernel/Syscalls/execve.cpp index fc281fc4a8..99a5b49c91 100644 --- a/Kernel/Syscalls/execve.cpp +++ b/Kernel/Syscalls/execve.cpp @@ -551,7 +551,7 @@ ErrorOr Process::do_exec(NonnullLockRefPtr main_progr // This ensures that the process always has a valid page directory. Memory::MemoryManager::enter_address_space(*load_result.space); - m_space = load_result.space.release_nonnull(); + m_space.with([&](auto& space) { space = load_result.space.release_nonnull(); }); m_executable.with([&](auto& executable) { executable = main_program_description->custody(); }); m_arguments = move(arguments); @@ -661,7 +661,7 @@ ErrorOr Process::do_exec(NonnullLockRefPtr main_progr regs.rip = load_result.entry_eip; regs.rsp = new_userspace_sp; #endif - regs.cr3 = address_space().page_directory().cr3(); + regs.cr3 = address_space().with([](auto& space) { return space->page_directory().cr3(); }); { TemporaryChange profiling_disabler(m_profiling, was_profiling); diff --git a/Kernel/Syscalls/fork.cpp b/Kernel/Syscalls/fork.cpp index 68a637d8f5..130a491294 100644 --- a/Kernel/Syscalls/fork.cpp +++ b/Kernel/Syscalls/fork.cpp @@ -65,7 +65,6 @@ ErrorOr Process::sys$fork(RegisterState& regs) }); dbgln_if(FORK_DEBUG, "fork: child={}", child); - child->address_space().set_enforces_syscall_regions(address_space().enforces_syscall_regions()); // A child created via fork(2) inherits a copy of its parent's signal mask child_first_thread->update_signal_mask(Thread::current()->signal_mask()); @@ -123,23 +122,26 @@ ErrorOr Process::sys$fork(RegisterState& regs) # error Unknown architecture #endif - { - TRY(address_space().region_tree().with([&](auto& parent_region_tree) -> ErrorOr { - return child->address_space().region_tree().with([&](auto& child_region_tree) -> ErrorOr { - for (auto& region : parent_region_tree.regions()) { - dbgln_if(FORK_DEBUG, "fork: cloning Region '{}' @ {}", region.name(), region.vaddr()); - auto region_clone = TRY(region.try_clone()); - TRY(region_clone->map(child->address_space().page_directory(), Memory::ShouldFlushTLB::No)); - TRY(child_region_tree.place_specifically(*region_clone, region.range())); - auto* child_region = region_clone.leak_ptr(); - - if (®ion == m_master_tls_region.unsafe_ptr()) - child->m_master_tls_region = TRY(child_region->try_make_weak_ptr()); - } - return {}; + TRY(address_space().with([&](auto& parent_space) { + return child->address_space().with([&](auto& child_space) { + child_space->set_enforces_syscall_regions(parent_space->enforces_syscall_regions()); + return parent_space->region_tree().with([&](auto& parent_region_tree) -> ErrorOr { + return child_space->region_tree().with([&](auto& child_region_tree) -> ErrorOr { + for (auto& region : parent_region_tree.regions()) { + dbgln_if(FORK_DEBUG, "fork: cloning Region '{}' @ {}", region.name(), region.vaddr()); + auto region_clone = TRY(region.try_clone()); + TRY(region_clone->map(child_space->page_directory(), Memory::ShouldFlushTLB::No)); + TRY(child_region_tree.place_specifically(*region_clone, region.range())); + auto* child_region = region_clone.leak_ptr(); + + if (®ion == m_master_tls_region.unsafe_ptr()) + child->m_master_tls_region = TRY(child_region->try_make_weak_ptr()); + } + return {}; + }); }); - })); - } + }); + })); thread_finalizer_guard.disarm(); diff --git a/Kernel/Syscalls/futex.cpp b/Kernel/Syscalls/futex.cpp index 8289385065..6b12bd584a 100644 --- a/Kernel/Syscalls/futex.cpp +++ b/Kernel/Syscalls/futex.cpp @@ -18,7 +18,7 @@ static Singletonwith([this](auto& queues) { - auto const* address_space = &this->address_space(); + auto const* address_space = this->address_space().with([](auto& space) { return space.ptr(); }); queues.remove_all_matching([address_space](auto& futex_key, auto& futex_queue) { if ((futex_key.raw.offset & futex_key_private_flag) == 0) return false; @@ -45,45 +45,47 @@ ErrorOr Process::get_futex_key(FlatPtr user_address, bool shared if (!shared) { // If this is thread-shared, we can skip searching the matching region return GlobalFutexKey { .private_ = { - .address_space = &address_space(), + .address_space = this->address_space().with([](auto& space) { return space.ptr(); }), .user_address = user_address | futex_key_private_flag, } }; } - auto* matching_region = address_space().find_region_containing(range); - if (!matching_region) - return EFAULT; + return address_space().with([&](auto& space) -> ErrorOr { + auto* matching_region = space->find_region_containing(range); + if (!matching_region) + return EFAULT; + + // The user wants to share this futex, but if the address doesn't point to a shared resource, there's not + // much sharing to be done, so let's mark this as private + if (!matching_region->is_shared()) { + return GlobalFutexKey { + .private_ = { + .address_space = space.ptr(), + .user_address = user_address | futex_key_private_flag, + } + }; + } + + // This address is backed by a shared VMObject, if it's an AnonymousVMObject, it can be shared between processes + // via forking, and shared regions that are cloned during a fork retain their original AnonymousVMObject. + // On the other hand, if it's a SharedInodeVMObject, it can be shared by two processes mapping the same file as + // MAP_SHARED, but since they are deduplicated based on the inode, in all cases the VMObject pointer should be + // a unique global identifier. + // NOTE: This assumes that a program will not unmap the only region keeping the vmobject alive while waiting on it, + // if it does, it will get stuck waiting forever until interrupted by a signal, but since that use case is defined as + // a programmer error, we are fine with it. + + auto const& vmobject = matching_region->vmobject(); + if (vmobject.is_inode()) + VERIFY(vmobject.is_shared_inode()); - // The user wants to share this futex, but if the address doesn't point to a shared resource, there's not - // much sharing to be done, so let's mark this as private - if (!matching_region->is_shared()) { return GlobalFutexKey { - .private_ = { - .address_space = &address_space(), - .user_address = user_address | futex_key_private_flag, - } + .shared = { + .vmobject = &vmobject, + .offset = matching_region->offset_in_vmobject_from_vaddr(range.base()) } }; - } - - // This address is backed by a shared VMObject, if it's an AnonymousVMObject, it can be shared between processes - // via forking, and shared regions that are cloned during a fork retain their original AnonymousVMObject. - // On the other hand, if it's a SharedInodeVMObject, it can be shared by two processes mapping the same file as - // MAP_SHARED, but since they are deduplicated based on the inode, in all cases the VMObject pointer should be - // a unique global identifier. - // NOTE: This assumes that a program will not unmap the only region keeping the vmobject alive while waiting on it, - // if it does, it will get stuck waiting forever until interrupted by a signal, but since that use case is defined as - // a programmer error, we are fine with it. - - auto const& vmobject = matching_region->vmobject(); - if (vmobject.is_inode()) - VERIFY(vmobject.is_shared_inode()); - - return GlobalFutexKey { - .shared = { - .vmobject = &vmobject, - .offset = matching_region->offset_in_vmobject_from_vaddr(range.base()) } - }; + }); } ErrorOr Process::sys$futex(Userspace user_params) diff --git a/Kernel/Syscalls/get_stack_bounds.cpp b/Kernel/Syscalls/get_stack_bounds.cpp index 1c77277b47..7fdbfdd0e8 100644 --- a/Kernel/Syscalls/get_stack_bounds.cpp +++ b/Kernel/Syscalls/get_stack_bounds.cpp @@ -14,16 +14,18 @@ ErrorOr Process::sys$get_stack_bounds(Userspace user_stack_ba VERIFY_NO_PROCESS_BIG_LOCK(this); auto& regs = Thread::current()->get_register_dump_from_stack(); FlatPtr stack_pointer = regs.userspace_sp(); - auto* stack_region = address_space().find_region_containing(Memory::VirtualRange { VirtualAddress(stack_pointer), 1 }); + return address_space().with([&](auto& space) -> ErrorOr { + auto* stack_region = space->find_region_containing(Memory::VirtualRange { VirtualAddress(stack_pointer), 1 }); - // The syscall handler should have killed us if we had an invalid stack pointer. - VERIFY(stack_region); + // The syscall handler should have killed us if we had an invalid stack pointer. + VERIFY(stack_region); - FlatPtr stack_base = stack_region->range().base().get(); - size_t stack_size = stack_region->size(); - TRY(copy_to_user(user_stack_base, &stack_base)); - TRY(copy_to_user(user_stack_size, &stack_size)); - return 0; + FlatPtr stack_base = stack_region->range().base().get(); + size_t stack_size = stack_region->size(); + TRY(copy_to_user(user_stack_base, &stack_base)); + TRY(copy_to_user(user_stack_size, &stack_size)); + return 0; + }); } } diff --git a/Kernel/Syscalls/mmap.cpp b/Kernel/Syscalls/mmap.cpp index c5afee7b7b..0d5ebba9bd 100644 --- a/Kernel/Syscalls/mmap.cpp +++ b/Kernel/Syscalls/mmap.cpp @@ -192,32 +192,23 @@ ErrorOr Process::sys$mmap(Userspace use Memory::Region* region = nullptr; - // If MAP_FIXED is specified, existing mappings that intersect the requested range are removed. - if (map_fixed) - TRY(address_space().unmap_mmap_range(VirtualAddress(addr), size)); - - Memory::VirtualRange requested_range { VirtualAddress { addr }, rounded_size }; - if (addr && !(map_fixed || map_fixed_noreplace)) { - // If there's an address but MAP_FIXED wasn't specified, the address is just a hint. - requested_range = { {}, rounded_size }; - } + LockRefPtr description; + LockRefPtr vmobject; if (map_anonymous) { auto strategy = map_noreserve ? AllocationStrategy::None : AllocationStrategy::Reserve; - LockRefPtr vmobject; + if (flags & MAP_PURGEABLE) { vmobject = TRY(Memory::AnonymousVMObject::try_create_purgeable_with_size(rounded_size, strategy)); } else { vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(rounded_size, strategy)); } - - region = TRY(address_space().allocate_region_with_vmobject(map_randomized ? Memory::RandomizeVirtualAddress::Yes : Memory::RandomizeVirtualAddress::No, requested_range.base(), requested_range.size(), alignment, vmobject.release_nonnull(), 0, {}, prot, map_shared)); } else { if (offset < 0) return EINVAL; if (static_cast(offset) & ~PAGE_MASK) return EINVAL; - auto description = TRY(open_file_description(fd)); + description = TRY(open_file_description(fd)); if (description->is_directory()) return ENODEV; // Require read access even when read protection is not requested. @@ -229,24 +220,40 @@ ErrorOr Process::sys$mmap(Userspace use } if (description->inode()) TRY(validate_inode_mmap_prot(prot, *description->inode(), map_shared)); - - region = TRY(description->mmap(*this, requested_range, static_cast(offset), prot, map_shared)); } - if (!region) - return ENOMEM; + return address_space().with([&](auto& space) -> ErrorOr { + // If MAP_FIXED is specified, existing mappings that intersect the requested range are removed. + if (map_fixed) + TRY(space->unmap_mmap_range(VirtualAddress(addr), size)); - region->set_mmap(true); - if (map_shared) - region->set_shared(true); - if (map_stack) - region->set_stack(true); - if (name) - region->set_name(move(name)); + Memory::VirtualRange requested_range { VirtualAddress { addr }, rounded_size }; + if (addr && !(map_fixed || map_fixed_noreplace)) { + // If there's an address but MAP_FIXED wasn't specified, the address is just a hint. + requested_range = { {}, rounded_size }; + } + + if (map_anonymous) { + region = TRY(space->allocate_region_with_vmobject(map_randomized ? Memory::RandomizeVirtualAddress::Yes : Memory::RandomizeVirtualAddress::No, requested_range.base(), requested_range.size(), alignment, vmobject.release_nonnull(), 0, {}, prot, map_shared)); + } else { + region = TRY(description->mmap(*this, *space, requested_range, static_cast(offset), prot, map_shared)); + } + + if (!region) + return ENOMEM; - PerformanceManager::add_mmap_perf_event(*this, *region); + region->set_mmap(true); + if (map_shared) + region->set_shared(true); + if (map_stack) + region->set_stack(true); + if (name) + region->set_name(move(name)); - return region->vaddr().get(); + PerformanceManager::add_mmap_perf_event(*this, *region); + + return region->vaddr().get(); + }); } ErrorOr Process::sys$mprotect(Userspace addr, size_t size, int prot) @@ -265,117 +272,119 @@ ErrorOr Process::sys$mprotect(Userspace addr, size_t size, int p if (!is_user_range(range_to_mprotect)) return EFAULT; - if (auto* whole_region = address_space().find_region_from_range(range_to_mprotect)) { - if (!whole_region->is_mmap()) - return EPERM; - TRY(validate_mmap_prot(prot, whole_region->is_stack(), whole_region->vmobject().is_anonymous(), whole_region)); - if (whole_region->access() == Memory::prot_to_region_access_flags(prot)) - return 0; - if (whole_region->vmobject().is_inode()) - TRY(validate_inode_mmap_prot(prot, static_cast(whole_region->vmobject()).inode(), whole_region->is_shared())); - whole_region->set_readable(prot & PROT_READ); - whole_region->set_writable(prot & PROT_WRITE); - whole_region->set_executable(prot & PROT_EXEC); - - whole_region->remap(); - return 0; - } - - // Check if we can carve out the desired range from an existing region - if (auto* old_region = address_space().find_region_containing(range_to_mprotect)) { - if (!old_region->is_mmap()) - return EPERM; - TRY(validate_mmap_prot(prot, old_region->is_stack(), old_region->vmobject().is_anonymous(), old_region)); - if (old_region->access() == Memory::prot_to_region_access_flags(prot)) + return address_space().with([&](auto& space) -> ErrorOr { + if (auto* whole_region = space->find_region_from_range(range_to_mprotect)) { + if (!whole_region->is_mmap()) + return EPERM; + TRY(validate_mmap_prot(prot, whole_region->is_stack(), whole_region->vmobject().is_anonymous(), whole_region)); + if (whole_region->access() == Memory::prot_to_region_access_flags(prot)) + return 0; + if (whole_region->vmobject().is_inode()) + TRY(validate_inode_mmap_prot(prot, static_cast(whole_region->vmobject()).inode(), whole_region->is_shared())); + whole_region->set_readable(prot & PROT_READ); + whole_region->set_writable(prot & PROT_WRITE); + whole_region->set_executable(prot & PROT_EXEC); + + whole_region->remap(); return 0; - if (old_region->vmobject().is_inode()) - TRY(validate_inode_mmap_prot(prot, static_cast(old_region->vmobject()).inode(), old_region->is_shared())); - - // Remove the old region from our regions tree, since were going to add another region - // with the exact same start address. - auto region = address_space().take_region(*old_region); - region->unmap(); - - // This vector is the region(s) adjacent to our range. - // We need to allocate a new region for the range we wanted to change permission bits on. - auto adjacent_regions = TRY(address_space().try_split_region_around_range(*region, range_to_mprotect)); - - size_t new_range_offset_in_vmobject = region->offset_in_vmobject() + (range_to_mprotect.base().get() - region->range().base().get()); - auto* new_region = TRY(address_space().try_allocate_split_region(*region, range_to_mprotect, new_range_offset_in_vmobject)); - new_region->set_readable(prot & PROT_READ); - new_region->set_writable(prot & PROT_WRITE); - new_region->set_executable(prot & PROT_EXEC); - - // Map the new regions using our page directory (they were just allocated and don't have one). - for (auto* adjacent_region : adjacent_regions) { - TRY(adjacent_region->map(address_space().page_directory())); } - TRY(new_region->map(address_space().page_directory())); - return 0; - } - if (auto const& regions = TRY(address_space().find_regions_intersecting(range_to_mprotect)); regions.size()) { - size_t full_size_found = 0; - // Check that all intersecting regions are compatible. - for (auto const* region : regions) { - if (!region->is_mmap()) + // Check if we can carve out the desired range from an existing region + if (auto* old_region = space->find_region_containing(range_to_mprotect)) { + if (!old_region->is_mmap()) return EPERM; - TRY(validate_mmap_prot(prot, region->is_stack(), region->vmobject().is_anonymous(), region)); - if (region->vmobject().is_inode()) - TRY(validate_inode_mmap_prot(prot, static_cast(region->vmobject()).inode(), region->is_shared())); - full_size_found += region->range().intersect(range_to_mprotect).size(); - } - - if (full_size_found != range_to_mprotect.size()) - return ENOMEM; - - // Finally, iterate over each region, either updating its access flags if the range covers it wholly, - // or carving out a new subregion with the appropriate access flags set. - for (auto* old_region : regions) { + TRY(validate_mmap_prot(prot, old_region->is_stack(), old_region->vmobject().is_anonymous(), old_region)); if (old_region->access() == Memory::prot_to_region_access_flags(prot)) - continue; - - auto const intersection_to_mprotect = range_to_mprotect.intersect(old_region->range()); - // If the region is completely covered by range, simply update the access flags - if (intersection_to_mprotect == old_region->range()) { - old_region->set_readable(prot & PROT_READ); - old_region->set_writable(prot & PROT_WRITE); - old_region->set_executable(prot & PROT_EXEC); + return 0; + if (old_region->vmobject().is_inode()) + TRY(validate_inode_mmap_prot(prot, static_cast(old_region->vmobject()).inode(), old_region->is_shared())); - old_region->remap(); - continue; - } // Remove the old region from our regions tree, since were going to add another region // with the exact same start address. - auto region = address_space().take_region(*old_region); + auto region = space->take_region(*old_region); region->unmap(); // This vector is the region(s) adjacent to our range. // We need to allocate a new region for the range we wanted to change permission bits on. - auto adjacent_regions = TRY(address_space().try_split_region_around_range(*old_region, intersection_to_mprotect)); - - // Since the range is not contained in a single region, it can only partially cover its starting and ending region, - // therefore carving out a chunk from the region will always produce a single extra region, and not two. - VERIFY(adjacent_regions.size() == 1); - - size_t new_range_offset_in_vmobject = old_region->offset_in_vmobject() + (intersection_to_mprotect.base().get() - old_region->range().base().get()); - auto* new_region = TRY(address_space().try_allocate_split_region(*region, intersection_to_mprotect, new_range_offset_in_vmobject)); + auto adjacent_regions = TRY(space->try_split_region_around_range(*region, range_to_mprotect)); + size_t new_range_offset_in_vmobject = region->offset_in_vmobject() + (range_to_mprotect.base().get() - region->range().base().get()); + auto* new_region = TRY(space->try_allocate_split_region(*region, range_to_mprotect, new_range_offset_in_vmobject)); new_region->set_readable(prot & PROT_READ); new_region->set_writable(prot & PROT_WRITE); new_region->set_executable(prot & PROT_EXEC); - // Map the new region using our page directory (they were just allocated and don't have one) if any. - if (adjacent_regions.size()) - TRY(adjacent_regions[0]->map(address_space().page_directory())); - - TRY(new_region->map(address_space().page_directory())); + // Map the new regions using our page directory (they were just allocated and don't have one). + for (auto* adjacent_region : adjacent_regions) { + TRY(adjacent_region->map(space->page_directory())); + } + TRY(new_region->map(space->page_directory())); + return 0; } - return 0; - } + if (auto const& regions = TRY(space->find_regions_intersecting(range_to_mprotect)); regions.size()) { + size_t full_size_found = 0; + // Check that all intersecting regions are compatible. + for (auto const* region : regions) { + if (!region->is_mmap()) + return EPERM; + TRY(validate_mmap_prot(prot, region->is_stack(), region->vmobject().is_anonymous(), region)); + if (region->vmobject().is_inode()) + TRY(validate_inode_mmap_prot(prot, static_cast(region->vmobject()).inode(), region->is_shared())); + full_size_found += region->range().intersect(range_to_mprotect).size(); + } - return EINVAL; + if (full_size_found != range_to_mprotect.size()) + return ENOMEM; + + // Finally, iterate over each region, either updating its access flags if the range covers it wholly, + // or carving out a new subregion with the appropriate access flags set. + for (auto* old_region : regions) { + if (old_region->access() == Memory::prot_to_region_access_flags(prot)) + continue; + + auto const intersection_to_mprotect = range_to_mprotect.intersect(old_region->range()); + // If the region is completely covered by range, simply update the access flags + if (intersection_to_mprotect == old_region->range()) { + old_region->set_readable(prot & PROT_READ); + old_region->set_writable(prot & PROT_WRITE); + old_region->set_executable(prot & PROT_EXEC); + + old_region->remap(); + continue; + } + // Remove the old region from our regions tree, since were going to add another region + // with the exact same start address. + auto region = space->take_region(*old_region); + region->unmap(); + + // This vector is the region(s) adjacent to our range. + // We need to allocate a new region for the range we wanted to change permission bits on. + auto adjacent_regions = TRY(space->try_split_region_around_range(*old_region, intersection_to_mprotect)); + + // Since the range is not contained in a single region, it can only partially cover its starting and ending region, + // therefore carving out a chunk from the region will always produce a single extra region, and not two. + VERIFY(adjacent_regions.size() == 1); + + size_t new_range_offset_in_vmobject = old_region->offset_in_vmobject() + (intersection_to_mprotect.base().get() - old_region->range().base().get()); + auto* new_region = TRY(space->try_allocate_split_region(*region, intersection_to_mprotect, new_range_offset_in_vmobject)); + + new_region->set_readable(prot & PROT_READ); + new_region->set_writable(prot & PROT_WRITE); + new_region->set_executable(prot & PROT_EXEC); + + // Map the new region using our page directory (they were just allocated and don't have one) if any. + if (adjacent_regions.size()) + TRY(adjacent_regions[0]->map(space->page_directory())); + + TRY(new_region->map(space->page_directory())); + } + + return 0; + } + + return EINVAL; + }); } ErrorOr Process::sys$madvise(Userspace address, size_t size, int advice) @@ -391,22 +400,24 @@ ErrorOr Process::sys$madvise(Userspace address, size_t size, int if (!is_user_range(range_to_madvise)) return EFAULT; - auto* region = address_space().find_region_from_range(range_to_madvise); - if (!region) - return EINVAL; - if (!region->is_mmap()) - return EPERM; - if (advice == MADV_SET_VOLATILE || advice == MADV_SET_NONVOLATILE) { - if (!region->vmobject().is_anonymous()) - return EINVAL; - auto& vmobject = static_cast(region->vmobject()); - if (!vmobject.is_purgeable()) + return address_space().with([&](auto& space) -> ErrorOr { + auto* region = space->find_region_from_range(range_to_madvise); + if (!region) return EINVAL; - bool was_purged = false; - TRY(vmobject.set_volatile(advice == MADV_SET_VOLATILE, was_purged)); - return was_purged ? 1 : 0; - } - return EINVAL; + if (!region->is_mmap()) + return EPERM; + if (advice == MADV_SET_VOLATILE || advice == MADV_SET_NONVOLATILE) { + if (!region->vmobject().is_anonymous()) + return EINVAL; + auto& vmobject = static_cast(region->vmobject()); + if (!vmobject.is_purgeable()) + return EINVAL; + bool was_purged = false; + TRY(vmobject.set_volatile(advice == MADV_SET_VOLATILE, was_purged)); + return was_purged ? 1 : 0; + } + return EINVAL; + }); } ErrorOr Process::sys$set_mmap_name(Userspace user_params) @@ -421,23 +432,27 @@ ErrorOr Process::sys$set_mmap_name(Userspaceis_mmap()) - return EPERM; + return address_space().with([&](auto& space) -> ErrorOr { + auto* region = space->find_region_from_range(range); + if (!region) + return EINVAL; + if (!region->is_mmap()) + return EPERM; - region->set_name(move(name)); - PerformanceManager::add_mmap_perf_event(*this, *region); + region->set_name(move(name)); + PerformanceManager::add_mmap_perf_event(*this, *region); - return 0; + return 0; + }); } ErrorOr Process::sys$munmap(Userspace addr, size_t size) { VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); TRY(require_promise(Pledge::stdio)); - TRY(address_space().unmap_mmap_range(addr.vaddr(), size)); + TRY(address_space().with([&](auto& space) { + return space->unmap_mmap_range(addr.vaddr(), size); + })); return 0; } @@ -449,32 +464,34 @@ ErrorOr Process::sys$mremap(Userspace auto old_range = TRY(Memory::expand_range_to_page_boundaries((FlatPtr)params.old_address, params.old_size)); - auto* old_region = address_space().find_region_from_range(old_range); - if (!old_region) - return EINVAL; + return address_space().with([&](auto& space) -> ErrorOr { + auto* old_region = space->find_region_from_range(old_range); + if (!old_region) + return EINVAL; - if (!old_region->is_mmap()) - return EPERM; + if (!old_region->is_mmap()) + return EPERM; - if (old_region->vmobject().is_shared_inode() && params.flags & MAP_PRIVATE && !(params.flags & (MAP_ANONYMOUS | MAP_NORESERVE))) { - auto range = old_region->range(); - auto old_prot = region_access_flags_to_prot(old_region->access()); - auto old_offset = old_region->offset_in_vmobject(); - NonnullLockRefPtr inode = static_cast(old_region->vmobject()).inode(); + if (old_region->vmobject().is_shared_inode() && params.flags & MAP_PRIVATE && !(params.flags & (MAP_ANONYMOUS | MAP_NORESERVE))) { + auto range = old_region->range(); + auto old_prot = region_access_flags_to_prot(old_region->access()); + auto old_offset = old_region->offset_in_vmobject(); + NonnullLockRefPtr inode = static_cast(old_region->vmobject()).inode(); - auto new_vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(inode)); - auto old_name = old_region->take_name(); + auto new_vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(inode)); + auto old_name = old_region->take_name(); - old_region->unmap(); - address_space().deallocate_region(*old_region); + old_region->unmap(); + space->deallocate_region(*old_region); - auto* new_region = TRY(address_space().allocate_region_with_vmobject(range, move(new_vmobject), old_offset, old_name->view(), old_prot, false)); - new_region->set_mmap(true); - return new_region->vaddr().get(); - } + auto* new_region = TRY(space->allocate_region_with_vmobject(range, move(new_vmobject), old_offset, old_name->view(), old_prot, false)); + new_region->set_mmap(true); + return new_region->vaddr().get(); + } - dbgln("sys$mremap: Unimplemented remap request (flags={})", params.flags); - return ENOTIMPL; + dbgln("sys$mremap: Unimplemented remap request (flags={})", params.flags); + return ENOTIMPL; + }); } ErrorOr Process::sys$allocate_tls(Userspace initial_data, size_t size) @@ -504,56 +521,61 @@ ErrorOr Process::sys$allocate_tls(Userspace initial_data, if (multiple_threads) return EINVAL; - auto* region = TRY(address_space().allocate_region(Memory::RandomizeVirtualAddress::Yes, {}, size, PAGE_SIZE, "Master TLS"sv, PROT_READ | PROT_WRITE)); + return address_space().with([&](auto& space) -> ErrorOr { + auto* region = TRY(space->allocate_region(Memory::RandomizeVirtualAddress::Yes, {}, size, PAGE_SIZE, "Master TLS"sv, PROT_READ | PROT_WRITE)); - m_master_tls_region = TRY(region->try_make_weak_ptr()); - m_master_tls_size = size; - m_master_tls_alignment = PAGE_SIZE; + m_master_tls_region = TRY(region->try_make_weak_ptr()); + m_master_tls_size = size; + m_master_tls_alignment = PAGE_SIZE; - { - Kernel::SmapDisabler disabler; - void* fault_at; - if (!Kernel::safe_memcpy((char*)m_master_tls_region.unsafe_ptr()->vaddr().as_ptr(), (char*)initial_data.ptr(), size, fault_at)) - return EFAULT; - } + { + Kernel::SmapDisabler disabler; + void* fault_at; + if (!Kernel::safe_memcpy((char*)m_master_tls_region.unsafe_ptr()->vaddr().as_ptr(), (char*)initial_data.ptr(), size, fault_at)) + return EFAULT; + } - TRY(main_thread->make_thread_specific_region({})); + TRY(main_thread->make_thread_specific_region({})); #if ARCH(I386) - auto& tls_descriptor = Processor::current().get_gdt_entry(GDT_SELECTOR_TLS); - tls_descriptor.set_base(main_thread->thread_specific_data()); - tls_descriptor.set_limit(main_thread->thread_specific_region_size()); + auto& tls_descriptor = Processor::current().get_gdt_entry(GDT_SELECTOR_TLS); + tls_descriptor.set_base(main_thread->thread_specific_data()); + tls_descriptor.set_limit(main_thread->thread_specific_region_size()); #else - MSR fs_base_msr(MSR_FS_BASE); - fs_base_msr.set(main_thread->thread_specific_data().get()); + MSR fs_base_msr(MSR_FS_BASE); + fs_base_msr.set(main_thread->thread_specific_data().get()); #endif - return m_master_tls_region.unsafe_ptr()->vaddr().get(); + return m_master_tls_region.unsafe_ptr()->vaddr().get(); + }); } ErrorOr Process::sys$msyscall(Userspace address) { VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); - if (address_space().enforces_syscall_regions()) - return EPERM; - if (!address) { - address_space().set_enforces_syscall_regions(true); - return 0; - } + return address_space().with([&](auto& space) -> ErrorOr { + if (space->enforces_syscall_regions()) + return EPERM; - if (!Memory::is_user_address(address.vaddr())) - return EFAULT; + if (!address) { + space->set_enforces_syscall_regions(true); + return 0; + } - auto* region = address_space().find_region_containing(Memory::VirtualRange { address.vaddr(), 1 }); - if (!region) - return EINVAL; + if (!Memory::is_user_address(address.vaddr())) + return EFAULT; - if (!region->is_mmap()) - return EINVAL; + auto* region = space->find_region_containing(Memory::VirtualRange { address.vaddr(), 1 }); + if (!region) + return EINVAL; - region->set_syscall_region(true); - return 0; + if (!region->is_mmap()) + return EINVAL; + + region->set_syscall_region(true); + return 0; + }); } ErrorOr Process::sys$msync(Userspace address, size_t size, int flags) @@ -572,37 +594,39 @@ ErrorOr Process::sys$msync(Userspace address, size_t size, int f // Note: This is not specified auto rounded_size = TRY(Memory::page_round_up(size)); - auto regions = TRY(address_space().find_regions_intersecting(Memory::VirtualRange { address.vaddr(), rounded_size })); - // All regions from address upto address+size shall be mapped - if (regions.is_empty()) - return ENOMEM; + return address_space().with([&](auto& space) -> ErrorOr { + auto regions = TRY(space->find_regions_intersecting(Memory::VirtualRange { address.vaddr(), rounded_size })); + // All regions from address upto address+size shall be mapped + if (regions.is_empty()) + return ENOMEM; - size_t total_intersection_size = 0; - Memory::VirtualRange range_to_sync { address.vaddr(), rounded_size }; - for (auto const* region : regions) { - // Region was not mapped - if (!region->is_mmap()) + size_t total_intersection_size = 0; + Memory::VirtualRange range_to_sync { address.vaddr(), rounded_size }; + for (auto const* region : regions) { + // Region was not mapped + if (!region->is_mmap()) + return ENOMEM; + total_intersection_size += region->range().intersect(range_to_sync).size(); + } + // Part of the indicated range was not mapped + if (total_intersection_size != size) return ENOMEM; - total_intersection_size += region->range().intersect(range_to_sync).size(); - } - // Part of the indicated range was not mapped - if (total_intersection_size != size) - return ENOMEM; - - for (auto* region : regions) { - auto& vmobject = region->vmobject(); - if (!vmobject.is_shared_inode()) - continue; - - off_t offset = region->offset_in_vmobject() + address.ptr() - region->range().base().get(); - - auto& inode_vmobject = static_cast(vmobject); - // FIXME: If multiple regions belong to the same vmobject we might want to coalesce these writes - // FIXME: Handle MS_ASYNC - TRY(inode_vmobject.sync(offset / PAGE_SIZE, rounded_size / PAGE_SIZE)); - // FIXME: Handle MS_INVALIDATE - } - return 0; + + for (auto* region : regions) { + auto& vmobject = region->vmobject(); + if (!vmobject.is_shared_inode()) + continue; + + off_t offset = region->offset_in_vmobject() + address.ptr() - region->range().base().get(); + + auto& inode_vmobject = static_cast(vmobject); + // FIXME: If multiple regions belong to the same vmobject we might want to coalesce these writes + // FIXME: Handle MS_ASYNC + TRY(inode_vmobject.sync(offset / PAGE_SIZE, rounded_size / PAGE_SIZE)); + // FIXME: Handle MS_INVALIDATE + } + return 0; + }); } } diff --git a/Kernel/Syscalls/ptrace.cpp b/Kernel/Syscalls/ptrace.cpp index 3915b39c51..68842ac9d0 100644 --- a/Kernel/Syscalls/ptrace.cpp +++ b/Kernel/Syscalls/ptrace.cpp @@ -197,31 +197,34 @@ ErrorOr Process::peek_user_data(Span destination, Userspace ErrorOr Process::poke_user_data(Userspace address, FlatPtr data) { Memory::VirtualRange range = { address.vaddr(), sizeof(FlatPtr) }; - auto* region = address_space().find_region_containing(range); - if (!region) - return EFAULT; - ScopedAddressSpaceSwitcher switcher(*this); - if (region->is_shared()) { - // If the region is shared, we change its vmobject to a PrivateInodeVMObject - // to prevent the write operation from changing any shared inode data - VERIFY(region->vmobject().is_shared_inode()); - auto vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(static_cast(region->vmobject()).inode())); - region->set_vmobject(move(vmobject)); - region->set_shared(false); - } - bool const was_writable = region->is_writable(); - if (!was_writable) { - region->set_writable(true); - region->remap(); - } - ScopeGuard rollback([&]() { + + return address_space().with([&](auto& space) -> ErrorOr { + auto* region = space->find_region_containing(range); + if (!region) + return EFAULT; + ScopedAddressSpaceSwitcher switcher(*this); + if (region->is_shared()) { + // If the region is shared, we change its vmobject to a PrivateInodeVMObject + // to prevent the write operation from changing any shared inode data + VERIFY(region->vmobject().is_shared_inode()); + auto vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(static_cast(region->vmobject()).inode())); + region->set_vmobject(move(vmobject)); + region->set_shared(false); + } + bool const was_writable = region->is_writable(); if (!was_writable) { - region->set_writable(false); + region->set_writable(true); region->remap(); } + ScopeGuard rollback([&]() { + if (!was_writable) { + region->set_writable(false); + region->remap(); + } + }); + + return copy_to_user(address, &data); }); - - return copy_to_user(address, &data); } ErrorOr Thread::peek_debug_register(u32 register_index) diff --git a/Kernel/Syscalls/sigaction.cpp b/Kernel/Syscalls/sigaction.cpp index 7c4b9488bf..6417901c1a 100644 --- a/Kernel/Syscalls/sigaction.cpp +++ b/Kernel/Syscalls/sigaction.cpp @@ -134,100 +134,40 @@ ErrorOr Process::remap_range_as_stack(FlatPtr address, size_t size) if (!is_user_range(range_to_remap)) return EFAULT; - if (auto* whole_region = address_space().find_region_from_range(range_to_remap)) { - if (!whole_region->is_mmap()) - return EPERM; - if (!whole_region->vmobject().is_anonymous() || whole_region->is_shared()) - return EINVAL; - whole_region->unsafe_clear_access(); - whole_region->set_readable(true); - whole_region->set_writable(true); - whole_region->set_stack(true); - whole_region->set_syscall_region(false); - whole_region->clear_to_zero(); - whole_region->remap(); - - return {}; - } - - if (auto* old_region = address_space().find_region_containing(range_to_remap)) { - if (!old_region->is_mmap()) - return EPERM; - if (!old_region->vmobject().is_anonymous() || old_region->is_shared()) - return EINVAL; - - // Remove the old region from our regions tree, since were going to add another region - // with the exact same start address. - auto region = address_space().take_region(*old_region); - region->unmap(); - - // This vector is the region(s) adjacent to our range. - // We need to allocate a new region for the range we wanted to change permission bits on. - auto adjacent_regions = TRY(address_space().try_split_region_around_range(*region, range_to_remap)); - - size_t new_range_offset_in_vmobject = region->offset_in_vmobject() + (range_to_remap.base().get() - region->range().base().get()); - auto* new_region = TRY(address_space().try_allocate_split_region(*region, range_to_remap, new_range_offset_in_vmobject)); - new_region->unsafe_clear_access(); - new_region->set_readable(true); - new_region->set_writable(true); - new_region->set_stack(true); - new_region->set_syscall_region(false); - new_region->clear_to_zero(); - - // Map the new regions using our page directory (they were just allocated and don't have one). - for (auto* adjacent_region : adjacent_regions) { - TRY(adjacent_region->map(address_space().page_directory())); + return address_space().with([&](auto& space) -> ErrorOr { + if (auto* whole_region = space->find_region_from_range(range_to_remap)) { + if (!whole_region->is_mmap()) + return EPERM; + if (!whole_region->vmobject().is_anonymous() || whole_region->is_shared()) + return EINVAL; + whole_region->unsafe_clear_access(); + whole_region->set_readable(true); + whole_region->set_writable(true); + whole_region->set_stack(true); + whole_region->set_syscall_region(false); + whole_region->clear_to_zero(); + whole_region->remap(); + + return {}; } - TRY(new_region->map(address_space().page_directory())); - - return {}; - } - if (auto const& regions = TRY(address_space().find_regions_intersecting(range_to_remap)); regions.size()) { - size_t full_size_found = 0; - // Check that all intersecting regions are compatible. - for (auto const* region : regions) { - if (!region->is_mmap()) + if (auto* old_region = space->find_region_containing(range_to_remap)) { + if (!old_region->is_mmap()) return EPERM; - if (!region->vmobject().is_anonymous() || region->is_shared()) + if (!old_region->vmobject().is_anonymous() || old_region->is_shared()) return EINVAL; - full_size_found += region->range().intersect(range_to_remap).size(); - } - if (full_size_found != range_to_remap.size()) - return ENOMEM; - - // Finally, iterate over each region, either updating its access flags if the range covers it wholly, - // or carving out a new subregion with the appropriate access flags set. - for (auto* old_region : regions) { - auto const intersection_to_remap = range_to_remap.intersect(old_region->range()); - // If the region is completely covered by range, simply update the access flags - if (intersection_to_remap == old_region->range()) { - old_region->unsafe_clear_access(); - old_region->set_readable(true); - old_region->set_writable(true); - old_region->set_stack(true); - old_region->set_syscall_region(false); - old_region->clear_to_zero(); - old_region->remap(); - continue; - } // Remove the old region from our regions tree, since were going to add another region // with the exact same start address. - auto region = address_space().take_region(*old_region); + auto region = space->take_region(*old_region); region->unmap(); // This vector is the region(s) adjacent to our range. // We need to allocate a new region for the range we wanted to change permission bits on. - auto adjacent_regions = TRY(address_space().try_split_region_around_range(*old_region, intersection_to_remap)); - - // Since the range is not contained in a single region, it can only partially cover its starting and ending region, - // therefore carving out a chunk from the region will always produce a single extra region, and not two. - VERIFY(adjacent_regions.size() == 1); - - size_t new_range_offset_in_vmobject = old_region->offset_in_vmobject() + (intersection_to_remap.base().get() - old_region->range().base().get()); - auto* new_region = TRY(address_space().try_allocate_split_region(*region, intersection_to_remap, new_range_offset_in_vmobject)); + auto adjacent_regions = TRY(space->try_split_region_around_range(*region, range_to_remap)); + size_t new_range_offset_in_vmobject = region->offset_in_vmobject() + (range_to_remap.base().get() - region->range().base().get()); + auto* new_region = TRY(space->try_allocate_split_region(*region, range_to_remap, new_range_offset_in_vmobject)); new_region->unsafe_clear_access(); new_region->set_readable(true); new_region->set_writable(true); @@ -235,16 +175,78 @@ ErrorOr Process::remap_range_as_stack(FlatPtr address, size_t size) new_region->set_syscall_region(false); new_region->clear_to_zero(); - // Map the new region using our page directory (they were just allocated and don't have one) if any. - TRY(adjacent_regions[0]->map(address_space().page_directory())); + // Map the new regions using our page directory (they were just allocated and don't have one). + for (auto* adjacent_region : adjacent_regions) { + TRY(adjacent_region->map(space->page_directory())); + } + TRY(new_region->map(space->page_directory())); - TRY(new_region->map(address_space().page_directory())); + return {}; } - return {}; - } + if (auto const& regions = TRY(space->find_regions_intersecting(range_to_remap)); regions.size()) { + size_t full_size_found = 0; + // Check that all intersecting regions are compatible. + for (auto const* region : regions) { + if (!region->is_mmap()) + return EPERM; + if (!region->vmobject().is_anonymous() || region->is_shared()) + return EINVAL; + full_size_found += region->range().intersect(range_to_remap).size(); + } + + if (full_size_found != range_to_remap.size()) + return ENOMEM; - return EINVAL; + // Finally, iterate over each region, either updating its access flags if the range covers it wholly, + // or carving out a new subregion with the appropriate access flags set. + for (auto* old_region : regions) { + auto const intersection_to_remap = range_to_remap.intersect(old_region->range()); + // If the region is completely covered by range, simply update the access flags + if (intersection_to_remap == old_region->range()) { + old_region->unsafe_clear_access(); + old_region->set_readable(true); + old_region->set_writable(true); + old_region->set_stack(true); + old_region->set_syscall_region(false); + old_region->clear_to_zero(); + old_region->remap(); + continue; + } + // Remove the old region from our regions tree, since were going to add another region + // with the exact same start address. + auto region = space->take_region(*old_region); + region->unmap(); + + // This vector is the region(s) adjacent to our range. + // We need to allocate a new region for the range we wanted to change permission bits on. + auto adjacent_regions = TRY(space->try_split_region_around_range(*old_region, intersection_to_remap)); + + // Since the range is not contained in a single region, it can only partially cover its starting and ending region, + // therefore carving out a chunk from the region will always produce a single extra region, and not two. + VERIFY(adjacent_regions.size() == 1); + + size_t new_range_offset_in_vmobject = old_region->offset_in_vmobject() + (intersection_to_remap.base().get() - old_region->range().base().get()); + auto* new_region = TRY(space->try_allocate_split_region(*region, intersection_to_remap, new_range_offset_in_vmobject)); + + new_region->unsafe_clear_access(); + new_region->set_readable(true); + new_region->set_writable(true); + new_region->set_stack(true); + new_region->set_syscall_region(false); + new_region->clear_to_zero(); + + // Map the new region using our page directory (they were just allocated and don't have one) if any. + TRY(adjacent_regions[0]->map(space->page_directory())); + + TRY(new_region->map(space->page_directory())); + } + + return {}; + } + + return EINVAL; + }); } ErrorOr Process::sys$sigaltstack(Userspace user_ss, Userspace user_old_ss) diff --git a/Kernel/Syscalls/thread.cpp b/Kernel/Syscalls/thread.cpp index 8f6c6b0d81..27f06924da 100644 --- a/Kernel/Syscalls/thread.cpp +++ b/Kernel/Syscalls/thread.cpp @@ -27,8 +27,11 @@ ErrorOr Process::sys$create_thread(void* (*entry)(void*), Userspaceaddress_space(), VirtualAddress(user_sp.value() - 4))) - return EFAULT; + TRY(address_space().with([&](auto& space) -> ErrorOr { + if (!MM.validate_user_stack(*space, VirtualAddress(user_sp.value() - 4))) + return EFAULT; + return {}; + })); // FIXME: return EAGAIN if Thread::all_threads().size() is greater than PTHREAD_THREADS_MAX @@ -60,7 +63,7 @@ ErrorOr Process::sys$create_thread(void* (*entry)(void*), Userspacepage_directory().cr3(); }); TRY(thread->make_thread_specific_region({})); @@ -92,7 +95,9 @@ void Process::sys$exit_thread(Userspace exit_value, Userspace stac PerformanceManager::add_thread_exit_event(*current_thread); if (stack_location) { - auto unmap_result = address_space().unmap_mmap_range(stack_location.vaddr(), stack_size); + auto unmap_result = address_space().with([&](auto& space) { + return space->unmap_mmap_range(stack_location.vaddr(), stack_size); + }); if (unmap_result.is_error()) dbgln("Failed to unmap thread stack, terminating thread anyway. Error code: {}", unmap_result.error()); } diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index fc4ae0b549..ba6281755d 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -105,7 +105,7 @@ Thread::Thread(NonnullLockRefPtr process, NonnullOwnPtr # error Unknown architecture #endif - m_regs.cr3 = m_process->address_space().page_directory().cr3(); + m_regs.cr3 = m_process->address_space().with([](auto& space) { return space->page_directory().cr3(); }); m_kernel_stack_base = m_kernel_stack_region->vaddr().get(); m_kernel_stack_top = m_kernel_stack_region->vaddr().offset(default_kernel_stack_size).get() & ~(FlatPtr)0x7u; @@ -471,8 +471,10 @@ void Thread::exit(void* exit_value) u32 unlock_count; [[maybe_unused]] auto rc = unlock_process_if_locked(unlock_count); if (m_thread_specific_range.has_value()) { - auto* region = process().address_space().find_region_from_range(m_thread_specific_range.value()); - process().address_space().deallocate_region(*region); + process().address_space().with([&](auto& space) { + auto* region = space->find_region_from_range(m_thread_specific_range.value()); + space->deallocate_region(*region); + }); } #ifdef ENABLE_KERNEL_COVERAGE_COLLECTION KCOVDevice::free_thread(); @@ -1352,15 +1354,18 @@ static ErrorOr symbolicate(RecognizedSymbol const& symbol, Process& proces if (!Memory::is_user_address(VirtualAddress(symbol.address))) { TRY(builder.try_append("0xdeadc0de\n"sv)); } else { - if (auto* region = process.address_space().find_region_containing({ VirtualAddress(symbol.address), sizeof(FlatPtr) })) { - size_t offset = symbol.address - region->vaddr().get(); - if (auto region_name = region->name(); !region_name.is_null() && !region_name.is_empty()) - TRY(builder.try_appendff("{:p} {} + {:#x}\n", (void*)symbol.address, region_name, offset)); - else - TRY(builder.try_appendff("{:p} {:p} + {:#x}\n", (void*)symbol.address, region->vaddr().as_ptr(), offset)); - } else { - TRY(builder.try_appendff("{:p}\n", symbol.address)); - } + TRY(process.address_space().with([&](auto& space) -> ErrorOr { + if (auto* region = space->find_region_containing({ VirtualAddress(symbol.address), sizeof(FlatPtr) })) { + size_t offset = symbol.address - region->vaddr().get(); + if (auto region_name = region->name(); !region_name.is_null() && !region_name.is_empty()) + TRY(builder.try_appendff("{:p} {} + {:#x}\n", (void*)symbol.address, region_name, offset)); + else + TRY(builder.try_appendff("{:p} {:p} + {:#x}\n", (void*)symbol.address, region->vaddr().as_ptr(), offset)); + } else { + TRY(builder.try_appendff("{:p}\n", symbol.address)); + } + return {}; + })); } return true; } @@ -1412,20 +1417,22 @@ ErrorOr Thread::make_thread_specific_region(Badge) if (!process().m_master_tls_region) return {}; - auto* region = TRY(process().address_space().allocate_region(Memory::RandomizeVirtualAddress::Yes, {}, thread_specific_region_size(), PAGE_SIZE, "Thread-specific"sv, PROT_READ | PROT_WRITE)); + return process().address_space().with([&](auto& space) -> ErrorOr { + auto* region = TRY(space->allocate_region(Memory::RandomizeVirtualAddress::Yes, {}, thread_specific_region_size(), PAGE_SIZE, "Thread-specific"sv, PROT_READ | PROT_WRITE)); - m_thread_specific_range = region->range(); + m_thread_specific_range = region->range(); - SmapDisabler disabler; - auto* thread_specific_data = (ThreadSpecificData*)region->vaddr().offset(align_up_to(process().m_master_tls_size, thread_specific_region_alignment())).as_ptr(); - auto* thread_local_storage = (u8*)((u8*)thread_specific_data) - align_up_to(process().m_master_tls_size, process().m_master_tls_alignment); - m_thread_specific_data = VirtualAddress(thread_specific_data); - thread_specific_data->self = thread_specific_data; + SmapDisabler disabler; + auto* thread_specific_data = (ThreadSpecificData*)region->vaddr().offset(align_up_to(process().m_master_tls_size, thread_specific_region_alignment())).as_ptr(); + auto* thread_local_storage = (u8*)((u8*)thread_specific_data) - align_up_to(process().m_master_tls_size, process().m_master_tls_alignment); + m_thread_specific_data = VirtualAddress(thread_specific_data); + thread_specific_data->self = thread_specific_data; - if (process().m_master_tls_size != 0) - memcpy(thread_local_storage, process().m_master_tls_region.unsafe_ptr()->vaddr().as_ptr(), process().m_master_tls_size); + if (process().m_master_tls_size != 0) + memcpy(thread_local_storage, process().m_master_tls_region.unsafe_ptr()->vaddr().as_ptr(), process().m_master_tls_size); - return {}; + return {}; + }); } LockRefPtr Thread::from_tid(ThreadID tid) -- cgit v1.2.3