mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-05-31 23:08:22 -04:00
ro/os: use os primitives for MapProcessCodeMemory
This commit is contained in:
parent
f5052b4bca
commit
c2c0a2e169
15 changed files with 297 additions and 400 deletions
|
@ -25,10 +25,13 @@ namespace ams::os::impl {
|
|||
AddressAllocationResult_OutOfSpace,
|
||||
};
|
||||
|
||||
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||
template<std::unsigned_integral AddressType_, std::unsigned_integral SizeType_>
|
||||
class AddressSpaceAllocatorBase {
|
||||
NON_COPYABLE(AddressSpaceAllocatorBase);
|
||||
NON_MOVEABLE(AddressSpaceAllocatorBase);
|
||||
public:
|
||||
using AddressType = AddressType_;
|
||||
using SizeType = SizeType_;
|
||||
private:
|
||||
static constexpr size_t MaxForbiddenRegions = 2;
|
||||
private:
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace ams::os::impl {
|
|||
public:
|
||||
using Base::Base;
|
||||
public:
|
||||
virtual bool CheckFreeSpace(uintptr_t address, size_t size) override {
|
||||
virtual bool CheckFreeSpace(AddressType address, SizeType size) override {
|
||||
/* Query the memory. */
|
||||
svc::MemoryInfo memory_info;
|
||||
svc::PageInfo page_info;
|
||||
|
|
|
@ -38,17 +38,21 @@ namespace ams::os::impl {
|
|||
class AslrSpaceManagerTemplate {
|
||||
NON_COPYABLE(AslrSpaceManagerTemplate);
|
||||
NON_MOVEABLE(AslrSpaceManagerTemplate);
|
||||
private:
|
||||
using AddressType = typename Allocator::AddressType;
|
||||
using SizeType = typename Allocator::SizeType;
|
||||
private:
|
||||
Impl m_impl;
|
||||
Allocator m_allocator;
|
||||
public:
|
||||
AslrSpaceManagerTemplate() : m_impl(), m_allocator(m_impl.GetAslrSpaceBeginAddress(), m_impl.GetAslrSpaceEndAddress(), AslrSpaceGuardSize, m_impl.GetForbiddenRegions(), m_impl.GetForbiddenRegionCount()) {
|
||||
template<typename... Args>
|
||||
AslrSpaceManagerTemplate(Args &&... args) : m_impl(), m_allocator(m_impl.GetAslrSpaceBeginAddress(), m_impl.GetAslrSpaceEndAddress(), AslrSpaceGuardSize, m_impl.GetForbiddenRegions(), m_impl.GetForbiddenRegionCount(), std::forward<Args>(args)...) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
uintptr_t AllocateSpace(size_t size, size_t align_offset) {
|
||||
AddressType AllocateSpace(SizeType size, SizeType align_offset) {
|
||||
/* Try to allocate a large-aligned space, if we can. */
|
||||
if (align_offset || size >= AslrSpaceLargeAlign) {
|
||||
if (align_offset || (size / AslrSpaceLargeAlign) != 0) {
|
||||
if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, align_offset & (AslrSpaceLargeAlign - 1)); large_align != 0) {
|
||||
return large_align;
|
||||
}
|
||||
|
@ -58,14 +62,14 @@ namespace ams::os::impl {
|
|||
return m_allocator.AllocateSpace(size, MemoryPageSize, 0);
|
||||
}
|
||||
|
||||
bool CheckGuardSpace(uintptr_t address, size_t size) {
|
||||
bool CheckGuardSpace(AddressType address, SizeType size) {
|
||||
return m_allocator.CheckGuardSpace(address, size, AslrSpaceGuardSize);
|
||||
}
|
||||
|
||||
template<typename MapFunction, typename UnmapFunction>
|
||||
Result MapAtRandomAddress(uintptr_t *out, MapFunction map_function, UnmapFunction unmap_function, size_t size, size_t align_offset) {
|
||||
Result MapAtRandomAddress(AddressType *out, MapFunction map_function, UnmapFunction unmap_function, SizeType size, SizeType align_offset) {
|
||||
/* Try to map up to 64 times. */
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
for (auto i = 0; i < 64; ++i) {
|
||||
/* Reserve space to map the memory. */
|
||||
const uintptr_t map_address = this->AllocateSpace(size, align_offset);
|
||||
if (map_address == 0) {
|
||||
|
@ -82,6 +86,9 @@ namespace ams::os::impl {
|
|||
if (!this->CheckGuardSpace(map_address, size)) {
|
||||
/* We don't have guard space, so unmap. */
|
||||
unmap_function(map_address, size);
|
||||
|
||||
/* NOTE: Nintendo is missing this continue; this is almost certainly a bug. */
|
||||
/* This will cause them to incorrectly return success after unmapping if guard space is not present. */
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class ProcessCodeMemoryImpl {
|
||||
public:
|
||||
static Result Map(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions);
|
||||
static Result Unmap(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions);
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_process_code_memory_impl.hpp"
|
||||
#include "os_aslr_space_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
class ProcessAddressSpaceAllocator final : public AddressSpaceAllocatorBase<u64, u64> {
|
||||
private:
|
||||
using Base = AddressSpaceAllocatorBase<u64, u64>;
|
||||
private:
|
||||
NativeHandle m_handle;
|
||||
public:
|
||||
ProcessAddressSpaceAllocator(u64 start_address, u64 end_address, SizeType guard_size, const AddressSpaceAllocatorForbiddenRegion *forbidden_regions, size_t num_forbidden_regions, NativeHandle handle) : Base(start_address, end_address, guard_size, forbidden_regions, num_forbidden_regions), m_handle(handle) {
|
||||
/* ... */
|
||||
}
|
||||
public:
|
||||
virtual bool CheckFreeSpace(AddressType address, SizeType size) override {
|
||||
/* Query the memory. */
|
||||
svc::MemoryInfo memory_info;
|
||||
svc::PageInfo page_info;
|
||||
R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(memory_info), std::addressof(page_info), m_handle, address));
|
||||
|
||||
return memory_info.state == svc::MemoryState_Free && address + size <= memory_info.base_address + memory_info.size;
|
||||
}
|
||||
};
|
||||
|
||||
using ProcessAslrSpaceManager = AslrSpaceManagerTemplate<ProcessAddressSpaceAllocator, AslrSpaceManagerImpl>;
|
||||
|
||||
size_t GetTotalProcessMemoryRegionSize(const ProcessMemoryRegion *regions, size_t num_regions) {
|
||||
size_t total = 0;
|
||||
|
||||
for (size_t i = 0; i < num_regions; ++i) {
|
||||
total += regions[i].size;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result ProcessCodeMemoryImpl::Map(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions) {
|
||||
/* Get the total process memory region size. */
|
||||
const size_t total_size = GetTotalProcessMemoryRegionSize(regions, num_regions);
|
||||
|
||||
/* Create an aslr space manager for the process. */
|
||||
auto process_aslr_space_manager = ProcessAslrSpaceManager(handle);
|
||||
|
||||
/* Map at a random address. */
|
||||
u64 mapped_address;
|
||||
R_TRY(process_aslr_space_manager.MapAtRandomAddress(std::addressof(mapped_address),
|
||||
[handle, regions, num_regions](u64 map_address, u64 map_size) -> Result {
|
||||
AMS_UNUSED(map_size);
|
||||
|
||||
/* Map the regions in order. */
|
||||
u64 mapped_size = 0;
|
||||
for (size_t i = 0; i < num_regions; ++i) {
|
||||
/* If we fail, unmap up to where we've mapped. */
|
||||
ON_RESULT_FAILURE { R_ABORT_UNLESS(ProcessCodeMemoryImpl::Unmap(handle, map_address, regions, i)); };
|
||||
|
||||
/* Map the current region. */
|
||||
R_TRY_CATCH(svc::MapProcessCodeMemory(handle, map_address + mapped_size, regions[i].address, regions[i].size)) {
|
||||
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||
R_CATCH(svc::ResultInvalidCurrentMemory) {
|
||||
/* Check if the process memory is invalid. */
|
||||
const u64 last_address = regions[i].address + regions[i].size - 1;
|
||||
u64 cur_address = regions[i].address;
|
||||
while (true) {
|
||||
svc::MemoryInfo memory_info;
|
||||
svc::PageInfo page_info;
|
||||
R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(memory_info), std::addressof(page_info), handle, cur_address));
|
||||
|
||||
R_UNLESS(memory_info.state == svc::MemoryState_Normal, os::ResultInvalidProcessMemory());
|
||||
R_UNLESS(memory_info.permission == svc::MemoryPermission_ReadWrite, os::ResultInvalidProcessMemory());
|
||||
R_UNLESS(memory_info.attribute == static_cast<svc::MemoryAttribute>(0), os::ResultInvalidProcessMemory());
|
||||
|
||||
cur_address = memory_info.base_address + memory_info.size;
|
||||
if (cur_address > last_address) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
R_THROW(os::ResultInvalidCurrentMemoryState());
|
||||
}
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
},
|
||||
[handle, regions, num_regions](u64 map_address, u64 map_size) -> void {
|
||||
AMS_UNUSED(map_size);
|
||||
R_ABORT_UNLESS(ProcessCodeMemoryImpl::Unmap(handle, map_address, regions, num_regions));
|
||||
},
|
||||
total_size,
|
||||
regions[0].address /* NOTE: This seems like a Nintendo bug, if the caller passed no regions. */
|
||||
));
|
||||
|
||||
/* Set the output address. */
|
||||
*out = mapped_address;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ProcessCodeMemoryImpl::Unmap(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions) {
|
||||
/* Get the total process memory region size. */
|
||||
const size_t total_size = GetTotalProcessMemoryRegionSize(regions, num_regions);
|
||||
|
||||
/* Unmap each region in order. */
|
||||
size_t cur_offset = total_size;
|
||||
for (size_t i = 0; i < num_regions; ++i) {
|
||||
/* We want to unmap in reverse order. */
|
||||
const auto &cur_region = regions[num_regions - 1 - i];
|
||||
|
||||
/* Subtract to update the current offset. */
|
||||
cur_offset -= cur_region.size;
|
||||
|
||||
/* Unmap. */
|
||||
R_TRY_CATCH(svc::UnmapProcessCodeMemory(handle, process_code_address + cur_offset, cur_region.address, cur_region.size)) {
|
||||
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "impl/os_process_code_memory_impl.hpp"
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
Result MapProcessCodeMemory(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions) {
|
||||
R_RETURN(::ams::os::impl::ProcessCodeMemoryImpl::Map(out, handle, regions, num_regions));
|
||||
}
|
||||
|
||||
Result UnmapProcessCodeMemory(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions) {
|
||||
R_RETURN(::ams::os::impl::ProcessCodeMemoryImpl::Unmap(handle, process_code_address, regions, num_regions));
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue