1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 13:38:11 +00:00

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.
This commit is contained in:
Andreas Kling 2022-08-23 17:58:05 +02:00
parent d6ef18f587
commit cf16b2c8e6
38 changed files with 708 additions and 627 deletions

View file

@ -35,7 +35,7 @@ void handle_crash(Kernel::RegisterState const& regs, char const* description, in
dump_registers(regs); dump_registers(regs);
if (crashed_in_kernel) { if (crashed_in_kernel) {
process.address_space().dump_regions(); process.address_space().with([&](auto& space) { space->dump_regions(); });
PANIC("Crash in ring 0"); PANIC("Crash in ring 0");
} }

View file

@ -303,9 +303,15 @@ void page_fault_handler(TrapFrame* trap)
}; };
VirtualAddress userspace_sp = VirtualAddress { regs.userspace_sp() }; 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); if (!faulted_in_kernel) {
return handle_crash(regs, "Bad stack on page fault", SIGSEGV); 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 } }; PageFault fault { regs.exception_code, VirtualAddress { fault_address } };

View file

@ -55,6 +55,7 @@ u32 RecursiveSpinlock::lock()
void RecursiveSpinlock::unlock(u32 prev_flags) void RecursiveSpinlock::unlock(u32 prev_flags)
{ {
VERIFY_INTERRUPTS_DISABLED();
VERIFY(m_recursions > 0); VERIFY(m_recursions > 0);
VERIFY(m_lock.load(AK::memory_order_relaxed) == FlatPtr(&Processor::current())); VERIFY(m_lock.load(AK::memory_order_relaxed) == FlatPtr(&Processor::current()));
if (--m_recursions == 0) { if (--m_recursions == 0) {

View file

@ -46,17 +46,19 @@ Coredump::Coredump(NonnullLockRefPtr<Process> process, NonnullLockRefPtr<OpenFil
, m_description(move(description)) , m_description(move(description))
{ {
m_num_program_headers = 0; m_num_program_headers = 0;
m_process->address_space().region_tree().with([&](auto& region_tree) { m_process->address_space().with([&](auto& space) {
for (auto& region : region_tree.regions()) { space->region_tree().with([&](auto& region_tree) {
for (auto& region : region_tree.regions()) {
#if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS #if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS
if (looks_like_userspace_heap_region(region)) if (looks_like_userspace_heap_region(region))
continue; continue;
#endif #endif
if (region.access() == Memory::Region::Access::None) if (region.access() == Memory::Region::Access::None)
continue; continue;
++m_num_program_headers; ++m_num_program_headers;
} }
});
}); });
++m_num_program_headers; // +1 for NOTE segment ++m_num_program_headers; // +1 for NOTE segment
} }
@ -135,38 +137,40 @@ ErrorOr<void> Coredump::write_elf_header()
ErrorOr<void> Coredump::write_program_headers(size_t notes_size) ErrorOr<void> Coredump::write_program_headers(size_t notes_size)
{ {
size_t offset = sizeof(ElfW(Ehdr)) + m_num_program_headers * sizeof(ElfW(Phdr)); size_t offset = sizeof(ElfW(Ehdr)) + m_num_program_headers * sizeof(ElfW(Phdr));
m_process->address_space().region_tree().with([&](auto& region_tree) { m_process->address_space().with([&](auto& space) {
for (auto& region : region_tree.regions()) { space->region_tree().with([&](auto& region_tree) {
for (auto& region : region_tree.regions()) {
#if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS #if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS
if (looks_like_userspace_heap_region(region)) if (looks_like_userspace_heap_region(region))
continue; continue;
#endif #endif
if (region.access() == Memory::Region::Access::None) if (region.access() == Memory::Region::Access::None)
continue; continue;
ElfW(Phdr) phdr {}; ElfW(Phdr) phdr {};
phdr.p_type = PT_LOAD; phdr.p_type = PT_LOAD;
phdr.p_offset = offset; phdr.p_offset = offset;
phdr.p_vaddr = region.vaddr().get(); phdr.p_vaddr = region.vaddr().get();
phdr.p_paddr = 0; phdr.p_paddr = 0;
phdr.p_filesz = region.page_count() * PAGE_SIZE; phdr.p_filesz = region.page_count() * PAGE_SIZE;
phdr.p_memsz = region.page_count() * PAGE_SIZE; phdr.p_memsz = region.page_count() * PAGE_SIZE;
phdr.p_align = 0; phdr.p_align = 0;
phdr.p_flags = region.is_readable() ? PF_R : 0; phdr.p_flags = region.is_readable() ? PF_R : 0;
if (region.is_writable()) if (region.is_writable())
phdr.p_flags |= PF_W; phdr.p_flags |= PF_W;
if (region.is_executable()) if (region.is_executable())
phdr.p_flags |= PF_X; 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<uint8_t*>(&phdr)), sizeof(ElfW(Phdr))); [[maybe_unused]] auto rc = m_description->write(UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast<uint8_t*>(&phdr)), sizeof(ElfW(Phdr)));
} }
});
}); });
ElfW(Phdr) notes_pheader {}; ElfW(Phdr) notes_pheader {};
@ -188,37 +192,39 @@ ErrorOr<void> Coredump::write_regions()
{ {
u8 zero_buffer[PAGE_SIZE] = {}; u8 zero_buffer[PAGE_SIZE] = {};
return m_process->address_space().region_tree().with([&](auto& region_tree) -> ErrorOr<void> { return m_process->address_space().with([&](auto& space) {
for (auto& region : region_tree.regions()) { return space->region_tree().with([&](auto& region_tree) -> ErrorOr<void> {
VERIFY(!region.is_kernel()); for (auto& region : region_tree.regions()) {
VERIFY(!region.is_kernel());
#if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS #if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS
if (looks_like_userspace_heap_region(region)) if (looks_like_userspace_heap_region(region))
continue; continue;
#endif #endif
if (region.access() == Memory::Region::Access::None) if (region.access() == Memory::Region::Access::None)
continue; 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 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()) if (!region.is_mapped())
continue; continue;
region.set_readable(true); region.set_readable(true);
region.remap(); region.remap();
for (size_t i = 0; i < region.page_count(); i++) { for (size_t i = 0; i < region.page_count(); i++) {
auto page = region.physical_page(i); auto page = region.physical_page(i);
auto src_buffer = [&]() -> ErrorOr<UserOrKernelBuffer> { auto src_buffer = [&]() -> ErrorOr<UserOrKernelBuffer> {
if (page) if (page)
return UserOrKernelBuffer::for_user_buffer(reinterpret_cast<uint8_t*>((region.vaddr().as_ptr() + (i * PAGE_SIZE))), PAGE_SIZE); return UserOrKernelBuffer::for_user_buffer(reinterpret_cast<uint8_t*>((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. // 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); return UserOrKernelBuffer::for_kernel_buffer(zero_buffer);
}(); }();
TRY(m_description->write(src_buffer.value(), PAGE_SIZE)); TRY(m_description->write(src_buffer.value(), PAGE_SIZE));
}
} }
} return {};
return {}; });
}); });
} }
@ -279,34 +285,36 @@ ErrorOr<void> Coredump::create_notes_threads_data(auto& builder) const
ErrorOr<void> Coredump::create_notes_regions_data(auto& builder) const ErrorOr<void> Coredump::create_notes_regions_data(auto& builder) const
{ {
size_t region_index = 0; size_t region_index = 0;
return m_process->address_space().region_tree().with([&](auto& region_tree) -> ErrorOr<void> { return m_process->address_space().with([&](auto& space) {
for (auto const& region : region_tree.regions()) { return space->region_tree().with([&](auto& region_tree) -> ErrorOr<void> {
for (auto const& region : region_tree.regions()) {
#if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS #if !INCLUDE_USERSPACE_HEAP_MEMORY_IN_COREDUMPS
if (looks_like_userspace_heap_region(region)) if (looks_like_userspace_heap_region(region))
continue; continue;
#endif #endif
if (region.access() == Memory::Region::Access::None) if (region.access() == Memory::Region::Access::None)
continue; continue;
ELF::Core::MemoryRegionInfo info {}; ELF::Core::MemoryRegionInfo info {};
info.header.type = ELF::Core::NotesEntryHeader::Type::MemoryRegionInfo; info.header.type = ELF::Core::NotesEntryHeader::Type::MemoryRegionInfo;
info.region_start = region.vaddr().get(); info.region_start = region.vaddr().get();
info.region_end = region.vaddr().offset(region.size()).get(); info.region_end = region.vaddr().offset(region.size()).get();
info.program_header_index = region_index++; 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: // NOTE: The region name *is* null-terminated, so the following is ok:
auto name = region.name(); auto name = region.name();
if (name.is_empty()) if (name.is_empty())
TRY(builder.append('\0')); TRY(builder.append('\0'));
else else
TRY(builder.append(name.characters_without_null_termination(), name.length() + 1)); TRY(builder.append(name.characters_without_null_termination(), name.length() + 1));
} }
return {}; return {};
});
}); });
} }
@ -344,7 +352,6 @@ ErrorOr<void> Coredump::create_notes_segment_data(auto& builder) const
ErrorOr<void> Coredump::write() ErrorOr<void> Coredump::write()
{ {
SpinlockLocker lock(m_process->address_space().get_lock());
ScopedAddressSpaceSwitcher switcher(m_process); ScopedAddressSpaceSwitcher switcher(m_process);
auto builder = TRY(KBufferBuilder::try_create()); auto builder = TRY(KBufferBuilder::try_create());

View file

@ -116,7 +116,7 @@ ErrorOr<void> KCOVDevice::ioctl(OpenFileDescription&, unsigned request, Userspac
} }
} }
ErrorOr<Memory::Region*> KCOVDevice::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) ErrorOr<Memory::Region*> KCOVDevice::mmap(Process& process, Memory::AddressSpace& address_space, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared)
{ {
auto pid = process.pid(); auto pid = process.pid();
auto maybe_kcov_instance = proc_instance->get(pid); auto maybe_kcov_instance = proc_instance->get(pid);
@ -126,8 +126,7 @@ ErrorOr<Memory::Region*> KCOVDevice::mmap(Process& process, OpenFileDescription&
if (!kcov_instance->vmobject()) if (!kcov_instance->vmobject())
return ENOBUFS; // mmaped, before KCOV_SETBUFSIZE return ENOBUFS; // mmaped, before KCOV_SETBUFSIZE
return process.address_space().allocate_region_with_vmobject( return address_space.allocate_region_with_vmobject(range, *kcov_instance->vmobject(), offset, {}, prot, shared);
range, *kcov_instance->vmobject(), offset, {}, prot, shared);
} }
} }

View file

@ -22,7 +22,7 @@ public:
static void free_process(); static void free_process();
// ^File // ^File
ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; ErrorOr<Memory::Region*> mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override;
ErrorOr<NonnullLockRefPtr<OpenFileDescription>> open(int options) override; ErrorOr<NonnullLockRefPtr<OpenFileDescription>> open(int options) override;
protected: protected:

View file

@ -42,7 +42,7 @@ ErrorOr<size_t> MemoryDevice::read(OpenFileDescription&, u64 offset, UserOrKerne
return length; return length;
} }
ErrorOr<Memory::Region*> MemoryDevice::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) ErrorOr<Memory::Region*> MemoryDevice::mmap(Process&, Memory::AddressSpace& address_space, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared)
{ {
auto viewed_address = PhysicalAddress(offset); auto viewed_address = PhysicalAddress(offset);
@ -63,7 +63,7 @@ ErrorOr<Memory::Region*> MemoryDevice::mmap(Process& process, OpenFileDescriptio
auto vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(viewed_address, range.size())); 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, range,
move(vmobject), move(vmobject),
0, 0,

View file

@ -19,7 +19,7 @@ public:
static NonnullLockRefPtr<MemoryDevice> must_create(); static NonnullLockRefPtr<MemoryDevice> must_create();
~MemoryDevice(); ~MemoryDevice();
virtual ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; virtual ErrorOr<Memory::Region*> mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override;
private: private:
MemoryDevice(); MemoryDevice();

View file

@ -17,12 +17,12 @@ AnonymousFile::AnonymousFile(NonnullLockRefPtr<Memory::AnonymousVMObject> vmobje
AnonymousFile::~AnonymousFile() = default; AnonymousFile::~AnonymousFile() = default;
ErrorOr<Memory::Region*> AnonymousFile::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) ErrorOr<Memory::Region*> AnonymousFile::mmap(Process&, Memory::AddressSpace& address_space, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared)
{ {
if (offset != 0) if (offset != 0)
return EINVAL; 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<NonnullOwnPtr<KString>> AnonymousFile::pseudo_path(OpenFileDescription const&) const ErrorOr<NonnullOwnPtr<KString>> AnonymousFile::pseudo_path(OpenFileDescription const&) const

View file

@ -20,7 +20,7 @@ public:
virtual ~AnonymousFile() override; virtual ~AnonymousFile() override;
virtual ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; virtual ErrorOr<Memory::Region*> mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override;
private: private:
virtual StringView class_name() const override { return "AnonymousFile"sv; } virtual StringView class_name() const override { return "AnonymousFile"sv; }

View file

@ -35,7 +35,7 @@ ErrorOr<void> File::ioctl(OpenFileDescription&, unsigned, Userspace<void*>)
return ENOTTY; return ENOTTY;
} }
ErrorOr<Memory::Region*> File::mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) ErrorOr<Memory::Region*> File::mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool)
{ {
return ENODEV; return ENODEV;
} }

View file

@ -90,7 +90,7 @@ public:
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) = 0; virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) = 0;
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) = 0; virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) = 0;
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg); virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg);
virtual ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared); virtual ErrorOr<Memory::Region*> mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared);
virtual ErrorOr<struct stat> stat() const { return EBADF; } virtual ErrorOr<struct stat> stat() const { return EBADF; }
// Although this might be better described "name" or "description", these terms already have other meanings. // Although this might be better described "name" or "description", these terms already have other meanings.

