Implement guard pages (#406)

Hey, looking for feedback on my guard page implementation. 

I wasn't sure on how to structure the additional windows specific memory
flags, since the emulation backends won't like additional guard flag. I
opted to create a new `memory_permission_ext` enum to hold the guard
flag, and a `nt_memory_permission` struct to wrap the "common" memory
permission flags, with the new extended flags. This struct implicitly
coerces to the original `memory_permission` to reduce the amount of
changes for the PR.

This however meant that I changed signatures of `map_memory` and
`apply_memory_protection` in `memory_interface` to accommodate this new
structure, and was an afterthought.

The `map_nt_to_emulator_protection` function might also need some
attention now, too. For future reference, windows uses
[MiMakeProtectionMask](https://doxygen.reactos.org/d1/d9a/marea_8c.html#adfb66408771a4df77c1056cc2a99ef21)
in ntoskrnl to map `PAGE_*` flags to [MM PTE
constants](https://reactos.org/wiki/Techwiki:Memory_management_in_the_Windows_XP_kernel).

The test added to the `test-sample` binary seems to be passing.

Fixes #21
This commit is contained in:
Maurice Heumann
2025-07-17 17:41:06 +02:00
committed by GitHub
13 changed files with 359 additions and 34 deletions

View File

@@ -3,6 +3,7 @@
#include "memory_region.hpp"
#include "address_utils.hpp"
#include "memory_permission_ext.hpp"
#include <vector>
#include <optional>
@@ -73,7 +74,7 @@ namespace utils
static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region)
{
region.length = static_cast<size_t>(buffer.read<uint64_t>());
region.permissions = buffer.read<memory_permission>();
region.permissions = buffer.read<nt_memory_permission>();
}
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
@@ -189,8 +190,8 @@ void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer
}
}
bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions,
memory_permission* old_permissions)
bool memory_manager::protect_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions,
nt_memory_permission* old_permissions)
{
const auto entry = this->find_reserved_region(address);
if (entry == this->reserved_regions_.end())
@@ -268,7 +269,7 @@ bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mm
return true;
}
bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions,
bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions,
const bool reserve_only)
{
if (this->overlaps_reserved_region(address, size))
@@ -286,8 +287,9 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size,
if (!reserve_only)
{
this->map_memory(address, size, permissions);
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
this->map_memory(address, size, permissions.is_guarded() ? memory_permission::none : permissions.common);
entry->second.committed_regions[address] =
committed_region{size, nt_memory_permission{memory_permission::read_write, permissions.extended}};
}
this->update_layout_version();
@@ -295,7 +297,7 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size,
return true;
}
bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions)
bool memory_manager::commit_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions)
{
const auto entry = this->find_reserved_region(address);
if (entry == this->reserved_regions_.end())
@@ -473,7 +475,7 @@ void memory_manager::unmap_all_memory()
this->reserved_regions_.clear();
}
uint64_t memory_manager::allocate_memory(const size_t size, const memory_permission permissions,
uint64_t memory_manager::allocate_memory(const size_t size, const nt_memory_permission permissions,
const bool reserve_only)
{
const auto allocation_base = this->find_free_allocation_base(size);
@@ -519,8 +521,8 @@ region_info memory_manager::get_region_info(const uint64_t address)
region_info result{};
result.start = MIN_ALLOCATION_ADDRESS;
result.length = static_cast<size_t>(MAX_ALLOCATION_ADDRESS - result.start);
result.permissions = memory_permission::none;
result.initial_permissions = memory_permission::none;
result.permissions = nt_memory_permission();
result.initial_permissions = nt_memory_permission();
result.allocation_base = {};
result.allocation_length = result.length;
result.is_committed = false;