Fix virtual memory semantics and refactor region policy (#681)

This commit is contained in:
Maurice Heumann
2026-01-04 19:46:14 +01:00
committed by GitHub
6 changed files with 297 additions and 68 deletions

View File

@@ -3,6 +3,7 @@
#include "../cpu_context.hpp"
#include "../emulator_utils.hpp"
#include "../syscall_utils.hpp"
#include "../memory_manager.hpp"
namespace syscalls
{
@@ -58,7 +59,15 @@ namespace syscalls
image_info.Protect = map_emulator_to_nt_protection(region_info.permissions);
image_info.AllocationProtect = map_emulator_to_nt_protection(region_info.initial_permissions);
image_info.Type = MEM_PRIVATE;
if (!region_info.is_reserved)
{
image_info.Type = 0;
}
else
{
image_info.Type = memory_region_policy::to_memory_basic_information_type(region_info.kind);
}
});
return STATUS_SUCCESS;
@@ -199,6 +208,12 @@ namespace syscalls
allocation_bytes = page_align_up(allocation_bytes);
bytes_to_allocate.write(allocation_bytes);
const auto base_protection = page_protection & ~static_cast<uint32_t>(PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE);
if (base_protection == PAGE_WRITECOPY || base_protection == PAGE_EXECUTE_WRITECOPY)
{
return STATUS_INVALID_PAGE_PROTECTION;
}
const auto protection = try_map_nt_to_emulator_protection(page_protection);
if (!protection.has_value())
{
@@ -218,7 +233,7 @@ namespace syscalls
// initial value of BaseAddress is non-NULL, the region is allocated starting at the specified virtual
// address rounded down to the next host page size address boundary. If the initial value of BaseAddress
// is NULL, the operating system will determine where to allocate the region.
potential_base = page_align_up(potential_base);
potential_base = page_align_down(potential_base);
}
if (!potential_base)
@@ -267,7 +282,7 @@ namespace syscalls
if (free_type == 0)
{
return STATUS_INVALID_PARAMETER;
return STATUS_INVALID_PARAMETER_4;
}
const auto allocation_base = base_address.read();
@@ -275,14 +290,70 @@ namespace syscalls
if (free_type & MEM_RELEASE)
{
return c.win_emu.memory.release_memory(allocation_base, static_cast<size_t>(allocation_size)) ? STATUS_SUCCESS
: STATUS_MEMORY_NOT_ALLOCATED;
if (!allocation_size)
{
const auto region_info = c.win_emu.memory.get_region_info(allocation_base);
if (!region_info.is_reserved)
{
return STATUS_MEMORY_NOT_ALLOCATED;
}
if (region_info.allocation_base != allocation_base)
{
return STATUS_FREE_VM_NOT_AT_BASE;
}
}
const auto region_kind = c.win_emu.memory.get_region_kind(allocation_base);
const auto denied_status = memory_region_policy::nt_free_virtual_memory_denied_status(region_kind);
if (denied_status != STATUS_SUCCESS)
{
return denied_status;
}
const bool success = c.win_emu.memory.release_memory(allocation_base, static_cast<size_t>(allocation_size));
if (success)
{
return STATUS_SUCCESS;
}
const auto region_info = c.win_emu.memory.get_region_info(allocation_base);
if (!region_info.is_reserved)
{
return STATUS_MEMORY_NOT_ALLOCATED;
}
return STATUS_FREE_VM_NOT_AT_BASE;
}
if (free_type & MEM_DECOMMIT)
{
return c.win_emu.memory.decommit_memory(allocation_base, static_cast<size_t>(allocation_size)) ? STATUS_SUCCESS
: STATUS_MEMORY_NOT_ALLOCATED;
const auto region_kind = c.win_emu.memory.get_region_kind(allocation_base);
const auto denied_status = memory_region_policy::nt_free_virtual_memory_denied_status(region_kind);
if (denied_status != STATUS_SUCCESS)
{
return denied_status;
}
auto decommit_size = static_cast<size_t>(allocation_size);
if (!decommit_size)
{
const auto region_info = c.win_emu.memory.get_region_info(allocation_base);
if (!region_info.is_reserved)
{
return STATUS_MEMORY_NOT_ALLOCATED;
}
if (region_info.allocation_base != allocation_base)
{
return STATUS_FREE_VM_NOT_AT_BASE;
}
decommit_size = region_info.allocation_length;
}
const bool success = c.win_emu.memory.decommit_memory(allocation_base, decommit_size);
return success ? STATUS_SUCCESS : STATUS_MEMORY_NOT_ALLOCATED;
}
throw std::runtime_error("Bad free type");

