Skip to content

Commit

Permalink
memory: Implement protecting multiple VMAs (#2484)
Browse files Browse the repository at this point in the history
* Implement protecting multiple VMAs

A handful of games expect this to work, and updated versions of Grand Theft Auto V crash if it doesn't work.

* Clang
  • Loading branch information
StevenMiller123 authored Feb 21, 2025
1 parent 14717b8 commit 54a1694
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 16 deletions.
43 changes: 28 additions & 15 deletions src/core/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,19 +481,14 @@ int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* pr
return ORBIS_OK;
}

int MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex};

// Find the virtual memory area that contains the specified address range.
auto it = FindVMA(addr);
if (it == vma_map.end() || !it->second.Contains(addr, size)) {
LOG_ERROR(Core, "Address range not mapped");
return ORBIS_KERNEL_ERROR_EINVAL;
}
s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t size,
MemoryProt prot) {
const auto start_in_vma = addr - vma_base.base;
const auto adjusted_size =
vma_base.size - start_in_vma < size ? vma_base.size - start_in_vma : size;

VirtualMemoryArea& vma = it->second;
if (vma.type == VMAType::Free) {
LOG_ERROR(Core, "Cannot change protection on free memory region");
if (vma_base.type == VMAType::Free) {
LOG_ERROR(Kernel_Vmm, "Cannot change protection on free memory region");
return ORBIS_KERNEL_ERROR_EINVAL;
}

Expand All @@ -504,13 +499,13 @@ int MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {

MemoryProt invalid_flags = prot & ~valid_flags;
if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) {
LOG_ERROR(Core, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}", u32(prot),
u32(invalid_flags));
LOG_ERROR(Kernel_Vmm, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}",
u32(prot), u32(invalid_flags));
return ORBIS_KERNEL_ERROR_EINVAL;
}

// Change protection
vma.prot = prot;
vma_base.prot = prot;

// Set permissions
Core::MemoryPermission perms{};
Expand All @@ -533,6 +528,24 @@ int MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {

impl.Protect(addr, size, perms);

return adjusted_size;
}

s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex};
s64 protected_bytes = 0;
do {
auto it = FindVMA(addr + protected_bytes);
auto& vma_base = it->second;
auto result = 0;
result = ProtectBytes(addr + protected_bytes, vma_base, size - protected_bytes, prot);
if (result < 0) {
// ProtectBytes returned an error, return it
return result;
}
protected_bytes += result;
} while (protected_bytes < size);

return ORBIS_OK;
}

Expand Down
4 changes: 3 additions & 1 deletion src/core/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ class MemoryManager {

int QueryProtection(VAddr addr, void** start, void** end, u32* prot);

int Protect(VAddr addr, size_t size, MemoryProt prot);
s32 Protect(VAddr addr, size_t size, MemoryProt prot);

s64 ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t size, MemoryProt prot);

int VirtualQuery(VAddr addr, int flags, ::Libraries::Kernel::OrbisVirtualQueryInfo* info);

Expand Down

0 comments on commit 54a1694

Please # to comment.