View file

@ -85,7 +85,7 @@ ErrorOr<void> InodeFile::ioctl(OpenFileDescription& description, unsigned reques
} }
} }
ErrorOr<Memory::Region*> InodeFile::mmap(Process& process, OpenFileDescription& description, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) ErrorOr<Memory::Region*> 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. // FIXME: If PROT_EXEC, check that the underlying file system isn't mounted noexec.
LockRefPtr<Memory::InodeVMObject> vmobject; LockRefPtr<Memory::InodeVMObject> vmobject;
@ -94,7 +94,7 @@ ErrorOr<Memory::Region*> InodeFile::mmap(Process& process, OpenFileDescription&
else else
vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(inode())); vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(inode()));
auto path = TRY(description.pseudo_path()); 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<NonnullOwnPtr<KString>> InodeFile::pseudo_path(OpenFileDescription const&) const ErrorOr<NonnullOwnPtr<KString>> InodeFile::pseudo_path(OpenFileDescription const&) const

View file

@ -33,7 +33,7 @@ public:
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override;
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override; virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override;
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override; virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override;
virtual ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; virtual ErrorOr<Memory::Region*> mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override;
virtual ErrorOr<struct stat> stat() const override { return inode().metadata().stat(); } virtual ErrorOr<struct stat> stat() const override { return inode().metadata().stat(); }
virtual ErrorOr<NonnullOwnPtr<KString>> pseudo_path(OpenFileDescription const&) const override; virtual ErrorOr<NonnullOwnPtr<KString>> pseudo_path(OpenFileDescription const&) const override;

View file

@ -374,9 +374,9 @@ InodeMetadata OpenFileDescription::metadata() const
return {}; return {};
} }
ErrorOr<Memory::Region*> OpenFileDescription::mmap(Process& process, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) ErrorOr<Memory::Region*> 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<void> OpenFileDescription::truncate(u64 length) ErrorOr<void> OpenFileDescription::truncate(u64 length)

View file

@ -92,7 +92,7 @@ public:
RefPtr<Custody> custody(); RefPtr<Custody> custody();
RefPtr<Custody const> custody() const; RefPtr<Custody const> custody() const;
ErrorOr<Memory::Region*> mmap(Process&, Memory::VirtualRange const&, u64 offset, int prot, bool shared); ErrorOr<Memory::Region*> mmap(Process&, Memory::AddressSpace&, Memory::VirtualRange const&, u64 offset, int prot, bool shared);
bool is_blocking() const; bool is_blocking() const;
void set_blocking(bool b); void set_blocking(bool b);

View file

@ -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("nfds"sv, process.fds().with_shared([](auto& fds) { return fds.open_count(); })));
TRY(process_object.add("name"sv, process.name())); 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("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())); size_t amount_virtual = 0;
TRY(process_object.add("amount_dirty_private"sv, process.address_space().amount_dirty_private())); size_t amount_resident = 0;
TRY(process_object.add("amount_clean_inode"sv, TRY(process.address_space().amount_clean_inode()))); size_t amount_dirty_private = 0;
TRY(process_object.add("amount_shared"sv, process.address_space().amount_shared())); size_t amount_clean_inode = 0;
TRY(process_object.add("amount_purgeable_volatile"sv, process.address_space().amount_purgeable_volatile())); size_t amount_shared = 0;
TRY(process_object.add("amount_purgeable_nonvolatile"sv, process.address_space().amount_purgeable_nonvolatile())); size_t amount_purgeable_volatile = 0;
size_t amount_purgeable_nonvolatile = 0;
TRY(process.address_space().with([&](auto& space) -> ErrorOr<void> {
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("dumpable"sv, process.is_dumpable()));
TRY(process_object.add("kernel"sv, process.is_kernel_process())); TRY(process_object.add("kernel"sv, process.is_kernel_process()));
auto thread_array = TRY(process_object.add_array("threads"sv)); auto thread_array = TRY(process_object.add_array("threads"sv));

View file

@ -32,13 +32,13 @@ DisplayConnector::DisplayConnector(size_t framebuffer_resource_size, bool enable
{ {
} }
ErrorOr<Memory::Region*> DisplayConnector::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) ErrorOr<Memory::Region*> DisplayConnector::mmap(Process&, Memory::AddressSpace& address_space, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared)
{ {
VERIFY(m_shared_framebuffer_vmobject); VERIFY(m_shared_framebuffer_vmobject);
if (offset != 0) if (offset != 0)
return Error::from_errno(ENOTSUP); return Error::from_errno(ENOTSUP);
return process.address_space().allocate_region_with_vmobject( return address_space.allocate_region_with_vmobject(
range, range,
*m_shared_framebuffer_vmobject, *m_shared_framebuffer_vmobject,
0, 0,

View file

@ -137,7 +137,7 @@ private:
virtual bool can_write(OpenFileDescription const&, u64) const final override { return true; } virtual bool can_write(OpenFileDescription const&, u64) const final override { return true; }
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override final; virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override final;
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override final; virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override final;
virtual ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) override final; virtual ErrorOr<Memory::Region*> mmap(Process&, Memory::AddressSpace&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) override final;
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override final; virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override final;
virtual StringView class_name() const override final { return "DisplayConnector"sv; } virtual StringView class_name() const override final { return "DisplayConnector"sv; }

View file

@ -53,8 +53,6 @@ public:
void remove_all_regions(Badge<Process>); void remove_all_regions(Badge<Process>);
RecursiveSpinlock& get_lock() const { return m_lock; }
ErrorOr<size_t> amount_clean_inode() const; ErrorOr<size_t> amount_clean_inode() const;
size_t amount_dirty_private() const; size_t amount_dirty_private() const;
size_t amount_virtual() const; size_t amount_virtual() const;
@ -66,8 +64,6 @@ public:
private: private:
AddressSpace(NonnullLockRefPtr<PageDirectory>, VirtualRange total_range); AddressSpace(NonnullLockRefPtr<PageDirectory>, VirtualRange total_range);
mutable RecursiveSpinlock m_lock { LockRank::None };
LockRefPtr<PageDirectory> m_page_directory; LockRefPtr<PageDirectory> m_page_directory;
// NOTE: The total range is also in the RegionTree, but since it never changes, // NOTE: The total range is also in the RegionTree, but since it never changes,

View file

@ -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); }); 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) Region* MemoryManager::find_user_region_from_vaddr(AddressSpace& space, VirtualAddress vaddr)
{ {
VERIFY(space.get_lock().is_locked_by_current_processor());
return space.find_region_containing({ vaddr, 1 }); return space.find_region_containing({ vaddr, 1 });
} }
Region* MemoryManager::find_user_region_from_vaddr(AddressSpace& space, VirtualAddress vaddr) void MemoryManager::validate_syscall_preconditions(Process& process, RegisterState const& regs)
{ {
SpinlockLocker lock(space.get_lock()); bool should_crash = false;
return find_user_region_from_vaddr_no_lock(space, vaddr); char const* crash_description = nullptr;
} int crash_signal = 0;
void MemoryManager::validate_syscall_preconditions(AddressSpace& space, RegisterState const& regs) auto unlock_and_handle_crash = [&](char const* description, int signal) {
{ should_crash = true;
// We take the space lock once here and then use the no_lock variants crash_description = description;
// to avoid excessive spinlock recursion in this extremely common path. crash_signal = signal;
SpinlockLocker lock(space.get_lock());
auto unlock_and_handle_crash = [&lock, &regs](char const* description, int signal) {
lock.unlock();
handle_crash(regs, description, signal);
}; };
{ process.address_space().with([&](auto& space) -> void {
VirtualAddress userspace_sp = VirtualAddress { regs.userspace_sp() }; 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); dbgln("Invalid stack pointer: {}", userspace_sp);
return unlock_and_handle_crash("Bad stack on syscall entry", SIGSEGV); return unlock_and_handle_crash("Bad stack on syscall entry", SIGSEGV);
} }
}
{
VirtualAddress ip = VirtualAddress { regs.ip() }; 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) { if (!calling_region) {
dbgln("Syscall from {:p} which has no associated region", ip); dbgln("Syscall from {:p} which has no associated region", ip);
return unlock_and_handle_crash("Syscall from unknown region", SIGSEGV); 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); 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"); dbgln("Syscall from non-syscall region");
return unlock_and_handle_crash("Syscall from non-syscall region", SIGSEGV); 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<CommittedPhysicalPageSet> MemoryManager::commit_physical_pages(size_t pa
dbgln("MM: Unable to commit {} pages, have only {}", page_count, m_system_memory_info.physical_pages_uncommitted); dbgln("MM: Unable to commit {} pages, have only {}", page_count, m_system_memory_info.physical_pages_uncommitted);
Process::for_each([&](Process const& process) { 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:{}", dbgln("{}({}) resident:{}, shared:{}, virtual:{}",
process.name(), process.name(),
process.pid(), process.pid(),
process.address_space().amount_resident() / PAGE_SIZE, amount_resident / PAGE_SIZE,
process.address_space().amount_shared() / PAGE_SIZE, amount_shared / PAGE_SIZE,
process.address_space().amount_virtual() / PAGE_SIZE); amount_virtual / PAGE_SIZE);
return IterationDecision::Continue; return IterationDecision::Continue;
}); });
@ -1007,7 +1011,9 @@ ErrorOr<NonnullLockRefPtrVector<PhysicalPage>> MemoryManager::allocate_contiguou
void MemoryManager::enter_process_address_space(Process& process) 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) 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); 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)) if (!is_user_address(vaddr))
return false; 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(); 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) void MemoryManager::unregister_kernel_region(Region& region)
{ {
VERIFY(region.is_kernel()); VERIFY(region.is_kernel());

View file

@ -159,7 +159,6 @@ public:
static void enter_process_address_space(Process&); static void enter_process_address_space(Process&);
static void enter_address_space(AddressSpace&); static void enter_address_space(AddressSpace&);
bool validate_user_stack_no_lock(AddressSpace&, VirtualAddress) const;
bool validate_user_stack(AddressSpace&, VirtualAddress) const; bool validate_user_stack(AddressSpace&, VirtualAddress) const;
enum class ShouldZeroFill { enum class ShouldZeroFill {
@ -222,8 +221,7 @@ public:
} }
static Region* find_user_region_from_vaddr(AddressSpace&, VirtualAddress); 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(Process&, RegisterState const&);
static void validate_syscall_preconditions(AddressSpace&, RegisterState const&);
void dump_kernel_regions(); void dump_kernel_regions();

View file

@ -45,9 +45,6 @@ public:
void delete_all_regions_assuming_they_are_unmapped(); 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&); bool remove(Region&);
Region* find_region_containing(VirtualAddress); Region* find_region_containing(VirtualAddress);
@ -58,9 +55,6 @@ private:
ErrorOr<VirtualRange> allocate_range_specific(VirtualAddress base, size_t size); ErrorOr<VirtualRange> allocate_range_specific(VirtualAddress base, size_t size);
ErrorOr<VirtualRange> allocate_range_randomized(size_t size, size_t alignment = PAGE_SIZE); ErrorOr<VirtualRange> 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; IntrusiveRedBlackTree<&Region::m_tree_node> m_regions;
VirtualRange const m_total_range; VirtualRange const m_total_range;
}; };

View file

