mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 08:47:34 +00:00
UserspaceEmulator: Support munmap/mprotect with partial mappings
Fixes #5663.
This commit is contained in:
parent
45443f24ec
commit
1e857de263
3 changed files with 70 additions and 17 deletions
|
@ -980,14 +980,35 @@ int Emulator::virt$pipe(FlatPtr vm_pipefd, int flags)
|
||||||
return rc;
|
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 });
|
auto new_end = round_up_to_power_of_two(address + size, PAGE_SIZE);
|
||||||
VERIFY(region);
|
address &= ~(PAGE_SIZE - 1);
|
||||||
if (region->size() != round_up_to_power_of_two(size, PAGE_SIZE))
|
size = new_end - address;
|
||||||
TODO();
|
}
|
||||||
m_range_allocator.deallocate(region->range());
|
|
||||||
mmu().remove_region(*region);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1037,6 +1058,7 @@ FlatPtr Emulator::virt$mremap(FlatPtr params_addr)
|
||||||
Syscall::SC_mremap_params params;
|
Syscall::SC_mremap_params params;
|
||||||
mmu().copy_from_vm(¶ms, params_addr, sizeof(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 (auto* region = mmu().find_region({ m_cpu.ds(), params.old_address })) {
|
||||||
if (!is<MmapRegion>(*region))
|
if (!is<MmapRegion>(*region))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -1086,15 +1108,24 @@ u32 Emulator::virt$unveil(u32)
|
||||||
|
|
||||||
u32 Emulator::virt$mprotect(FlatPtr base, size_t size, int prot)
|
u32 Emulator::virt$mprotect(FlatPtr base, size_t size, int prot)
|
||||||
{
|
{
|
||||||
if (auto* region = mmu().find_region({ m_cpu.ds(), base })) {
|
round_to_page_size(base, size);
|
||||||
if (!is<MmapRegion>(*region))
|
bool has_non_mmaped_region = false;
|
||||||
return -EINVAL;
|
|
||||||
VERIFY(region->size() == size);
|
mmu().for_regions_in({ 0x23, base }, size, [&](Region* region) {
|
||||||
auto& mmap_region = *(MmapRegion*)region;
|
if (region) {
|
||||||
mmap_region.set_prot(prot);
|
if (!is<MmapRegion>(*region)) {
|
||||||
return 0;
|
has_non_mmaped_region = true;
|
||||||
}
|
return IterationDecision::Break;
|
||||||
return -EINVAL;
|
}
|
||||||
|
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)
|
u32 Emulator::virt$madvise(FlatPtr, size_t, int)
|
||||||
|
|
|
@ -99,7 +99,7 @@ private:
|
||||||
u32 virt$mmap(u32);
|
u32 virt$mmap(u32);
|
||||||
FlatPtr virt$mremap(FlatPtr);
|
FlatPtr virt$mremap(FlatPtr);
|
||||||
u32 virt$mount(u32);
|
u32 virt$mount(u32);
|
||||||
u32 virt$munmap(FlatPtr address, u32 size);
|
u32 virt$munmap(FlatPtr address, size_t size);
|
||||||
u32 virt$gettid();
|
u32 virt$gettid();
|
||||||
u32 virt$getpid();
|
u32 virt$getpid();
|
||||||
u32 virt$unveil(u32);
|
u32 virt$unveil(u32);
|
||||||
|
|
|
@ -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:
|
private:
|
||||||
Emulator& m_emulator;
|
Emulator& m_emulator;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue