diff options
author | Ben Wiederhake <BenWiederhake.GitHub@gmx.de> | 2021-03-08 19:20:09 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-03-08 22:57:36 +0100 |
commit | 1e857de26354576fbaf46642e7956632bd1f8af9 (patch) | |
tree | a5ceaea5162d22ed40f8ed6a1915adf8de340939 /Userland/DevTools | |
parent | 45443f24ec6624a46abf7363c9ee962666dc5e5e (diff) | |
download | serenity-1e857de26354576fbaf46642e7956632bd1f8af9.zip |
UserspaceEmulator: Support munmap/mprotect with partial mappings
Fixes #5663.
Diffstat (limited to 'Userland/DevTools')
-rw-r--r-- | Userland/DevTools/UserspaceEmulator/Emulator.cpp | 63 | ||||
-rw-r--r-- | Userland/DevTools/UserspaceEmulator/Emulator.h | 2 | ||||
-rw-r--r-- | Userland/DevTools/UserspaceEmulator/SoftMMU.h | 22 |
3 files changed, 70 insertions, 17 deletions
diff --git a/Userland/DevTools/UserspaceEmulator/Emulator.cpp b/Userland/DevTools/UserspaceEmulator/Emulator.cpp index 3cf622c70d..77ba4a383c 100644 --- a/Userland/DevTools/UserspaceEmulator/Emulator.cpp +++ b/Userland/DevTools/UserspaceEmulator/Emulator.cpp @@ -980,14 +980,35 @@ int Emulator::virt$pipe(FlatPtr vm_pipefd, int flags) return rc; } -u32 Emulator::virt$munmap(FlatPtr address, u32 size) +static void round_to_page_size(FlatPtr& address, size_t& size) { - auto* region = mmu().find_region({ 0x23, address }); - VERIFY(region); - if (region->size() != round_up_to_power_of_two(size, PAGE_SIZE)) - TODO(); - m_range_allocator.deallocate(region->range()); - mmu().remove_region(*region); + auto new_end = round_up_to_power_of_two(address + size, PAGE_SIZE); + address &= ~(PAGE_SIZE - 1); + size = new_end - address; +} + +u32 Emulator::virt$munmap(FlatPtr address, size_t size) +{ + round_to_page_size(address, size); + Vector<Region*, 4> marked_for_deletion; + bool has_non_mmap_region = false; + mmu().for_regions_in({ 0x23, address }, size, [&](Region* region) { + if (region) { + if (!is<MmapRegion>(*region)) { + has_non_mmap_region = true; + return IterationDecision::Break; + } + marked_for_deletion.append(region); + } + return IterationDecision::Continue; + }); + if (has_non_mmap_region) + return -EINVAL; + + for (Region* region : marked_for_deletion) { + m_range_allocator.deallocate(region->range()); + mmu().remove_region(*region); + } return 0; } @@ -1037,6 +1058,7 @@ FlatPtr Emulator::virt$mremap(FlatPtr params_addr) Syscall::SC_mremap_params params; mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); + // FIXME: Support regions that have been split in the past (e.g. due to mprotect or munmap). if (auto* region = mmu().find_region({ m_cpu.ds(), params.old_address })) { if (!is<MmapRegion>(*region)) return -EINVAL; @@ -1086,15 +1108,24 @@ u32 Emulator::virt$unveil(u32) u32 Emulator::virt$mprotect(FlatPtr base, size_t size, int prot) { - if (auto* region = mmu().find_region({ m_cpu.ds(), base })) { - if (!is<MmapRegion>(*region)) - return -EINVAL; - VERIFY(region->size() == size); - auto& mmap_region = *(MmapRegion*)region; - mmap_region.set_prot(prot); - return 0; - } - return -EINVAL; + round_to_page_size(base, size); + bool has_non_mmaped_region = false; + + mmu().for_regions_in({ 0x23, base }, size, [&](Region* region) { + if (region) { + if (!is<MmapRegion>(*region)) { + has_non_mmaped_region = true; + return IterationDecision::Break; + } + auto& mmap_region = *(MmapRegion*)region; + mmap_region.set_prot(prot); + } + return IterationDecision::Continue; + }); + if (has_non_mmaped_region) + return -EINVAL; + + return 0; } u32 Emulator::virt$madvise(FlatPtr, size_t, int) diff --git a/Userland/DevTools/UserspaceEmulator/Emulator.h b/Userland/DevTools/UserspaceEmulator/Emulator.h index 961b9fcc89..b0bde0304a 100644 --- a/Userland/DevTools/UserspaceEmulator/Emulator.h +++ b/Userland/DevTools/UserspaceEmulator/Emulator.h @@ -99,7 +99,7 @@ private: u32 virt$mmap(u32); FlatPtr virt$mremap(FlatPtr); u32 virt$mount(u32); - u32 virt$munmap(FlatPtr address, u32 size); + u32 virt$munmap(FlatPtr address, size_t size); u32 virt$gettid(); u32 virt$getpid(); u32 virt$unveil(u32); diff --git a/Userland/DevTools/UserspaceEmulator/SoftMMU.h b/Userland/DevTools/UserspaceEmulator/SoftMMU.h index 5ee14c7a47..1fd8207131 100644 --- a/Userland/DevTools/UserspaceEmulator/SoftMMU.h +++ b/Userland/DevTools/UserspaceEmulator/SoftMMU.h @@ -87,6 +87,28 @@ public: } } + template<typename Callback> + void for_regions_in(X86::LogicalAddress address, size_t size, Callback callback) + { + VERIFY(size > 0); + X86::LogicalAddress address_end = address; + address_end.set_offset(address_end.offset() + size); + ensure_split_at(address); + ensure_split_at(address_end); + + size_t first_page = address.offset() / PAGE_SIZE; + size_t last_page = (address_end.offset() - 1) / PAGE_SIZE; + Region* last_reported = nullptr; + for (size_t page = first_page; page <= last_page; ++page) { + Region* current_region = m_page_to_region_map[page]; + if (page != first_page && current_region == last_reported) + continue; + if (callback(current_region) == IterationDecision::Break) + return; + last_reported = current_region; + } + } + private: Emulator& m_emulator; |