@ -334,8 +334,6 @@ OwnPtr<PerformanceEventBuffer> PerformanceEventBuffer::try_create_with_size(size
ErrorOr<void> PerformanceEventBuffer::add_process(Process const& process, ProcessEventType event_type) ErrorOr<void> PerformanceEventBuffer::add_process(Process const& process, ProcessEventType event_type)
{ {
SpinlockLocker locker(process.address_space().get_lock());
OwnPtr<KString> executable; OwnPtr<KString> executable;
if (process.executable()) if (process.executable())
executable = TRY(process.executable()->try_serialize_absolute_path()); executable = TRY(process.executable()->try_serialize_absolute_path());
@ -354,12 +352,14 @@ ErrorOr<void> PerformanceEventBuffer::add_process(Process const& process, Proces
}); });
TRY(result); TRY(result);
return process.address_space().region_tree().with([&](auto& region_tree) -> ErrorOr<void> { return process.address_space().with([&](auto& space) {
for (auto const& region : region_tree.regions()) { return space->region_tree().with([&](auto& region_tree) -> ErrorOr<void> {
TRY(append_with_ip_and_bp(process.pid(), 0, for (auto const& region : region_tree.regions()) {
0, 0, PERF_EVENT_MMAP, 0, region.range().base().get(), region.range().size(), region.name())); 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 {};
});
}); });
} }

View file

