summaryrefslogtreecommitdiff
path: root/Userland/DevTools
diff options
context:
space:
mode:
authorBen Wiederhake <BenWiederhake.GitHub@gmx.de>2021-03-08 19:20:09 +0100
committerAndreas Kling <kling@serenityos.org>2021-03-08 22:57:36 +0100
commit1e857de26354576fbaf46642e7956632bd1f8af9 (patch)
treea5ceaea5162d22ed40f8ed6a1915adf8de340939 /Userland/DevTools
parent45443f24ec6624a46abf7363c9ee962666dc5e5e (diff)
downloadserenity-1e857de26354576fbaf46642e7956632bd1f8af9.zip
UserspaceEmulator: Support munmap/mprotect with partial mappings
Fixes #5663.
Diffstat (limited to 'Userland/DevTools')
-rw-r--r--Userland/DevTools/UserspaceEmulator/Emulator.cpp63
-rw-r--r--Userland/DevTools/UserspaceEmulator/Emulator.h2
-rw-r--r--Userland/DevTools/UserspaceEmulator/SoftMMU.h22
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(&params, 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;