mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00: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
|
@ -26,6 +26,7 @@
|
||||||
#include <stratosphere/os/os_native_handle.hpp>
|
#include <stratosphere/os/os_native_handle.hpp>
|
||||||
#include <stratosphere/os/os_process_handle_api.hpp>
|
#include <stratosphere/os/os_process_handle_api.hpp>
|
||||||
#include <stratosphere/os/os_process_memory_api.hpp>
|
#include <stratosphere/os/os_process_memory_api.hpp>
|
||||||
|
#include <stratosphere/os/os_process_code_memory_api.hpp>
|
||||||
#include <stratosphere/os/os_random.hpp>
|
#include <stratosphere/os/os_random.hpp>
|
||||||
#include <stratosphere/os/os_mutex.hpp>
|
#include <stratosphere/os/os_mutex.hpp>
|
||||||
#include <stratosphere/os/os_condition_variable.hpp>
|
#include <stratosphere/os/os_condition_variable.hpp>
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* 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/os/os_native_handle.hpp>
|
||||||
|
#include <stratosphere/os/os_memory_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
struct ProcessMemoryRegion {
|
||||||
|
u64 address;
|
||||||
|
u64 size;
|
||||||
|
};
|
||||||
|
|
||||||
|
Result MapProcessCodeMemory(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions);
|
||||||
|
Result UnmapProcessCodeMemory(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions);
|
||||||
|
|
||||||
|
}
|
|
@ -25,10 +25,13 @@ namespace ams::os::impl {
|
||||||
AddressAllocationResult_OutOfSpace,
|
AddressAllocationResult_OutOfSpace,
|
||||||
};
|
};
|
||||||
|
|
||||||
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
template<std::unsigned_integral AddressType_, std::unsigned_integral SizeType_>
|
||||||
class AddressSpaceAllocatorBase {
|
class AddressSpaceAllocatorBase {
|
||||||
NON_COPYABLE(AddressSpaceAllocatorBase);
|
NON_COPYABLE(AddressSpaceAllocatorBase);
|
||||||
NON_MOVEABLE(AddressSpaceAllocatorBase);
|
NON_MOVEABLE(AddressSpaceAllocatorBase);
|
||||||
|
public:
|
||||||
|
using AddressType = AddressType_;
|
||||||
|
using SizeType = SizeType_;
|
||||||
private:
|
private:
|
||||||
static constexpr size_t MaxForbiddenRegions = 2;
|
static constexpr size_t MaxForbiddenRegions = 2;
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace ams::os::impl {
|
||||||
public:
|
public:
|
||||||
using Base::Base;
|
using Base::Base;
|
||||||
public:
|
public:
|
||||||
virtual bool CheckFreeSpace(uintptr_t address, size_t size) override {
|
virtual bool CheckFreeSpace(AddressType address, SizeType size) override {
|
||||||
/* Query the memory. */
|
/* Query the memory. */
|
||||||
svc::MemoryInfo memory_info;
|
svc::MemoryInfo memory_info;
|
||||||
svc::PageInfo page_info;
|
svc::PageInfo page_info;
|
||||||
|
|
|
@ -38,17 +38,21 @@ namespace ams::os::impl {
|
||||||
class AslrSpaceManagerTemplate {
|
class AslrSpaceManagerTemplate {
|
||||||
NON_COPYABLE(AslrSpaceManagerTemplate);
|
NON_COPYABLE(AslrSpaceManagerTemplate);
|
||||||
NON_MOVEABLE(AslrSpaceManagerTemplate);
|
NON_MOVEABLE(AslrSpaceManagerTemplate);
|
||||||
|
private:
|
||||||
|
using AddressType = typename Allocator::AddressType;
|
||||||
|
using SizeType = typename Allocator::SizeType;
|
||||||
private:
|
private:
|
||||||
Impl m_impl;
|
Impl m_impl;
|
||||||
Allocator m_allocator;
|
Allocator m_allocator;
|
||||||
public:
|
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. */
|
/* 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) {
|
if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, align_offset & (AslrSpaceLargeAlign - 1)); large_align != 0) {
|
||||||
return large_align;
|
return large_align;
|
||||||
}
|
}
|
||||||
|
@ -58,14 +62,14 @@ namespace ams::os::impl {
|
||||||
return m_allocator.AllocateSpace(size, MemoryPageSize, 0);
|
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);
|
return m_allocator.CheckGuardSpace(address, size, AslrSpaceGuardSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename MapFunction, typename UnmapFunction>
|
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. */
|
/* 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. */
|
/* Reserve space to map the memory. */
|
||||||
const uintptr_t map_address = this->AllocateSpace(size, align_offset);
|
const uintptr_t map_address = this->AllocateSpace(size, align_offset);
|
||||||
if (map_address == 0) {
|
if (map_address == 0) {
|
||||||
|
@ -82,6 +86,9 @@ namespace ams::os::impl {
|
||||||
if (!this->CheckGuardSpace(map_address, size)) {
|
if (!this->CheckGuardSpace(map_address, size)) {
|
||||||
/* We don't have guard space, so unmap. */
|
/* We don't have guard space, so unmap. */
|
||||||
unmap_function(map_address, size);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
namespace ams::ro::impl {
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ namespace ams::os {
|
||||||
R_DEFINE_ERROR_RESULT(SessionClosedForReceive, 510);
|
R_DEFINE_ERROR_RESULT(SessionClosedForReceive, 510);
|
||||||
R_DEFINE_ERROR_RESULT(SessionClosedForReply, 511);
|
R_DEFINE_ERROR_RESULT(SessionClosedForReply, 511);
|
||||||
R_DEFINE_ERROR_RESULT(ReceiveListBroken, 512);
|
R_DEFINE_ERROR_RESULT(ReceiveListBroken, 512);
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidProcessMemory, 513);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(NotImplemented, 1000);
|
R_DEFINE_ERROR_RESULT(NotImplemented, 1000);
|
||||||
R_DEFINE_ERROR_RESULT(NotSupported, 1001);
|
R_DEFINE_ERROR_RESULT(NotSupported, 1001);
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "ro_map_utils.hpp"
|
|
||||||
|
|
||||||
namespace ams::ro::impl {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
|
|
||||||
ALWAYS_INLINE u64 GetCurrentProcessInfo(svc::InfoType info_type) {
|
|
||||||
u64 value;
|
|
||||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), info_type, svc::PseudoHandle::CurrentProcess, 0));
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Result SearchFreeRegion(uintptr_t *out, size_t mapping_size) {
|
|
||||||
/* Get address space extents. */
|
|
||||||
const uintptr_t heap_start = GetCurrentProcessInfo(svc::InfoType_HeapRegionAddress);
|
|
||||||
const size_t heap_size = GetCurrentProcessInfo(svc::InfoType_HeapRegionSize);
|
|
||||||
const uintptr_t alias_start = GetCurrentProcessInfo(svc::InfoType_AliasRegionAddress);
|
|
||||||
const size_t alias_size = GetCurrentProcessInfo(svc::InfoType_AliasRegionSize);
|
|
||||||
const uintptr_t aslr_start = GetCurrentProcessInfo(svc::InfoType_AslrRegionAddress);
|
|
||||||
const size_t aslr_size = GetCurrentProcessInfo(svc::InfoType_AslrRegionSize);
|
|
||||||
|
|
||||||
/* Iterate upwards to find a free region. */
|
|
||||||
uintptr_t address = aslr_start;
|
|
||||||
while (true) {
|
|
||||||
/* Declare variables for memory querying. */
|
|
||||||
svc::MemoryInfo mem_info;
|
|
||||||
svc::PageInfo page_info;
|
|
||||||
|
|
||||||
/* Check that we're still within bounds. */
|
|
||||||
R_UNLESS(address < address + mapping_size, ro::ResultOutOfAddressSpace());
|
|
||||||
|
|
||||||
/* If we're within the heap region, skip to the end of the heap region. */
|
|
||||||
if (heap_size != 0 && !(address + mapping_size - 1 < heap_start || heap_start + heap_size - 1 < address)) {
|
|
||||||
R_UNLESS(address < heap_start + heap_size, ro::ResultOutOfAddressSpace());
|
|
||||||
address = heap_start + heap_size;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we're within the alias region, skip to the end of the alias region. */
|
|
||||||
if (alias_size != 0 && !(address + mapping_size - 1 < alias_start || alias_start + alias_size - 1 < address)) {
|
|
||||||
R_UNLESS(address < alias_start + alias_size, ro::ResultOutOfAddressSpace());
|
|
||||||
address = alias_start + alias_size;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the current memory range. */
|
|
||||||
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address));
|
|
||||||
|
|
||||||
/* If the memory range is free and big enough, use it. */
|
|
||||||
if (mem_info.state == svc::MemoryState_Free && mapping_size <= ((mem_info.base_address + mem_info.size) - address)) {
|
|
||||||
*out = address;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check that we can advance. */
|
|
||||||
R_UNLESS(address < mem_info.base_address + mem_info.size, ro::ResultOutOfAddressSpace());
|
|
||||||
R_UNLESS(mem_info.base_address + mem_info.size - 1 < aslr_start + aslr_size - 1, ro::ResultOutOfAddressSpace());
|
|
||||||
|
|
||||||
/* Advance. */
|
|
||||||
address = mem_info.base_address + mem_info.size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
/*
|
|
||||||
* 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::ro::impl {
|
|
||||||
|
|
||||||
constexpr inline auto RetrySearchCount = 512;
|
|
||||||
|
|
||||||
Result SearchFreeRegion(uintptr_t *out, size_t mapping_size);
|
|
||||||
|
|
||||||
class ProcessRegionInfo {
|
|
||||||
NON_COPYABLE(ProcessRegionInfo);
|
|
||||||
NON_MOVEABLE(ProcessRegionInfo);
|
|
||||||
private:
|
|
||||||
static constexpr size_t StackGuardSize = 4 * os::MemoryPageSize;
|
|
||||||
private:
|
|
||||||
u64 m_heap_start;
|
|
||||||
u64 m_heap_size;
|
|
||||||
u64 m_alias_start;
|
|
||||||
u64 m_alias_size;
|
|
||||||
u64 m_aslr_start;
|
|
||||||
u64 m_aslr_size;
|
|
||||||
public:
|
|
||||||
ProcessRegionInfo(os::NativeHandle process) {
|
|
||||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_heap_start), svc::InfoType_HeapRegionAddress, process, 0));
|
|
||||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_heap_size), svc::InfoType_HeapRegionSize, process, 0));
|
|
||||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_alias_start), svc::InfoType_AliasRegionAddress, process, 0));
|
|
||||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_alias_size), svc::InfoType_AliasRegionSize, process, 0));
|
|
||||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_aslr_start), svc::InfoType_AslrRegionAddress, process, 0));
|
|
||||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_aslr_size), svc::InfoType_AslrRegionSize, process, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 GetAslrRegion(u64 mapping_size) const {
|
|
||||||
/* If we can, look for a region. */
|
|
||||||
if (mapping_size <= m_aslr_size) {
|
|
||||||
for (auto i = 0; i < RetrySearchCount; ++i) {
|
|
||||||
/* Get a random address. */
|
|
||||||
const u64 address = m_aslr_start + os::GenerateRandomU64((m_aslr_size - mapping_size) / os::MemoryPageSize) * os::MemoryPageSize;
|
|
||||||
|
|
||||||
/* Check that it's not contained within heap. */
|
|
||||||
if (m_heap_size != 0 && !(address + mapping_size - 1 < m_heap_start || m_heap_start + m_heap_size - 1 < address)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check that it's not contained within alias. */
|
|
||||||
if (m_alias_size != 0 && !(address + mapping_size - 1 < m_alias_start || m_alias_start + m_alias_size - 1 < address)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the address. */
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We failed to find a region. */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CanEmplaceGuardSpaces(os::NativeHandle process, u64 address, u64 size) {
|
|
||||||
/* NOTE: Nintendo does not check the results of these svc calls. */
|
|
||||||
svc::MemoryInfo mem_info;
|
|
||||||
svc::PageInfo page_info;
|
|
||||||
|
|
||||||
/* Check for guard availability before the region. */
|
|
||||||
R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(mem_info), std::addressof(page_info), process, address - 1));
|
|
||||||
if (!(mem_info.state == svc::MemoryState_Free && mem_info.base_address <= address - StackGuardSize)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for guard availability after the region. */
|
|
||||||
R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(mem_info), std::addressof(page_info), process, address + size));
|
|
||||||
if (!(mem_info.state == svc::MemoryState_Free && address + size + StackGuardSize <= mem_info.base_address + mem_info.size)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class AutoCloseMap {
|
|
||||||
private:
|
|
||||||
Result m_result;
|
|
||||||
uintptr_t m_map_address;
|
|
||||||
os::NativeHandle m_handle;
|
|
||||||
u64 m_address;
|
|
||||||
u64 m_size;
|
|
||||||
public:
|
|
||||||
AutoCloseMap(uintptr_t map, os::NativeHandle handle, u64 addr, u64 size) : m_map_address(map), m_handle(handle), m_address(addr), m_size(size) {
|
|
||||||
m_result = svc::MapProcessMemory(m_map_address, m_handle, m_address, m_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
~AutoCloseMap() {
|
|
||||||
if (m_handle != os::InvalidNativeHandle && R_SUCCEEDED(m_result)) {
|
|
||||||
R_ABORT_UNLESS(svc::UnmapProcessMemory(m_map_address, m_handle, m_address, m_size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetResult() const {
|
|
||||||
return m_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsSuccess() const {
|
|
||||||
return R_SUCCEEDED(m_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cancel() {
|
|
||||||
m_handle = os::InvalidNativeHandle;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class MappedCodeMemory {
|
|
||||||
NON_COPYABLE(MappedCodeMemory);
|
|
||||||
private:
|
|
||||||
os::NativeHandle m_handle;
|
|
||||||
Result m_result;
|
|
||||||
u64 m_dst_address;
|
|
||||||
u64 m_src_address;
|
|
||||||
u64 m_size;
|
|
||||||
public:
|
|
||||||
constexpr MappedCodeMemory() : m_handle(os::InvalidNativeHandle), m_result(ro::ResultInternalError()), m_dst_address(0), m_src_address(0), m_size(0) {
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
|
|
||||||
MappedCodeMemory(os::NativeHandle handle, u64 dst, u64 src, u64 size) : m_handle(handle), m_dst_address(dst), m_src_address(src), m_size(size) {
|
|
||||||
m_result = svc::MapProcessCodeMemory(m_handle, m_dst_address, m_src_address, m_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
~MappedCodeMemory() {
|
|
||||||
if (m_handle != os::InvalidNativeHandle && R_SUCCEEDED(m_result) && m_size > 0) {
|
|
||||||
R_ABORT_UNLESS(svc::UnmapProcessCodeMemory(m_handle, m_dst_address, m_src_address, m_size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MappedCodeMemory(MappedCodeMemory &&rhs) : m_handle(rhs.m_handle), m_result(rhs.m_result), m_dst_address(rhs.m_dst_address), m_src_address(rhs.m_src_address), m_size(rhs.m_size) {
|
|
||||||
rhs.m_handle = os::InvalidNativeHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
MappedCodeMemory &operator=(MappedCodeMemory &&rhs) {
|
|
||||||
m_handle = rhs.m_handle;
|
|
||||||
m_result = rhs.m_result;
|
|
||||||
m_dst_address = rhs.m_dst_address;
|
|
||||||
m_src_address = rhs.m_src_address;
|
|
||||||
m_size = rhs.m_size;
|
|
||||||
|
|
||||||
rhs.m_handle = os::InvalidNativeHandle;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetResult() const {
|
|
||||||
return m_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsSuccess() const {
|
|
||||||
return R_SUCCEEDED(m_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cancel() {
|
|
||||||
m_handle = os::InvalidNativeHandle;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -15,65 +15,38 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "ro_nro_utils.hpp"
|
#include "ro_nro_utils.hpp"
|
||||||
#include "ro_map_utils.hpp"
|
|
||||||
|
|
||||||
namespace ams::ro::impl {
|
namespace ams::ro::impl {
|
||||||
|
|
||||||
Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
|
namespace {
|
||||||
/* Re-map the NRO/BSS as code memory in the destination process. */
|
|
||||||
MappedCodeMemory nro_mcm;
|
|
||||||
MappedCodeMemory bss_mcm;
|
|
||||||
ProcessRegionInfo region_info(process_handle);
|
|
||||||
u64 base_address;
|
|
||||||
{
|
|
||||||
const u64 memory_size = nro_heap_size + bss_heap_size;
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < RetrySearchCount; ++i) {
|
|
||||||
/* Get a random address for the nro. */
|
|
||||||
base_address = region_info.GetAslrRegion(memory_size);
|
|
||||||
R_UNLESS(base_address != 0, ro::ResultOutOfAddressSpace());
|
|
||||||
|
|
||||||
/* Map the NRO, retrying if random address was invalid. */
|
ALWAYS_INLINE size_t SetupNroProcessMemoryRegions(os::ProcessMemoryRegion *regions, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
|
||||||
MappedCodeMemory tmp_nro_mcm(process_handle, base_address, nro_heap_address, nro_heap_size);
|
/* Reset region count. */
|
||||||
R_TRY_CATCH(tmp_nro_mcm.GetResult()) {
|
size_t num_regions = 0;
|
||||||
R_CATCH(svc::ResultInvalidCurrentMemory) { continue; }
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
|
|
||||||
/* Handle bss. */
|
/* We always want a region for the nro. */
|
||||||
|
regions[num_regions++] = { nro_heap_address, nro_heap_size };
|
||||||
|
|
||||||
|
/* If we have bss, create a region for bss. */
|
||||||
if (bss_heap_size > 0) {
|
if (bss_heap_size > 0) {
|
||||||
/* Map BSS, retrying if random address was invalid. */
|
regions[num_regions++] = { bss_heap_address, bss_heap_size };
|
||||||
MappedCodeMemory tmp_bss_mcm(process_handle, base_address + nro_heap_size, bss_heap_address, bss_heap_size);
|
}
|
||||||
R_TRY_CATCH(tmp_bss_mcm.GetResult()) {
|
|
||||||
R_CATCH(svc::ResultInvalidCurrentMemory) { continue; }
|
return num_regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
|
||||||
|
/* Set up the process memory regions. */
|
||||||
|
os::ProcessMemoryRegion regions[2];
|
||||||
|
const size_t num_regions = SetupNroProcessMemoryRegions(regions, nro_heap_address, nro_heap_size, bss_heap_address, bss_heap_size);
|
||||||
|
|
||||||
|
/* Re-map the nro/bss as code memory in the destination process. */
|
||||||
|
R_TRY_CATCH(os::MapProcessCodeMemory(out_base_address, process_handle, regions, num_regions)) {
|
||||||
|
R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace())
|
||||||
} R_END_TRY_CATCH;
|
} R_END_TRY_CATCH;
|
||||||
|
|
||||||
/* Check that we can have guard spaces. */
|
|
||||||
if (!region_info.CanEmplaceGuardSpaces(process_handle, base_address, memory_size)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We succeeded, so save the bss memory. */
|
|
||||||
bss_mcm = std::move(tmp_bss_mcm);
|
|
||||||
} else {
|
|
||||||
/* Check that we can have guard spaces. */
|
|
||||||
if (!region_info.CanEmplaceGuardSpaces(process_handle, base_address, memory_size)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We succeeded, so save the code memory. */
|
|
||||||
nro_mcm = std::move(tmp_nro_mcm);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
R_UNLESS(i != RetrySearchCount, ro::ResultOutOfAddressSpace());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cancel the automatic closing of our mappings. */
|
|
||||||
nro_mcm.Cancel();
|
|
||||||
bss_mcm.Cancel();
|
|
||||||
|
|
||||||
*out_base_address = base_address;
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,28 +55,20 @@ namespace ams::ro::impl {
|
||||||
const u64 ro_offset = rx_offset + rx_size;
|
const u64 ro_offset = rx_offset + rx_size;
|
||||||
const u64 rw_offset = ro_offset + ro_size;
|
const u64 rw_offset = ro_offset + ro_size;
|
||||||
|
|
||||||
R_TRY(svc::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, svc::MemoryPermission_ReadExecute));
|
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, os::MemoryPermission_ReadExecute));
|
||||||
R_TRY(svc::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, svc::MemoryPermission_Read));
|
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, os::MemoryPermission_ReadOnly));
|
||||||
R_TRY(svc::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, svc::MemoryPermission_ReadWrite));
|
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, os::MemoryPermission_ReadWrite));
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size) {
|
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
|
||||||
/* First, unmap bss. */
|
/* Set up the process memory regions. */
|
||||||
if (bss_heap_size > 0) {
|
os::ProcessMemoryRegion regions[2];
|
||||||
R_TRY(svc::UnmapProcessCodeMemory(process_handle, base_address + code_size + rw_size, bss_heap_address, bss_heap_size));
|
const size_t num_regions = SetupNroProcessMemoryRegions(regions, nro_heap_address, nro_heap_size, bss_heap_address, bss_heap_size);
|
||||||
}
|
|
||||||
|
|
||||||
/* Next, unmap .rwdata */
|
/* Unmap the nro/bss. */
|
||||||
if (rw_size > 0) {
|
R_RETURN(os::UnmapProcessCodeMemory(process_handle, base_address, regions, num_regions));
|
||||||
R_TRY(svc::UnmapProcessCodeMemory(process_handle, base_address + code_size, nro_heap_address + code_size, rw_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finally, unmap .text + .rodata. */
|
|
||||||
R_TRY(svc::UnmapProcessCodeMemory(process_handle, base_address, nro_heap_address, code_size));
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,6 @@ namespace ams::ro::impl {
|
||||||
/* Utilities for working with NROs. */
|
/* Utilities for working with NROs. */
|
||||||
Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
|
Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
|
||||||
Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size);
|
Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size);
|
||||||
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size);
|
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "ro_nrr_utils.hpp"
|
#include "ro_nrr_utils.hpp"
|
||||||
#include "ro_map_utils.hpp"
|
|
||||||
#include "ro_service_impl.hpp"
|
#include "ro_service_impl.hpp"
|
||||||
|
|
||||||
namespace ams::ro::impl {
|
namespace ams::ro::impl {
|
||||||
|
@ -196,51 +195,29 @@ namespace ams::ro::impl {
|
||||||
|
|
||||||
/* Utilities for working with NRRs. */
|
/* Utilities for working with NRRs. */
|
||||||
Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, void *out_hash, size_t out_hash_size, os::NativeHandle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, NrrKind nrr_kind, bool enforce_nrr_kind) {
|
Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, void *out_hash, size_t out_hash_size, os::NativeHandle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, NrrKind nrr_kind, bool enforce_nrr_kind) {
|
||||||
/* Re-map the NRR as code memory in the destination process. */
|
/* Re-map the nrr as code memory in the destination process. */
|
||||||
MappedCodeMemory nrr_mcm;
|
u64 code_address = 0;
|
||||||
ProcessRegionInfo region_info(process_handle);
|
const os::ProcessMemoryRegion region = { nrr_heap_address, nrr_heap_size };
|
||||||
u64 code_address;
|
R_TRY_CATCH(os::MapProcessCodeMemory(std::addressof(code_address), process_handle, std::addressof(region), 1)) {
|
||||||
{
|
R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace())
|
||||||
int i;
|
|
||||||
for (i = 0; i < RetrySearchCount; ++i) {
|
|
||||||
/* Get a random address for the nrr. */
|
|
||||||
code_address = region_info.GetAslrRegion(nrr_heap_size);
|
|
||||||
R_UNLESS(code_address != 0, ro::ResultOutOfAddressSpace());
|
|
||||||
|
|
||||||
/* Map the code memory, retrying if the random address was invalid. */
|
|
||||||
MappedCodeMemory tmp_mcm(process_handle, code_address, nrr_heap_address, nrr_heap_size);
|
|
||||||
R_TRY_CATCH(tmp_mcm.GetResult()) {
|
|
||||||
R_CATCH(svc::ResultInvalidCurrentMemory) { continue; }
|
|
||||||
} R_END_TRY_CATCH;
|
} R_END_TRY_CATCH;
|
||||||
|
|
||||||
/* Check that we can have guard spaces. */
|
/* If we fail, unmap the nrr code memory. */
|
||||||
if (!region_info.CanEmplaceGuardSpaces(process_handle, code_address, nrr_heap_size)) {
|
ON_RESULT_FAILURE { R_ABORT_UNLESS(os::UnmapProcessCodeMemory(process_handle, code_address, std::addressof(region), 1)); };
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We succeeded, so save the code memory. */
|
/* Map the nrr in our process. */
|
||||||
nrr_mcm = std::move(tmp_mcm);
|
void *mapped_memory = nullptr;
|
||||||
break;
|
R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, code_address, region.size)) {
|
||||||
}
|
R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace())
|
||||||
|
} R_END_TRY_CATCH;
|
||||||
|
|
||||||
R_UNLESS(i != RetrySearchCount, ro::ResultOutOfAddressSpace());
|
/* If we fail, unmap the nrr memory. */
|
||||||
}
|
ON_RESULT_FAILURE_2 { os::UnmapProcessMemory(mapped_memory, process_handle, code_address, region.size); };
|
||||||
|
|
||||||
/* Decide where to map the NRR in our process. */
|
/* Validate the nrr header. */
|
||||||
uintptr_t map_address;
|
NrrHeader *nrr_header = static_cast<NrrHeader *>(mapped_memory);
|
||||||
R_UNLESS(R_SUCCEEDED(SearchFreeRegion(std::addressof(map_address), nrr_heap_size)), ro::ResultOutOfAddressSpace());
|
|
||||||
|
|
||||||
/* NOTE: Nintendo does not check the return value of this map. We will check, instead of aborting if it fails. */
|
|
||||||
AutoCloseMap nrr_map(map_address, process_handle, code_address, nrr_heap_size);
|
|
||||||
R_TRY(nrr_map.GetResult());
|
|
||||||
|
|
||||||
NrrHeader *nrr_header = reinterpret_cast<NrrHeader *>(map_address);
|
|
||||||
R_TRY(ValidateNrr(nrr_header, nrr_heap_size, program_id, nrr_kind, enforce_nrr_kind));
|
R_TRY(ValidateNrr(nrr_header, nrr_heap_size, program_id, nrr_kind, enforce_nrr_kind));
|
||||||
|
|
||||||
/* Cancel the automatic closing of our mappings. */
|
|
||||||
nrr_map.Cancel();
|
|
||||||
nrr_mcm.Cancel();
|
|
||||||
|
|
||||||
/* Save a copy of the hash that we verified. */
|
/* Save a copy of the hash that we verified. */
|
||||||
crypto::GenerateSha256(out_hash, out_hash_size, nrr_header->GetSignedArea(), nrr_header->GetSignedAreaSize());
|
crypto::GenerateSha256(out_hash, out_hash_size, nrr_header->GetSignedArea(), nrr_header->GetSignedAreaSize());
|
||||||
|
|
||||||
|
@ -250,16 +227,18 @@ namespace ams::ro::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UnmapNrr(os::NativeHandle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) {
|
Result UnmapNrr(os::NativeHandle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) {
|
||||||
R_TRY(svc::UnmapProcessMemory(reinterpret_cast<uintptr_t>(header), process_handle, mapped_code_address, nrr_heap_size));
|
/* Unmap our process mapping. */
|
||||||
R_TRY(svc::UnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size));
|
os::UnmapProcessMemory(const_cast<NrrHeader *>(header), process_handle, mapped_code_address, nrr_heap_size);
|
||||||
R_SUCCEED();
|
|
||||||
|
/* Unmap the code memory mapping. */
|
||||||
|
const os::ProcessMemoryRegion region = { nrr_heap_address, nrr_heap_size };
|
||||||
|
R_RETURN(os::UnmapProcessCodeMemory(process_handle, mapped_code_address, std::addressof(region), 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValidateNrrHashTableEntry(const void *signed_area, size_t signed_area_size, size_t hashes_offset, size_t num_hashes, const void *nrr_hash, const u8 *hash_table, const void *desired_hash) {
|
bool ValidateNrrHashTableEntry(const void *signed_area, size_t signed_area_size, size_t hashes_offset, size_t num_hashes, const void *nrr_hash, const u8 *hash_table, const void *desired_hash) {
|
||||||
crypto::Sha256Generator sha256;
|
crypto::Sha256Generator sha256;
|
||||||
sha256.Initialize();
|
sha256.Initialize();
|
||||||
|
|
||||||
|
|
||||||
/* Hash data before the hash table. */
|
/* Hash data before the hash table. */
|
||||||
const size_t pre_hash_table_size = hashes_offset - NrrHeader::GetSignedAreaOffset();
|
const size_t pre_hash_table_size = hashes_offset - NrrHeader::GetSignedAreaOffset();
|
||||||
sha256.Update(signed_area, pre_hash_table_size);
|
sha256.Update(signed_area, pre_hash_table_size);
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "ro_map_utils.hpp"
|
|
||||||
#include "ro_nrr_utils.hpp"
|
#include "ro_nrr_utils.hpp"
|
||||||
#include "ro_nro_utils.hpp"
|
#include "ro_nro_utils.hpp"
|
||||||
#include "ro_patcher.hpp"
|
#include "ro_patcher.hpp"
|
||||||
|
@ -248,16 +247,17 @@ namespace ams::ro::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) {
|
Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) {
|
||||||
/* Find space to map the NRO. */
|
/* Map the NRO. */
|
||||||
uintptr_t map_address;
|
void *mapped_memory = nullptr;
|
||||||
R_UNLESS(R_SUCCEEDED(SearchFreeRegion(std::addressof(map_address), expected_nro_size)), ro::ResultOutOfAddressSpace());
|
R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), m_process_handle, base_address, expected_nro_size)) {
|
||||||
|
R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace())
|
||||||
|
} R_END_TRY_CATCH;
|
||||||
|
|
||||||
/* Actually map the NRO. */
|
/* When we're done, unmap the memory. */
|
||||||
AutoCloseMap nro_map(map_address, m_process_handle, base_address, expected_nro_size);
|
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, m_process_handle, base_address, expected_nro_size); };
|
||||||
R_TRY(nro_map.GetResult());
|
|
||||||
|
|
||||||
/* Validate header. */
|
/* Validate header. */
|
||||||
const NroHeader *header = reinterpret_cast<const NroHeader *>(map_address);
|
const NroHeader *header = static_cast<const NroHeader *>(mapped_memory);
|
||||||
R_UNLESS(header->IsMagicValid(), ro::ResultInvalidNro());
|
R_UNLESS(header->IsMagicValid(), ro::ResultInvalidNro());
|
||||||
|
|
||||||
/* Read sizes from header. */
|
/* Read sizes from header. */
|
||||||
|
@ -298,7 +298,7 @@ namespace ams::ro::impl {
|
||||||
R_UNLESS(R_FAILED(this->GetNroInfoByModuleId(nullptr, module_id)), ro::ResultAlreadyLoaded());
|
R_UNLESS(R_FAILED(this->GetNroInfoByModuleId(nullptr, module_id)), ro::ResultAlreadyLoaded());
|
||||||
|
|
||||||
/* Apply patches to NRO. */
|
/* Apply patches to NRO. */
|
||||||
LocateAndApplyIpsPatchesToModule(module_id, reinterpret_cast<u8 *>(map_address), nro_size);
|
LocateAndApplyIpsPatchesToModule(module_id, static_cast<u8 *>(mapped_memory), nro_size);
|
||||||
|
|
||||||
/* Copy to output. */
|
/* Copy to output. */
|
||||||
*out_module_id = *module_id;
|
*out_module_id = *module_id;
|
||||||
|
@ -552,21 +552,14 @@ namespace ams::ro::impl {
|
||||||
|
|
||||||
/* Map the NRO. */
|
/* Map the NRO. */
|
||||||
R_TRY(MapNro(std::addressof(nro_info->base_address), context->GetProcessHandle(), nro_address, nro_size, bss_address, bss_size));
|
R_TRY(MapNro(std::addressof(nro_info->base_address), context->GetProcessHandle(), nro_address, nro_size, bss_address, bss_size));
|
||||||
|
ON_RESULT_FAILURE { UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, nro_size, bss_address, bss_size); };
|
||||||
|
|
||||||
/* Validate the NRO (parsing region extents). */
|
/* Validate the NRO (parsing region extents). */
|
||||||
u64 rx_size = 0, ro_size = 0, rw_size = 0;
|
u64 rx_size = 0, ro_size = 0, rw_size = 0;
|
||||||
{
|
|
||||||
ON_RESULT_FAILURE { UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, bss_address, bss_size, nro_size, 0); };
|
|
||||||
|
|
||||||
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), nro_info->base_address, nro_size, bss_size));
|
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), nro_info->base_address, nro_size, bss_size));
|
||||||
}
|
|
||||||
|
|
||||||
/* Set NRO perms. */
|
/* Set NRO perms. */
|
||||||
{
|
|
||||||
ON_RESULT_FAILURE { UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, bss_address, bss_size, rx_size + ro_size, rw_size); };
|
|
||||||
|
|
||||||
R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size));
|
R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size));
|
||||||
}
|
|
||||||
|
|
||||||
context->SetNroInfoInUse(nro_info, true);
|
context->SetNroInfoInUse(nro_info, true);
|
||||||
nro_info->code_size = rx_size + ro_size;
|
nro_info->code_size = rx_size + ro_size;
|
||||||
|
@ -594,7 +587,7 @@ namespace ams::ro::impl {
|
||||||
context->SetNroInfoInUse(nro_info, false);
|
context->SetNroInfoInUse(nro_info, false);
|
||||||
std::memset(nro_info, 0, sizeof(*nro_info));
|
std::memset(nro_info, 0, sizeof(*nro_info));
|
||||||
}
|
}
|
||||||
R_RETURN(UnmapNro(context->GetProcessHandle(), nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.bss_heap_address, nro_backup.bss_heap_size, nro_backup.code_size, nro_backup.rw_size));
|
R_RETURN(UnmapNro(context->GetProcessHandle(), nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.code_size + nro_backup.rw_size, nro_backup.bss_heap_address, nro_backup.bss_heap_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Debug service implementations. */
|
/* Debug service implementations. */
|
||||||
|
|
Loading…
Reference in a new issue