@ -218,16 +218,25 @@ void Process::unprotect_data()
ErrorOr<NonnullLockRefPtr<Process>> Process::try_create(LockRefPtr<Thread>& first_thread, NonnullOwnPtr<KString> name, UserID uid, GroupID gid, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, TTY* tty, Process* fork_parent) ErrorOr<NonnullLockRefPtr<Process>> Process::try_create(LockRefPtr<Thread>& first_thread, NonnullOwnPtr<KString> name, UserID uid, GroupID gid, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, TTY* tty, Process* fork_parent)
{ {
auto space = TRY(Memory::AddressSpace::try_create(fork_parent ? &fork_parent->address_space() : nullptr)); OwnPtr<Memory::AddressSpace> new_address_space;
if (fork_parent) {
TRY(fork_parent->address_space().with([&](auto& parent_address_space) -> ErrorOr<void> {
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 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 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)))); 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; return process;
} }
Process::Process(NonnullOwnPtr<KString> name, NonnullRefPtr<Credentials> credentials, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, TTY* tty, UnveilNode unveil_tree) Process::Process(NonnullOwnPtr<KString> name, NonnullRefPtr<Credentials> credentials, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, TTY* tty, UnveilNode unveil_tree)
: m_name(move(name)) : m_name(move(name))
, m_space(LockRank::None)
, m_protected_data_lock(LockRank::None) , m_protected_data_lock(LockRank::None)
, m_is_kernel_process(is_kernel_process) , m_is_kernel_process(is_kernel_process)
, m_executable(LockRank::None, move(executable)) , m_executable(LockRank::None, move(executable))
@ -248,7 +257,9 @@ Process::Process(NonnullOwnPtr<KString> name, NonnullRefPtr<Credentials> credent
ErrorOr<void> Process::attach_resources(NonnullOwnPtr<Memory::AddressSpace>&& preallocated_space, LockRefPtr<Thread>& first_thread, Process* fork_parent) ErrorOr<void> Process::attach_resources(NonnullOwnPtr<Memory::AddressSpace>&& preallocated_space, LockRefPtr<Thread>& first_thread, Process* fork_parent)
{ {
m_space = move(preallocated_space); m_space.with([&](auto& space) {
space = move(preallocated_space);
});
auto create_first_thread = [&] { auto create_first_thread = [&] {
if (fork_parent) { if (fork_parent) {
@ -401,7 +412,7 @@ void Process::crash(int signal, FlatPtr ip, bool out_of_memory)
protected_data.termination_signal = signal; protected_data.termination_signal = signal;
}); });
set_should_generate_coredump(!out_of_memory); set_should_generate_coredump(!out_of_memory);
address_space().dump_regions(); address_space().with([](auto& space) { space->dump_regions(); });
VERIFY(is_user_process()); VERIFY(is_user_process());
die(); die();
// We can not return from here, as there is nowhere // We can not return from here, as there is nowhere
@ -662,7 +673,7 @@ void Process::finalize()
unblock_waiters(Thread::WaitBlocker::UnblockFlags::Terminated); unblock_waiters(Thread::WaitBlocker::UnblockFlags::Terminated);
m_space->remove_all_regions({}); m_space.with([](auto& space) { space->remove_all_regions({}); });
VERIFY(ref_count() > 0); VERIFY(ref_count() > 0);
// WaitBlockerSet::finalize will be in charge of dropping the last // WaitBlockerSet::finalize will be in charge of dropping the last

View file

@ -558,8 +558,8 @@ public:
PerformanceEventBuffer* perf_events() { return m_perf_event_buffer; } PerformanceEventBuffer* perf_events() { return m_perf_event_buffer; }
PerformanceEventBuffer const* perf_events() const { return m_perf_event_buffer; } PerformanceEventBuffer const* perf_events() const { return m_perf_event_buffer; }
Memory::AddressSpace& address_space() { return *m_space; } SpinlockProtected<OwnPtr<Memory::AddressSpace>>& address_space() { return m_space; }
Memory::AddressSpace const& address_space() const { return *m_space; } SpinlockProtected<OwnPtr<Memory::AddressSpace>> const& address_space() const { return m_space; }
VirtualAddress signal_trampoline() const VirtualAddress signal_trampoline() const
{ {
@ -656,7 +656,7 @@ private:
NonnullOwnPtr<KString> m_name; NonnullOwnPtr<KString> m_name;
OwnPtr<Memory::AddressSpace> m_space; SpinlockProtected<OwnPtr<Memory::AddressSpace>> m_space;
LockRefPtr<ProcessGroup> m_pg; LockRefPtr<ProcessGroup> m_pg;

View file

@ -267,45 +267,47 @@ ErrorOr<void> Process::procfs_get_fds_stats(KBufferBuilder& builder) const
ErrorOr<void> Process::procfs_get_virtual_memory_stats(KBufferBuilder& builder) const ErrorOr<void> Process::procfs_get_virtual_memory_stats(KBufferBuilder& builder) const
{ {
auto array = TRY(JsonArraySerializer<>::try_create(builder)); auto array = TRY(JsonArraySerializer<>::try_create(builder));
TRY(address_space().region_tree().with([&](auto& region_tree) -> ErrorOr<void> { TRY(address_space().with([&](auto& space) {
for (auto const& region : region_tree.regions()) { return space->region_tree().with([&](auto& region_tree) -> ErrorOr<void> {
auto current_process_credentials = Process::current().credentials(); for (auto const& region : region_tree.regions()) {
if (!region.is_user() && !current_process_credentials->is_superuser()) auto current_process_credentials = Process::current().credentials();
continue; if (!region.is_user() && !current_process_credentials->is_superuser())
auto region_object = TRY(array.add_object()); continue;
TRY(region_object.add("readable"sv, region.is_readable())); auto region_object = TRY(array.add_object());
TRY(region_object.add("writable"sv, region.is_writable())); TRY(region_object.add("readable"sv, region.is_readable()));
TRY(region_object.add("executable"sv, region.is_executable())); TRY(region_object.add("writable"sv, region.is_writable()));
TRY(region_object.add("stack"sv, region.is_stack())); TRY(region_object.add("executable"sv, region.is_executable()));
TRY(region_object.add("shared"sv, region.is_shared())); TRY(region_object.add("stack"sv, region.is_stack()));
TRY(region_object.add("syscall"sv, region.is_syscall_region())); TRY(region_object.add("shared"sv, region.is_shared()));
TRY(region_object.add("purgeable"sv, region.vmobject().is_anonymous())); TRY(region_object.add("syscall"sv, region.is_syscall_region()));
if (region.vmobject().is_anonymous()) { TRY(region_object.add("purgeable"sv, region.vmobject().is_anonymous()));
TRY(region_object.add("volatile"sv, static_cast<Memory::AnonymousVMObject const&>(region.vmobject()).is_volatile())); if (region.vmobject().is_anonymous()) {
} TRY(region_object.add("volatile"sv, static_cast<Memory::AnonymousVMObject const&>(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("cacheable"sv, region.is_cacheable()));
TRY(region_object.add("size"sv, region.size())); TRY(region_object.add("address"sv, region.vaddr().get()));
TRY(region_object.add("amount_resident"sv, region.amount_resident())); TRY(region_object.add("size"sv, region.size()));
TRY(region_object.add("amount_dirty"sv, region.amount_dirty())); TRY(region_object.add("amount_resident"sv, region.amount_resident()));
TRY(region_object.add("cow_pages"sv, region.cow_pages())); TRY(region_object.add("amount_dirty"sv, region.amount_dirty()));
TRY(region_object.add("name"sv, region.name())); TRY(region_object.add("cow_pages"sv, region.cow_pages()));
TRY(region_object.add("vmobject"sv, region.vmobject().class_name())); TRY(region_object.add("name"sv, region.name()));
TRY(region_object.add("vmobject"sv, region.vmobject().class_name()));
StringBuilder pagemap_builder; StringBuilder pagemap_builder;
for (size_t i = 0; i < region.page_count(); ++i) { for (size_t i = 0; i < region.page_count(); ++i) {
auto page = region.physical_page(i); auto page = region.physical_page(i);
if (!page) if (!page)
pagemap_builder.append('N'); pagemap_builder.append('N');
else if (page->is_shared_zero_page() || page->is_lazy_committed_page()) else if (page->is_shared_zero_page() || page->is_lazy_committed_page())
pagemap_builder.append('Z'); pagemap_builder.append('Z');
else else
pagemap_builder.append('P'); 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())); return {};
TRY(region_object.finish()); });
}
return {};
})); }));
TRY(array.finish()); TRY(array.finish());
return {}; return {};

View file

@ -204,7 +204,7 @@ NEVER_INLINE void syscall_handler(TrapFrame* trap)
PANIC("Syscall from process with IOPL != 0"); 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 function;
FlatPtr arg1; FlatPtr arg1;

View file

@ -17,8 +17,10 @@ ErrorOr<FlatPtr> Process::sys$map_time_page()
auto& vmobject = TimeManagement::the().time_page_vmobject(); 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 address_space().with([&](auto& space) -> ErrorOr<FlatPtr> {
return region->vaddr().get(); 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<FlatPtr> Process::sys$clock_gettime(clockid_t clock_id, Userspace<timespec*> user_ts) ErrorOr<FlatPtr> Process::sys$clock_gettime(clockid_t clock_id, Userspace<timespec*> user_ts)

View file

@ -551,7 +551,7 @@ ErrorOr<void> Process::do_exec(NonnullLockRefPtr<OpenFileDescription> main_progr
// This ensures that the process always has a valid page directory. // This ensures that the process always has a valid page directory.
Memory::MemoryManager::enter_address_space(*load_result.space); 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_executable.with([&](auto& executable) { executable = main_program_description->custody(); });
m_arguments = move(arguments); m_arguments = move(arguments);
@ -661,7 +661,7 @@ ErrorOr<void> Process::do_exec(NonnullLockRefPtr<OpenFileDescription> main_progr
regs.rip = load_result.entry_eip; regs.rip = load_result.entry_eip;
regs.rsp = new_userspace_sp; regs.rsp = new_userspace_sp;
#endif #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); TemporaryChange profiling_disabler(m_profiling, was_profiling);

View file

@ -65,7 +65,6 @@ ErrorOr<FlatPtr> Process::sys$fork(RegisterState& regs)
}); });
dbgln_if(FORK_DEBUG, "fork: child={}", child); 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 // 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()); child_first_thread->update_signal_mask(Thread::current()->signal_mask());
@ -123,23 +122,26 @@ ErrorOr<FlatPtr> Process::sys$fork(RegisterState& regs)
# error Unknown architecture # error Unknown architecture
#endif #endif
{ TRY(address_space().with([&](auto& parent_space) {
TRY(address_space().region_tree().with([&](auto& parent_region_tree) -> ErrorOr<void> { return child->address_space().with([&](auto& child_space) {
return child->address_space().region_tree().with([&](auto& child_region_tree) -> ErrorOr<void> { child_space->set_enforces_syscall_regions(parent_space->enforces_syscall_regions());
for (auto& region : parent_region_tree.regions()) { return parent_space->region_tree().with([&](auto& parent_region_tree) -> ErrorOr<void> {
dbgln_if(FORK_DEBUG, "fork: cloning Region '{}' @ {}", region.name(), region.vaddr()); return child_space->region_tree().with([&](auto& child_region_tree) -> ErrorOr<void> {
auto region_clone = TRY(region.try_clone()); for (auto& region : parent_region_tree.regions()) {
TRY(region_clone->map(child->address_space().page_directory(), Memory::ShouldFlushTLB::No)); dbgln_if(FORK_DEBUG, "fork: cloning Region '{}' @ {}", region.name(), region.vaddr());
TRY(child_region_tree.place_specifically(*region_clone, region.range())); auto region_clone = TRY(region.try_clone());
auto* child_region = region_clone.leak_ptr(); 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 (&region == m_master_tls_region.unsafe_ptr()) if (&region == m_master_tls_region.unsafe_ptr())
child->m_master_tls_region = TRY(child_region->try_make_weak_ptr()); child->m_master_tls_region = TRY(child_region->try_make_weak_ptr());
} }
return {}; return {};
});
}); });
})); });
} }));
thread_finalizer_guard.disarm(); thread_finalizer_guard.disarm();

View file

@ -18,7 +18,7 @@ static Singleton<SpinlockProtected<HashMap<GlobalFutexKey, NonnullLockRefPtr<Fut
void Process::clear_futex_queues_on_exec() void Process::clear_futex_queues_on_exec()
{ {
s_global_futex_queues->with([this](auto& queues) { s_global_futex_queues->with([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) { queues.remove_all_matching([address_space](auto& futex_key, auto& futex_queue) {
if ((futex_key.raw.offset & futex_key_private_flag) == 0) if ((futex_key.raw.offset & futex_key_private_flag) == 0)
return false; return false;
@ -45,45 +45,47 @@ ErrorOr<GlobalFutexKey> Process::get_futex_key(FlatPtr user_address, bool shared
if (!shared) { // If this is thread-shared, we can skip searching the matching region if (!shared) { // If this is thread-shared, we can skip searching the matching region
return GlobalFutexKey { return GlobalFutexKey {
.private_ = { .private_ = {
.address_space = &address_space(), .address_space = this->address_space().with([](auto& space) { return space.ptr(); }),
.user_address = user_address | futex_key_private_flag, .user_address = user_address | futex_key_private_flag,
} }
}; };
} }
auto* matching_region = address_space().find_region_containing(range); return address_space().with([&](auto& space) -> ErrorOr<GlobalFutexKey> {
if (!matching_region) auto* matching_region = space->find_region_containing(range);
return EFAULT; 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 { return GlobalFutexKey {
.private_ = { .shared = {
.address_space = &address_space(), .vmobject = &vmobject,
.user_address = user_address | futex_key_private_flag, .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<FlatPtr> Process::sys$futex(Userspace<Syscall::SC_futex_params const*> user_params) ErrorOr<FlatPtr> Process::sys$futex(Userspace<Syscall::SC_futex_params const*> user_params)

View file

@ -14,16 +14,18 @@ ErrorOr<FlatPtr> Process::sys$get_stack_bounds(Userspace<FlatPtr*> user_stack_ba
VERIFY_NO_PROCESS_BIG_LOCK(this); VERIFY_NO_PROCESS_BIG_LOCK(this);
auto& regs = Thread::current()->get_register_dump_from_stack(); auto& regs = Thread::current()->get_register_dump_from_stack();
FlatPtr stack_pointer = regs.userspace_sp(); 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<FlatPtr> {
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. // The syscall handler should have killed us if we had an invalid stack pointer.
VERIFY(stack_region); VERIFY(stack_region);
FlatPtr stack_base = stack_region->range().base().get(); FlatPtr stack_base = stack_region->range().base().get();
size_t stack_size = stack_region->size(); size_t stack_size = stack_region->size();
TRY(copy_to_user(user_stack_base, &stack_base)); TRY(copy_to_user(user_stack_base, &stack_base));
TRY(copy_to_user(user_stack_size, &stack_size)); TRY(copy_to_user(user_stack_size, &stack_size));
return 0; return 0;
});
} }
} }

View file

@ -192,32 +192,23 @@ ErrorOr<FlatPtr> Process::sys$mmap(Userspace<Syscall::SC_mmap_params const*> use
Memory::Region* region = nullptr; Memory::Region* region = nullptr;
// If MAP_FIXED is specified, existing mappings that intersect the requested range are removed. LockRefPtr<OpenFileDescription> description;
if (map_fixed) LockRefPtr<Memory::AnonymousVMObject> vmobject;
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 };
}
if (map_anonymous) { if (map_anonymous) {
auto strategy = map_noreserve ? AllocationStrategy::None : AllocationStrategy::Reserve; auto strategy = map_noreserve ? AllocationStrategy::None : AllocationStrategy::Reserve;
LockRefPtr<Memory::AnonymousVMObject> vmobject;
if (flags & MAP_PURGEABLE) { if (flags & MAP_PURGEABLE) {
vmobject = TRY(Memory::AnonymousVMObject::try_create_purgeable_with_size(rounded_size, strategy)); vmobject = TRY(Memory::AnonymousVMObject::try_create_purgeable_with_size(rounded_size, strategy));
} else { } else {
vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(rounded_size, strategy)); 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 { } else {
if (offset < 0) if (offset < 0)
return EINVAL; return EINVAL;
if (static_cast<size_t>(offset) & ~PAGE_MASK) if (static_cast<size_t>(offset) & ~PAGE_MASK)
return EINVAL; return EINVAL;
auto description = TRY(open_file_description(fd)); description = TRY(open_file_description(fd));
if (description->is_directory()) if (description->is_directory())
return ENODEV; return ENODEV;
// Require read access even when read protection is not requested. // Require read access even when read protection is not requested.
@ -229,24 +220,40 @@ ErrorOr<FlatPtr> Process::sys$mmap(Userspace<Syscall::SC_mmap_params const*> use
} }
if (description->inode()) if (description->inode())
TRY(validate_inode_mmap_prot(prot, *description->inode(), map_shared)); TRY(validate_inode_mmap_prot(prot, *description->inode(), map_shared));
region = TRY(description->mmap(*this, requested_range, static_cast<u64>(offset), prot, map_shared));
} }
if (!region) return address_space().with([&](auto& space) -> ErrorOr<FlatPtr> {
return ENOMEM; // 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); Memory::VirtualRange requested_range { VirtualAddress { addr }, rounded_size };
if (map_shared) if (addr && !(map_fixed || map_fixed_noreplace)) {
region->set_shared(true); // If there's an address but MAP_FIXED wasn't specified, the address is just a hint.
if (map_stack) requested_range = { {}, rounded_size };
region->set_stack(true); }
if (name)
region->set_name(move(name));
PerformanceManager::add_mmap_perf_event(*this, *region); 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<u64>(offset), prot, map_shared));
}
return region->vaddr().get(); if (!region)
return ENOMEM;
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));
PerformanceManager::add_mmap_perf_event(*this, *region);
return region->vaddr().get();
});
} }
ErrorOr<FlatPtr> Process::sys$mprotect(Userspace<void*> addr, size_t size, int prot) ErrorOr<FlatPtr> Process::sys$mprotect(Userspace<void*> addr, size_t size, int prot)
@ -265,117 +272,119 @@ ErrorOr<FlatPtr> Process::sys$mprotect(Userspace<void*> addr, size_t size, int p
if (!is_user_range(range_to_mprotect)) if (!is_user_range(range_to_mprotect))
return EFAULT; return EFAULT;
if (auto* whole_region = address_space().find_region_from_range(range_to_mprotect)) { return address_space().with([&](auto& space) -> ErrorOr<FlatPtr> {
if (!whole_region->is_mmap()) if (auto* whole_region = space->find_region_from_range(range_to_mprotect)) {
return EPERM; if (!whole_region->is_mmap())
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<Memory::InodeVMObject const&>(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 0;
if (old_region->vmobject().is_inode())
TRY(validate_inode_mmap_prot(prot, static_cast<Memory::InodeVMObject const&>(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())
return EPERM; return EPERM;
TRY(validate_mmap_prot(prot, region->is_stack(), region->vmobject().is_anonymous(), region)); TRY(validate_mmap_prot(prot, whole_region->is_stack(), whole_region->vmobject().is_anonymous(), whole_region));
if (region->vmobject().is_inode()) if (whole_region->access() == Memory::prot_to_region_access_flags(prot))
TRY(validate_inode_mmap_prot(prot, static_cast<Memory::InodeVMObject const&>(region->vmobject()).inode(), region->is_shared())); return 0;
full_size_found += region->range().intersect(range_to_mprotect).size(); if (whole_region->vmobject().is_inode())
TRY(validate_inode_mmap_prot(prot, static_cast<Memory::InodeVMObject const&>(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 (full_size_found != range_to_mprotect.size()) // Check if we can carve out the desired range from an existing region
return ENOMEM; if (auto* old_region = space->find_region_containing(range_to_mprotect)) {
if (!old_region->is_mmap())
// Finally, iterate over each region, either updating its access flags if the range covers it wholly, return EPERM;
// or carving out a new subregion with the appropriate access flags set. TRY(validate_mmap_prot(prot, old_region->is_stack(), old_region->vmobject().is_anonymous(), old_region));
for (auto* old_region : regions) {
if (old_region->access() == Memory::prot_to_region_access_flags(prot)) if (old_region->access() == Memory::prot_to_region_access_flags(prot))
continue; return 0;
if (old_region->vmobject().is_inode())
TRY(validate_inode_mmap_prot(prot, static_cast<Memory::InodeVMObject const&>(old_region->vmobject()).inode(), old_region->is_shared()));
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 // Remove the old region from our regions tree, since were going to add another region
// with the exact same start address. // with the exact same start address.
auto region = address_space().take_region(*old_region); auto region = space->take_region(*old_region);
region->unmap(); region->unmap();
// This vector is the region(s) adjacent to our range. // 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. // 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)); auto adjacent_regions = TRY(space->try_split_region_around_range(*region, range_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));
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_readable(prot & PROT_READ);
new_region->set_writable(prot & PROT_WRITE); new_region->set_writable(prot & PROT_WRITE);
new_region->set_executable(prot & PROT_EXEC); 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. // Map the new regions using our page directory (they were just allocated and don't have one).
if (adjacent_regions.size()) for (auto* adjacent_region : adjacent_regions) {
TRY(adjacent_regions[0]->map(address_space().page_directory())); TRY(adjacent_region->map(space->page_directory()));
}
TRY(new_region->map(address_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<Memory::InodeVMObject const&>(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<FlatPtr> Process::sys$madvise(Userspace<void*> address, size_t size, int advice) ErrorOr<FlatPtr> Process::sys$madvise(Userspace<void*> address, size_t size, int advice)
@ -391,22 +400,24 @@ ErrorOr<FlatPtr> Process::sys$madvise(Userspace<void*> address, size_t size, int
if (!is_user_range(range_to_madvise)) if (!is_user_range(range_to_madvise))
return EFAULT; return EFAULT;
auto* region = address_space().find_region_from_range(range_to_madvise); return address_space().with([&](auto& space) -> ErrorOr<FlatPtr> {
if (!region) auto* region = 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<Memory::AnonymousVMObject&>(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; 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<Memory::AnonymousVMObject&>(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<FlatPtr> Process::sys$set_mmap_name(Userspace<Syscall::SC_set_mmap_name_params const*> user_params) ErrorOr<FlatPtr> Process::sys$set_mmap_name(Userspace<Syscall::SC_set_mmap_name_params const*> user_params)
@ -421,23 +432,27 @@ ErrorOr<FlatPtr> Process::sys$set_mmap_name(Userspace<Syscall::SC_set_mmap_name_
auto name = TRY(try_copy_kstring_from_user(params.name)); auto name = TRY(try_copy_kstring_from_user(params.name));
auto range = TRY(Memory::expand_range_to_page_boundaries((FlatPtr)params.addr, params.size)); auto range = TRY(Memory::expand_range_to_page_boundaries((FlatPtr)params.addr, params.size));
auto* region = address_space().find_region_from_range(range); return address_space().with([&](auto& space) -> ErrorOr<FlatPtr> {
if (!region) auto* region = space->find_region_from_range(range);
return EINVAL; if (!region)
if (!region->is_mmap()) return EINVAL;
return EPERM; if (!region->is_mmap())
return EPERM;
region->set_name(move(name)); region->set_name(move(name));
PerformanceManager::add_mmap_perf_event(*this, *region); PerformanceManager::add_mmap_perf_event(*this, *region);
return 0; return 0;
});
} }
ErrorOr<FlatPtr> Process::sys$munmap(Userspace<void*> addr, size_t size) ErrorOr<FlatPtr> Process::sys$munmap(Userspace<void*> addr, size_t size)
{ {
VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
TRY(require_promise(Pledge::stdio)); 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; return 0;
} }
@ -449,32 +464,34 @@ ErrorOr<FlatPtr> Process::sys$mremap(Userspace<Syscall::SC_mremap_params const*>
auto old_range = TRY(Memory::expand_range_to_page_boundaries((FlatPtr)params.old_address, params.old_size)); 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); return address_space().with([&](auto& space) -> ErrorOr<FlatPtr> {
if (!old_region) auto* old_region = space->find_region_from_range(old_range);
return EINVAL; if (!old_region)
return EINVAL;
if (!old_region->is_mmap()) if (!old_region->is_mmap())
return EPERM; return EPERM;
if (old_region->vmobject().is_shared_inode() && params.flags & MAP_PRIVATE && !(params.flags & (MAP_ANONYMOUS | MAP_NORESERVE))) { if (old_region->vmobject().is_shared_inode() && params.flags & MAP_PRIVATE && !(params.flags & (MAP_ANONYMOUS | MAP_NORESERVE))) {
auto range = old_region->range(); auto range = old_region->range();
auto old_prot = region_access_flags_to_prot(old_region->access()); auto old_prot = region_access_flags_to_prot(old_region->access());
auto old_offset = old_region->offset_in_vmobject(); auto old_offset = old_region->offset_in_vmobject();
NonnullLockRefPtr inode = static_cast<Memory::SharedInodeVMObject&>(old_region->vmobject()).inode(); NonnullLockRefPtr inode = static_cast<Memory::SharedInodeVMObject&>(old_region->vmobject()).inode();
auto new_vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(inode)); auto new_vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(inode));
auto old_name = old_region->take_name(); auto old_name = old_region->take_name();
old_region->unmap(); old_region->unmap();
address_space().deallocate_region(*old_region); 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)); 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); new_region->set_mmap(true);
return new_region->vaddr().get(); return new_region->vaddr().get();
} }
dbgln("sys$mremap: Unimplemented remap request (flags={})", params.flags); dbgln("sys$mremap: Unimplemented remap request (flags={})", params.flags);
return ENOTIMPL; return ENOTIMPL;
});
} }
ErrorOr<FlatPtr> Process::sys$allocate_tls(Userspace<char const*> initial_data, size_t size) ErrorOr<FlatPtr> Process::sys$allocate_tls(Userspace<char const*> initial_data, size_t size)
@ -504,56 +521,61 @@ ErrorOr<FlatPtr> Process::sys$allocate_tls(Userspace<char const*> initial_data,
if (multiple_threads) if (multiple_threads)
return EINVAL; 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<FlatPtr> {
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_region = TRY(region->try_make_weak_ptr());
m_master_tls_size = size; m_master_tls_size = size;
m_master_tls_alignment = PAGE_SIZE; m_master_tls_alignment = PAGE_SIZE;
{ {
Kernel::SmapDisabler disabler; Kernel::SmapDisabler disabler;
void* fault_at; void* fault_at;
if (!Kernel::safe_memcpy((char*)m_master_tls_region.unsafe_ptr()->vaddr().as_ptr(), (char*)initial_data.ptr(), size, 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; return EFAULT;
} }
TRY(main_thread->make_thread_specific_region({})); TRY(main_thread->make_thread_specific_region({}));
#if ARCH(I386) #if ARCH(I386)
auto& tls_descriptor = Processor::current().get_gdt_entry(GDT_SELECTOR_TLS); auto& tls_descriptor = Processor::current().get_gdt_entry(GDT_SELECTOR_TLS);
tls_descriptor.set_base(main_thread->thread_specific_data()); tls_descriptor.set_base(main_thread->thread_specific_data());
tls_descriptor.set_limit(main_thread->thread_specific_region_size()); tls_descriptor.set_limit(main_thread->thread_specific_region_size());
#else #else
MSR fs_base_msr(MSR_FS_BASE); MSR fs_base_msr(MSR_FS_BASE);
fs_base_msr.set(main_thread->thread_specific_data().get()); fs_base_msr.set(main_thread->thread_specific_data().get());
#endif #endif
return m_master_tls_region.unsafe_ptr()->vaddr().get(); return m_master_tls_region.unsafe_ptr()->vaddr().get();
});
} }
ErrorOr<FlatPtr> Process::sys$msyscall(Userspace<void*> address) ErrorOr<FlatPtr> Process::sys$msyscall(Userspace<void*> address)
{ {
VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
if (address_space().enforces_syscall_regions())
return EPERM;
if (!address) { return address_space().with([&](auto& space) -> ErrorOr<FlatPtr> {
address_space().set_enforces_syscall_regions(true); if (space->enforces_syscall_regions())
return EPERM;
if (!address) {
space->set_enforces_syscall_regions(true);
return 0;
}
if (!Memory::is_user_address(address.vaddr()))
return EFAULT;
auto* region = space->find_region_containing(Memory::VirtualRange { address.vaddr(), 1 });
if (!region)
return EINVAL;
if (!region->is_mmap())
return EINVAL;
region->set_syscall_region(true);
return 0; return 0;
} });
if (!Memory::is_user_address(address.vaddr()))
return EFAULT;
auto* region = address_space().find_region_containing(Memory::VirtualRange { address.vaddr(), 1 });
if (!region)
return EINVAL;
if (!region->is_mmap())
return EINVAL;
region->set_syscall_region(true);
return 0;
} }
ErrorOr<FlatPtr> Process::sys$msync(Userspace<void*> address, size_t size, int flags) ErrorOr<FlatPtr> Process::sys$msync(Userspace<void*> address, size_t size, int flags)
@ -572,37 +594,39 @@ ErrorOr<FlatPtr> Process::sys$msync(Userspace<void*> address, size_t size, int f
// Note: This is not specified // Note: This is not specified
auto rounded_size = TRY(Memory::page_round_up(size)); auto rounded_size = TRY(Memory::page_round_up(size));
auto regions = TRY(address_space().find_regions_intersecting(Memory::VirtualRange { address.vaddr(), rounded_size })); return address_space().with([&](auto& space) -> ErrorOr<FlatPtr> {
// All regions from address upto address+size shall be mapped auto regions = TRY(space->find_regions_intersecting(Memory::VirtualRange { address.vaddr(), rounded_size }));
if (regions.is_empty()) // All regions from address upto address+size shall be mapped
return ENOMEM; if (regions.is_empty())
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; 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) { size_t total_intersection_size = 0;
auto& vmobject = region->vmobject(); Memory::VirtualRange range_to_sync { address.vaddr(), rounded_size };
if (!vmobject.is_shared_inode()) for (auto const* region : regions) {
continue; // 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;
off_t offset = region->offset_in_vmobject() + address.ptr() - region->range().base().get(); for (auto* region : regions) {
auto& vmobject = region->vmobject();
if (!vmobject.is_shared_inode())
continue;
auto& inode_vmobject = static_cast<Memory::SharedInodeVMObject&>(vmobject); off_t offset = region->offset_in_vmobject() + address.ptr() - region->range().base().get();
// FIXME: If multiple regions belong to the same vmobject we might want to coalesce these writes
// FIXME: Handle MS_ASYNC auto& inode_vmobject = static_cast<Memory::SharedInodeVMObject&>(vmobject);
TRY(inode_vmobject.sync(offset / PAGE_SIZE, rounded_size / PAGE_SIZE)); // FIXME: If multiple regions belong to the same vmobject we might want to coalesce these writes
// FIXME: Handle MS_INVALIDATE // FIXME: Handle MS_ASYNC
} TRY(inode_vmobject.sync(offset / PAGE_SIZE, rounded_size / PAGE_SIZE));
return 0; // FIXME: Handle MS_INVALIDATE
}
return 0;
});
} }
} }

View file

@ -197,31 +197,34 @@ ErrorOr<void> Process::peek_user_data(Span<u8> destination, Userspace<u8 const*>
ErrorOr<void> Process::poke_user_data(Userspace<FlatPtr*> address, FlatPtr data) ErrorOr<void> Process::poke_user_data(Userspace<FlatPtr*> address, FlatPtr data)
{ {
Memory::VirtualRange range = { address.vaddr(), sizeof(FlatPtr) }; Memory::VirtualRange range = { address.vaddr(), sizeof(FlatPtr) };
auto* region = address_space().find_region_containing(range);
if (!region) return address_space().with([&](auto& space) -> ErrorOr<void> {
return EFAULT; auto* region = space->find_region_containing(range);
ScopedAddressSpaceSwitcher switcher(*this); if (!region)
if (region->is_shared()) { return EFAULT;
// If the region is shared, we change its vmobject to a PrivateInodeVMObject ScopedAddressSpaceSwitcher switcher(*this);
// to prevent the write operation from changing any shared inode data if (region->is_shared()) {
VERIFY(region->vmobject().is_shared_inode()); // If the region is shared, we change its vmobject to a PrivateInodeVMObject
auto vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(static_cast<Memory::SharedInodeVMObject&>(region->vmobject()).inode())); // to prevent the write operation from changing any shared inode data
region->set_vmobject(move(vmobject)); VERIFY(region->vmobject().is_shared_inode());
region->set_shared(false); auto vmobject = TRY(Memory::PrivateInodeVMObject::try_create_with_inode(static_cast<Memory::SharedInodeVMObject&>(region->vmobject()).inode()));
} region->set_vmobject(move(vmobject));
bool const was_writable = region->is_writable(); region->set_shared(false);
if (!was_writable) { }
region->set_writable(true); bool const was_writable = region->is_writable();
region->remap();
}
ScopeGuard rollback([&]() {
if (!was_writable) { if (!was_writable) {
region->set_writable(false); region->set_writable(true);
region->remap(); 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<FlatPtr> Thread::peek_debug_register(u32 register_index) ErrorOr<FlatPtr> Thread::peek_debug_register(u32 register_index)

View file

@ -134,100 +134,40 @@ ErrorOr<void> Process::remap_range_as_stack(FlatPtr address, size_t size)
if (!is_user_range(range_to_remap)) if (!is_user_range(range_to_remap))
return EFAULT; return EFAULT;
if (auto* whole_region = address_space().find_region_from_range(range_to_remap)) { return address_space().with([&](auto& space) -> ErrorOr<void> {
if (!whole_region->is_mmap()) if (auto* whole_region = space->find_region_from_range(range_to_remap)) {
return EPERM; if (!whole_region->is_mmap())
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()));
}
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())
return EPERM; return EPERM;
if (!region->vmobject().is_anonymous() || region->is_shared()) if (!whole_region->vmobject().is_anonymous() || whole_region->is_shared())
return EINVAL; return EINVAL;
full_size_found += region->range().intersect(range_to_remap).size(); 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 (full_size_found != range_to_remap.size()) if (auto* old_region = space->find_region_containing(range_to_remap)) {
return ENOMEM; if (!old_region->is_mmap())
return EPERM;
if (!old_region->vmobject().is_anonymous() || old_region->is_shared())
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 // Remove the old region from our regions tree, since were going to add another region
// with the exact same start address. // with the exact same start address.
auto region = address_space().take_region(*old_region); auto region = space->take_region(*old_region);
region->unmap(); region->unmap();
// This vector is the region(s) adjacent to our range. // 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. // 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)); auto adjacent_regions = TRY(space->try_split_region_around_range(*region, range_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));
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->unsafe_clear_access();
new_region->set_readable(true); new_region->set_readable(true);
new_region->set_writable(true); new_region->set_writable(true);
@ -235,16 +175,78 @@ ErrorOr<void> Process::remap_range_as_stack(FlatPtr address, size_t size)
new_region->set_syscall_region(false); new_region->set_syscall_region(false);
new_region->clear_to_zero(); new_region->clear_to_zero();
// Map the new region using our page directory (they were just allocated and don't have one) if any. // Map the new regions using our page directory (they were just allocated and don't have one).
TRY(adjacent_regions[0]->map(address_space().page_directory())); 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();
}
return EINVAL; 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 = 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<FlatPtr> Process::sys$sigaltstack(Userspace<stack_t const*> user_ss, Userspace<stack_t*> user_old_ss) ErrorOr<FlatPtr> Process::sys$sigaltstack(Userspace<stack_t const*> user_ss, Userspace<stack_t*> user_old_ss)

View file

@ -27,8 +27,11 @@ ErrorOr<FlatPtr> Process::sys$create_thread(void* (*entry)(void*), Userspace<Sys
if (user_sp.has_overflow()) if (user_sp.has_overflow())
return EOVERFLOW; return EOVERFLOW;
if (!MM.validate_user_stack(this->address_space(), VirtualAddress(user_sp.value() - 4))) TRY(address_space().with([&](auto& space) -> ErrorOr<void> {
return EFAULT; 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 // FIXME: return EAGAIN if Thread::all_threads().size() is greater than PTHREAD_THREADS_MAX
@ -60,7 +63,7 @@ ErrorOr<FlatPtr> Process::sys$create_thread(void* (*entry)(void*), Userspace<Sys
regs.rdx = params.rdx; regs.rdx = params.rdx;
regs.rcx = params.rcx; regs.rcx = params.rcx;
#endif #endif
regs.cr3 = address_space().page_directory().cr3(); regs.cr3 = address_space().with([](auto& space) { return space->page_directory().cr3(); });
TRY(thread->make_thread_specific_region({})); TRY(thread->make_thread_specific_region({}));
@ -92,7 +95,9 @@ void Process::sys$exit_thread(Userspace<void*> exit_value, Userspace<void*> stac
PerformanceManager::add_thread_exit_event(*current_thread); PerformanceManager::add_thread_exit_event(*current_thread);
if (stack_location) { 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()) if (unmap_result.is_error())
dbgln("Failed to unmap thread stack, terminating thread anyway. Error code: {}", unmap_result.error()); dbgln("Failed to unmap thread stack, terminating thread anyway. Error code: {}", unmap_result.error());
} }

View file

@ -105,7 +105,7 @@ Thread::Thread(NonnullLockRefPtr<Process> process, NonnullOwnPtr<Memory::Region>
# error Unknown architecture # error Unknown architecture
#endif #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_base = m_kernel_stack_region->vaddr().get();
m_kernel_stack_top = m_kernel_stack_region->vaddr().offset(default_kernel_stack_size).get() & ~(FlatPtr)0x7u; 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; u32 unlock_count;
[[maybe_unused]] auto rc = unlock_process_if_locked(unlock_count); [[maybe_unused]] auto rc = unlock_process_if_locked(unlock_count);
if (m_thread_specific_range.has_value()) { if (m_thread_specific_range.has_value()) {
auto* region = process().address_space().find_region_from_range(m_thread_specific_range.value()); process().address_space().with([&](auto& space) {
process().address_space().deallocate_region(*region); auto* region = space->find_region_from_range(m_thread_specific_range.value());
space->deallocate_region(*region);
});
} }
#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION #ifdef ENABLE_KERNEL_COVERAGE_COLLECTION
KCOVDevice::free_thread(); KCOVDevice::free_thread();
@ -1352,15 +1354,18 @@ static ErrorOr<bool> symbolicate(RecognizedSymbol const& symbol, Process& proces
if (!Memory::is_user_address(VirtualAddress(symbol.address))) { if (!Memory::is_user_address(VirtualAddress(symbol.address))) {
TRY(builder.try_append("0xdeadc0de\n"sv)); TRY(builder.try_append("0xdeadc0de\n"sv));
} else { } else {
if (auto* region = process.address_space().find_region_containing({ VirtualAddress(symbol.address), sizeof(FlatPtr) })) { TRY(process.address_space().with([&](auto& space) -> ErrorOr<void> {
size_t offset = symbol.address - region->vaddr().get(); if (auto* region = space->find_region_containing({ VirtualAddress(symbol.address), sizeof(FlatPtr) })) {
if (auto region_name = region->name(); !region_name.is_null() && !region_name.is_empty()) size_t offset = symbol.address - region->vaddr().get();
TRY(builder.try_appendff("{:p} {} + {:#x}\n", (void*)symbol.address, region_name, offset)); if (auto region_name = region->name(); !region_name.is_null() && !region_name.is_empty())
else TRY(builder.try_appendff("{:p} {} + {:#x}\n", (void*)symbol.address, region_name, offset));
TRY(builder.try_appendff("{:p} {:p} + {:#x}\n", (void*)symbol.address, region->vaddr().as_ptr(), offset)); else
} else { TRY(builder.try_appendff("{:p} {:p} + {:#x}\n", (void*)symbol.address, region->vaddr().as_ptr(), offset));
TRY(builder.try_appendff("{:p}\n", symbol.address)); } else {
} TRY(builder.try_appendff("{:p}\n", symbol.address));
}
return {};
}));
} }
return true; return true;
} }
@ -1412,20 +1417,22 @@ ErrorOr<void> Thread::make_thread_specific_region(Badge<Process>)
if (!process().m_master_tls_region) if (!process().m_master_tls_region)
return {}; 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<void> {
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; 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_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); 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); m_thread_specific_data = VirtualAddress(thread_specific_data);
thread_specific_data->self = thread_specific_data; thread_specific_data->self = thread_specific_data;
if (process().m_master_tls_size != 0) 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); memcpy(thread_local_storage, process().m_master_tls_region.unsafe_ptr()->vaddr().as_ptr(), process().m_master_tls_size);
return {}; return {};
});
} }
LockRefPtr<Thread> Thread::from_tid(ThreadID tid) LockRefPtr<Thread> Thread::from_tid(ThreadID tid)