View File

@@ -1,6 +1,7 @@
#include "../std_include.hpp"
#include "../emulator_utils.hpp"
#include "../syscall_utils.hpp"
#include "../memory_manager.hpp"
#include <utils/io.hpp>
@@ -193,7 +194,8 @@ namespace syscalls
constexpr auto shared_section_size = 0x10000;
const auto address = c.win_emu.memory.find_free_allocation_base(shared_section_size);
c.win_emu.memory.allocate_memory(address, shared_section_size, memory_permission::read_write);
c.win_emu.memory.allocate_memory(address, shared_section_size, memory_permission::read_write, false,
memory_region_kind::section_view);
c.proc.shared_section_address = address;
c.proc.shared_section_size = shared_section_size;
@@ -206,7 +208,8 @@ namespace syscalls
constexpr auto dbwin_buffer_section_size = 0x1000;
const auto address = c.win_emu.memory.find_free_allocation_base(dbwin_buffer_section_size);
c.win_emu.memory.allocate_memory(address, dbwin_buffer_section_size, memory_permission::read_write);
c.win_emu.memory.allocate_memory(address, dbwin_buffer_section_size, memory_permission::read_write, false,
memory_region_kind::section_view);
c.proc.dbwin_buffer = address;
c.proc.dbwin_buffer_size = dbwin_buffer_section_size;
@@ -373,7 +376,7 @@ namespace syscalls
const auto aligned_size = static_cast<size_t>(page_align_up(size));
const auto reserve_only = section_entry->allocation_attributes == SEC_RESERVE;
const auto protection = map_nt_to_emulator_protection(section_entry->section_page_protection);
const auto address = c.win_emu.memory.allocate_memory(aligned_size, protection, reserve_only);
const auto address = c.win_emu.memory.allocate_memory(aligned_size, protection, reserve_only, 0, memory_region_kind::section_view);
if (!reserve_only && !file_data.empty())
{
@@ -577,24 +580,27 @@ namespace syscalls
return STATUS_INVALID_PARAMETER;
}
if (base_address == c.proc.shared_section_address)
if (c.proc.shared_section_address && base_address >= c.proc.shared_section_address &&
base_address < c.proc.shared_section_address + c.proc.shared_section_size)
{
const auto address = c.proc.shared_section_address;
c.proc.shared_section_address = 0;
c.win_emu.memory.release_memory(base_address, static_cast<size_t>(c.proc.shared_section_size));
c.win_emu.memory.release_memory(address, static_cast<size_t>(c.proc.shared_section_size));
return STATUS_SUCCESS;
}
if (base_address == c.proc.dbwin_buffer)
if (c.proc.dbwin_buffer && is_within_start_and_length(base_address, c.proc.dbwin_buffer, c.proc.dbwin_buffer_size))
{
const auto address = c.proc.dbwin_buffer;
c.proc.dbwin_buffer = 0;
c.win_emu.memory.release_memory(base_address, static_cast<size_t>(c.proc.dbwin_buffer_size));
c.win_emu.memory.release_memory(address, static_cast<size_t>(c.proc.dbwin_buffer_size));
return STATUS_SUCCESS;
}
const auto* mod = c.win_emu.mod_manager.find_by_address(base_address);
if (mod != nullptr)
{
if (c.win_emu.mod_manager.unmap(base_address))
if (c.win_emu.mod_manager.unmap(mod->image_base))
{
return STATUS_SUCCESS;
}
@@ -602,14 +608,14 @@ namespace syscalls
return STATUS_INVALID_PARAMETER;
}
if (c.win_emu.memory.release_memory(base_address, 0))
const auto region_info = c.win_emu.memory.get_region_info(base_address);
if (region_info.is_reserved && memory_region_policy::is_section_kind(region_info.kind) &&
c.win_emu.memory.release_memory(region_info.allocation_base, 0))
{
return STATUS_SUCCESS;
}
c.win_emu.log.error("Unmapping non-module/non-memory section not supported!\n");
c.emu.stop();
return STATUS_NOT_SUPPORTED;
return STATUS_NOT_MAPPED_VIEW;
}
NTSTATUS handle_NtUnmapViewOfSectionEx(const syscall_context& c, const handle process_handle, const uint64_t base_address,