diff --git a/app/src/main/cpp/skyline/kernel/memory.cpp b/app/src/main/cpp/skyline/kernel/memory.cpp index 22653b0e..18e20bff 100644 --- a/app/src/main/cpp/skyline/kernel/memory.cpp +++ b/app/src/main/cpp/skyline/kernel/memory.cpp @@ -4,7 +4,7 @@ namespace skyline::kernel { ChunkDescriptor *MemoryManager::GetChunk(u64 address) { for (auto &chunk : chunkList) - if (chunk.address <= address && (chunk.address + chunk.size) >= address) + if (chunk.address <= address && (chunk.address + chunk.size) > address) return &chunk; return nullptr; } @@ -13,7 +13,7 @@ namespace skyline::kernel { auto chunk = GetChunk(address); if (chunk) for (auto &block : chunk->blockList) - if (block.address <= address && (block.address + block.size) >= address) + if (block.address <= address && (block.address + block.size) > address) return █ return nullptr; } @@ -38,7 +38,7 @@ namespace skyline::kernel { void MemoryManager::DeleteChunk(u64 address) { chunkList.remove_if([address](const ChunkDescriptor &chunk) { - return chunk.address <= address && (chunk.address + chunk.size) >= address; + return chunk.address <= address && (chunk.address + chunk.size) > address; }); } @@ -70,7 +70,7 @@ namespace skyline::kernel { void MemoryManager::InsertBlock(ChunkDescriptor *chunk, BlockDescriptor block) { for (auto iter = chunk->blockList.begin(); iter != chunk->blockList.end(); iter++) { - if (iter->address <= block.address && (iter->address + iter->size) >= block.address) { + if (iter->address <= block.address && (iter->address + iter->size) > block.address) { if (iter->address == block.address && iter->size == block.size) { iter->attributes = block.attributes; iter->permission = block.permission; @@ -133,7 +133,7 @@ namespace skyline::kernel { auto chunk = GetChunk(address); if (chunk) for (auto &block : chunk->blockList) - if (block.address <= address && (block.address + block.size) >= address) + if (block.address <= address && (block.address + block.size) > address) return DescriptorPack{block, *chunk}; return std::nullopt; } diff --git a/app/src/main/cpp/skyline/kernel/memory.h b/app/src/main/cpp/skyline/kernel/memory.h index bb51cc3e..1387881b 100644 --- a/app/src/main/cpp/skyline/kernel/memory.h +++ b/app/src/main/cpp/skyline/kernel/memory.h @@ -245,6 +245,7 @@ namespace skyline { struct ChunkDescriptor { u64 address; //!< The address of the current chunk u64 size; //!< The size of the current chunk in bytes + u64 host; //!< The address of the chunk in the host memory::MemoryState state; //!< The MemoryState for the current block std::forward_list blockList; //!< This linked list holds the block descriptors for all the children blocks of this Chunk }; diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index 8f3162aa..63a31681 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -5,8 +5,8 @@ namespace skyline::kernel::svc { void SetHeapSize(DeviceState &state) { const u32 size = state.ctx->registers.w1; if (size % constant::HeapSizeDiv != 0) { - state.ctx->registers.x1 = 0; state.ctx->registers.w0 = constant::status::InvSize; + state.ctx->registers.x1 = 0; state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size); return; } diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp index 394a5edd..9e4b9bf3 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp @@ -1,22 +1,32 @@ #include "KPrivateMemory.h" #include "KProcess.h" #include +#include #include +#include namespace skyline::kernel::type { - KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState) : address(address), size(size), KMemory(state, KType::KPrivateMemory) { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x2 = static_cast(permission.Get()); - fregs.x3 = static_cast(MAP_PRIVATE | MAP_ANONYMOUS | ((address) ? MAP_FIXED : 0)); - fregs.x4 = static_cast(-1); - fregs.x8 = __NR_mmap; + KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState) : size(size), KMemory(state, KType::KPrivateMemory) { + if (address && !utils::PageAligned(address)) + throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", address); + fd = ASharedMemory_create("KPrivateMemory", size); + if (fd < 0) + throw exception("An error occurred while creating shared memory: {}", fd); + auto host = mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0); + if (host == MAP_FAILED) + throw exception("An occurred while mapping shared memory: {}", strerror(errno)); + Registers fregs{ + .x0 = address, + .x1 = size, + .x2 = static_cast(permission.Get()), + .x3 = static_cast(MAP_SHARED | ((address) ? MAP_FIXED : 0)), + .x4 = static_cast(fd), + .x8 = __NR_mmap, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) - throw exception("An error occurred while mapping private section in child process"); - if (!this->address) - this->address = fregs.x0; + throw exception("An error occurred while mapping private memory in child process"); + this->address = fregs.x0; BlockDescriptor block{ .address = fregs.x0, .size = size, @@ -25,6 +35,7 @@ namespace skyline::kernel::type { ChunkDescriptor chunk{ .address = fregs.x0, .size = size, + .host = reinterpret_cast(host), .state = memState, .blockList = {block}, }; @@ -32,33 +43,70 @@ namespace skyline::kernel::type { } void KPrivateMemory::Resize(size_t nSize) { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x2 = nSize; - fregs.x8 = __NR_mremap; + if (close(fd) < 0) + throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); + fd = ASharedMemory_create("KPrivateMemory", nSize); + if (fd < 0) + throw exception("An error occurred while creating shared memory: {}", fd); + Registers fregs{ + .x0 = address, + .x1 = size, + .x8 = __NR_munmap + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) - throw exception("An error occurred while remapping private section in child process"); - size = nSize; + throw exception("An error occurred while unmapping private memory in child process"); + fregs = { + .x0 = address, + .x1 = nSize, + .x2 = static_cast(PROT_READ | PROT_WRITE | PROT_EXEC), + .x3 = static_cast(MAP_SHARED | MAP_FIXED), + .x4 = static_cast(fd), + .x8 = __NR_mmap, + }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); + if (fregs.x0 < 0) + throw exception("An error occurred while remapping private memory in child process"); auto chunk = state.os->memory.GetChunk(address); - MemoryManager::ResizeChunk(chunk, size); + state.process->WriteMemory(reinterpret_cast(chunk->host), address, std::min(nSize, size), true); + for (const auto &block : chunk->blockList) { + if((block.address - chunk->address) < size) { + fregs = { + .x0 = block.address, + .x1 = std::min(block.size, (chunk->address + nSize) - block.address), + .x2 = static_cast(block.permission.Get()), + .x8 = __NR_mprotect, + }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); + if (fregs.x0 < 0) + throw exception("An error occurred while updating private memory's permissions in child process"); + } else + break; + } + munmap(reinterpret_cast(chunk->host), size); + auto host = mmap(reinterpret_cast(chunk->host), nSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0); + if (host == MAP_FAILED) + throw exception("An occurred while mapping shared memory: {}", strerror(errno)); + chunk->host = reinterpret_cast(host); + MemoryManager::ResizeChunk(chunk, nSize); + size = nSize; } void KPrivateMemory::UpdatePermission(const u64 address, const u64 size, memory::Permission permission) { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x2 = static_cast(permission.Get()); - fregs.x8 = __NR_mprotect; + Registers fregs{ + .x0 = address, + .x1 = size, + .x2 = static_cast(permission.Get()), + .x8 = __NR_mprotect, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) - throw exception("An error occurred while updating private section's permissions in child process"); + throw exception("An error occurred while updating private memory's permissions in child process"); auto chunk = state.os->memory.GetChunk(address); BlockDescriptor block{ - .address = address, - .size = size, - .permission = permission, + .address = address, + .size = size, + .permission = permission, }; MemoryManager::InsertBlock(chunk, block); } @@ -66,14 +114,17 @@ namespace skyline::kernel::type { KPrivateMemory::~KPrivateMemory() { try { if (state.process) { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x8 = __NR_munmap; + Registers fregs{ + .x0 = address, + .x1 = size, + .x8 = __NR_munmap, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); } } catch (const std::exception &) { } + auto chunk = state.os->memory.GetChunk(address); + munmap(reinterpret_cast(chunk->host), chunk->size); state.os->memory.DeleteChunk(address); } }; diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h index 9d5224c0..90a031d4 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h @@ -7,6 +7,9 @@ namespace skyline::kernel::type { * @brief KPrivateMemory is used to map memory local to the guest process */ class KPrivateMemory : public KMemory { + private: + int fd; //!< A file descriptor to the underlying shared memory + public: u64 address{}; //!< The address of the allocated memory size_t size{}; //!< The size of the allocated memory diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index 0c92f215..61fbfeaa 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -83,45 +83,68 @@ namespace skyline::kernel::type { return process; } - void KProcess::ReadMemory(void *destination, u64 offset, size_t size) const { + u64 KProcess::GetHostAddress(u64 address) const { + auto chunk = state.os->memory.GetChunk(address); + return (chunk && chunk->host) ? chunk->host + (address - chunk->address) : 0; + } + + void KProcess::ReadMemory(void *destination, const u64 offset, const size_t size, const bool forceGuest) const { + if (!forceGuest) { + auto source = GetHostAddress(offset); + if (source) { + memcpy(destination, reinterpret_cast(source), size); + return; + } + } struct iovec local{ .iov_base = destination, - .iov_len = size + .iov_len = size, }; struct iovec remote{ .iov_base = reinterpret_cast(offset), - .iov_len = size + .iov_len = size, }; - if (process_vm_readv(pid, &local, 1, &remote, 1, 0) < 0) pread64(memFd, destination, size, offset); } - void KProcess::WriteMemory(void *source, u64 offset, size_t size) const { + void KProcess::WriteMemory(void *source, const u64 offset, const size_t size, const bool forceGuest) const { + if(!forceGuest) { + auto destination = GetHostAddress(offset); + if (destination) { + memcpy(reinterpret_cast(destination), source, size); + return; + } + } struct iovec local{ .iov_base = source, - .iov_len = size + .iov_len = size, }; struct iovec remote{ .iov_base = reinterpret_cast(offset), - .iov_len = size + .iov_len = size, }; - if (process_vm_writev(pid, &local, 1, &remote, 1, 0) < 0) pwrite64(memFd, source, size, offset); } void KProcess::CopyMemory(u64 source, u64 destination, size_t size) const { - if (size <= PAGE_SIZE) { - std::vector buffer(size); - state.process->ReadMemory(buffer.data(), source, size); - state.process->WriteMemory(buffer.data(), destination, size); + auto sourceHost = GetHostAddress(source); + auto destinationHost = GetHostAddress(destination); + if(sourceHost && destinationHost) { + memcpy(reinterpret_cast(destinationHost), reinterpret_cast(sourceHost), size); } else { - Registers fregs{}; - fregs.x0 = source; - fregs.x1 = destination; - fregs.x2 = size; - state.nce->ExecuteFunction(ThreadCall::Memcopy, fregs); + if (size <= PAGE_SIZE) { + std::vector buffer(size); + state.process->ReadMemory(buffer.data(), source, size); + state.process->WriteMemory(buffer.data(), destination, size); + } else { + Registers fregs{}; + fregs.x0 = source; + fregs.x1 = destination; + fregs.x2 = size; + state.nce->ExecuteFunction(ThreadCall::Memcopy, fregs); + } } } @@ -145,8 +168,8 @@ namespace skyline::kernel::type { void KProcess::MutexLock(u64 address, handle_t owner, bool alwaysLock) { std::unique_lock lock(mutexLock); u32 mtxVal = ReadMemory(address); - if(alwaysLock) { - if(!mtxVal) { + if (alwaysLock) { + if (!mtxVal) { state.logger->Warn("Mutex Value was 0"); mtxVal = (constant::MtxOwnerMask & state.thread->handle); WriteMemory(mtxVal, address); @@ -159,7 +182,7 @@ namespace skyline::kernel::type { } auto &mtxWaiters = mutexes[address]; std::shared_ptr status; - for (auto it = mtxWaiters.begin();;++it) { + for (auto it = mtxWaiters.begin();; ++it) { if (it != mtxWaiters.end() && (*it)->priority >= state.thread->priority) continue; status = std::make_shared(state.thread->priority, state.thread->pid); @@ -170,7 +193,7 @@ namespace skyline::kernel::type { while (!status->flag); lock.lock(); for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it) - if((*it)->pid == state.thread->pid) { + if ((*it)->pid == state.thread->pid) { mtxWaiters.erase(it); break; } @@ -197,7 +220,7 @@ namespace skyline::kernel::type { std::unique_lock lock(conditionalLock); auto &condWaiters = conditionals[address]; std::shared_ptr status; - for (auto it = condWaiters.begin();;++it) { + for (auto it = condWaiters.begin();; ++it) { if (it != condWaiters.end() && (*it)->priority >= state.thread->priority) continue; status = std::make_shared(state.thread->priority, state.thread->pid); @@ -213,7 +236,7 @@ namespace skyline::kernel::type { } lock.lock(); for (auto it = condWaiters.begin(); it != condWaiters.end(); ++it) - if((*it)->pid == state.thread->pid) { + if ((*it)->pid == state.thread->pid) { condWaiters.erase(it); break; } diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h index 3b5f135b..77b75e9e 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.h +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h @@ -169,21 +169,41 @@ namespace skyline::kernel::type { WriteMemory(&item, address, sizeof(Type)); } + /** + * @brief This returns the host address for a specific address in guest memory + * @param address The corresponding guest address + * @return The corresponding host address + */ + u64 GetHostAddress(const u64 address) const; + + /** + * @tparam Type The type of the pointer to return + * @param address The address on the guest + * @return A pointer corresponding to a certain address on the guest + * @note This can return a nullptr if the address is invalid + */ + template + inline Type* GetPointer(const u64 address) const { + return reinterpret_cast(GetHostAddress(address)); + } + /** * @brief Read data from the process's memory * @param destination The address to the location where the process memory is written * @param offset The address to read from in process memory * @param size The amount of memory to be read + * @param forceGuest This flag forces the write to be performed in guest address space */ - void ReadMemory(void *destination, u64 offset, size_t size) const; + void ReadMemory(void *destination, const u64 offset, const size_t size, const bool forceGuest = false) const; /** * @brief Write to the process's memory * @param source The address of where the data to be written is present * @param offset The address to write to in process memory * @param size The amount of memory to be written + * @param forceGuest This flag forces the write to be performed in guest address space */ - void WriteMemory(void *source, u64 offset, size_t size) const; + void WriteMemory(void *source, const u64 offset, const size_t size, const bool forceGuest = false) const; /** * @brief Copy one chunk to another in the process's memory @@ -191,7 +211,7 @@ namespace skyline::kernel::type { * @param destination The address to write the read data to * @param size The amount of memory to be copied */ - void CopyMemory(u64 source, u64 destination, size_t size) const; + void CopyMemory(const u64 source, const u64 destination, const size_t size) const; /** * @brief Creates a new handle to a KObject and adds it to the process handle_table diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp index c46a17cb..1d9363d6 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp @@ -7,7 +7,9 @@ namespace skyline::kernel::type { KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState) : initialState(memState), KMemory(state, KType::KSharedMemory) { - fd = ASharedMemory_create("", size); + if (address && !utils::PageAligned(address)) + throw exception("KSharedMemory was created with non-page-aligned address: 0x{:X}", address); + fd = ASharedMemory_create("KSharedMemory", size); if (fd < 0) throw exception("An error occurred while creating shared memory: {}", fd); address = reinterpret_cast(mmap(reinterpret_cast(address), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | ((address) ? MAP_FIXED : 0), fd, 0)); @@ -17,19 +19,23 @@ namespace skyline::kernel::type { } u64 KSharedMemory::Map(const u64 address, const u64 size, memory::Permission permission) { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x2 = static_cast(permission.Get()); - fregs.x3 = static_cast(MAP_SHARED | ((address) ? MAP_FIXED : 0)); - fregs.x4 = static_cast(fd); - fregs.x8 = __NR_mmap; + if (address && !utils::PageAligned(address)) + throw exception("KSharedMemory was mapped to a non-page-aligned address: 0x{:X}", address); + Registers fregs{ + .x0 = address, + .x1 = size, + .x2 = static_cast(permission.Get()), + .x3 = static_cast(MAP_SHARED | ((address) ? MAP_FIXED : 0)), + .x4 = static_cast(fd), + .x8 = __NR_mmap, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while mapping shared memory in guest"); guest = {.address = fregs.x0, .size = size, .permission = permission}; ChunkDescriptor chunk{ .address = fregs.x0, + .host = kernel.address, .size = size, .state = initialState, }; @@ -44,33 +50,81 @@ namespace skyline::kernel::type { } void KSharedMemory::Resize(size_t size) { - if (guest.valid()) { - Registers fregs{}; - fregs.x0 = guest.address; - fregs.x1 = guest.size; - fregs.x2 = size; - fregs.x8 = __NR_mremap; + if (guest.valid() && kernel.valid()) { + if (close(fd) < 0) + throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); + fd = ASharedMemory_create("KSharedMemory", size); + if (fd < 0) + throw exception("An error occurred while creating shared memory: {}", fd); + Registers fregs{ + .x0 = guest.address, + .x1 = guest.size, + .x8 = __NR_munmap + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) - throw exception("An error occurred while remapping shared memory in guest"); - guest.size = size; + throw exception("An error occurred while unmapping private memory in child process"); + fregs = { + .x0 = guest.address, + .x1 = size, + .x2 = static_cast(PROT_READ | PROT_WRITE | PROT_EXEC), + .x3 = static_cast(MAP_SHARED | MAP_FIXED), + .x4 = static_cast(fd), + .x8 = __NR_mmap, + }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); + if (fregs.x0 < 0) + throw exception("An error occurred while remapping private memory in child process"); + state.process->WriteMemory(reinterpret_cast(kernel.address), guest.address, std::min(guest.size, size), true); auto chunk = state.os->memory.GetChunk(guest.address); + for (const auto &block : chunk->blockList) { + if((block.address - chunk->address) < guest.size) { + fregs = { + .x0 = block.address, + .x1 = std::min(block.size, (chunk->address + size) - block.address), + .x2 = static_cast(block.permission.Get()), + .x8 = __NR_mprotect, + }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); + if (fregs.x0 < 0) + throw exception("An error occurred while updating private memory's permissions in child process"); + } else + break; + } + munmap(reinterpret_cast(kernel.address), kernel.size); + auto host = mmap(reinterpret_cast(chunk->host), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0); + if (host == MAP_FAILED) + throw exception("An occurred while mapping shared memory: {}", strerror(errno)); + guest.size = size; MemoryManager::ResizeChunk(chunk, size); - } - if (kernel.valid()) { - if (mremap(reinterpret_cast(kernel.address), kernel.size, size, 0) == MAP_FAILED) - throw exception("An error occurred while remapping shared region: {}", strerror(errno)); + } else if (kernel.valid()) { + if (close(fd) < 0) + throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); + fd = ASharedMemory_create("KSharedMemory", size); + if (fd < 0) + throw exception("An error occurred while creating shared memory: {}", fd); + std::vector data(std::min(size, kernel.size)); + memcpy(data.data(), reinterpret_cast(kernel.address), std::min(size, kernel.size)); + munmap(reinterpret_cast(kernel.address), kernel.size); + auto address = mmap(reinterpret_cast(kernel.address), size, kernel.permission.Get(), MAP_SHARED, fd, 0); + if (address == MAP_FAILED) + throw exception("An occurred while mapping shared memory: {}", strerror(errno)); + memcpy(address, data.data(), std::min(size, kernel.size)); + kernel.address = reinterpret_cast(address); kernel.size = size; + } else { + throw exception("Cannot resize KSharedMemory that's only on guest"); } } void KSharedMemory::UpdatePermission(u64 address, u64 size, memory::Permission permission, bool host) { if (guest.valid() && !host) { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x2 = static_cast(permission.Get()); - fregs.x8 = __NR_mprotect; + Registers fregs{ + .x0 = address, + .x1 = size, + .x2 = static_cast(permission.Get()), + .x8 = __NR_mprotect, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while updating shared memory's permissions in guest"); @@ -92,16 +146,17 @@ namespace skyline::kernel::type { KSharedMemory::~KSharedMemory() { try { if (guest.valid() && state.process) { - Registers fregs{}; - fregs.x0 = guest.address; - fregs.x1 = guest.size; - fregs.x8 = __NR_munmap; + Registers fregs{ + .x0 = guest.address, + .x1 = guest.size, + .x8 = __NR_munmap, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); } - if (kernel.valid()) - munmap(reinterpret_cast(kernel.address), kernel.size); } catch (const std::exception &) { } + if (kernel.valid()) + munmap(reinterpret_cast(kernel.address), kernel.size); state.os->memory.DeleteChunk(guest.address); close(fd); } diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h index cfb9e9be..1f95c2e0 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h @@ -70,7 +70,6 @@ namespace skyline::kernel::type { UpdatePermission(address, size, permission, false); } - /** * @brief Updates the permissions of a chunk of mapped memory * @param permission The new permissions to be set for the memory diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp index 68597fd2..eca2d69a 100644 --- a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp @@ -5,6 +5,8 @@ namespace skyline::kernel::type { KTransferMemory::KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState) : host(host), size(size), KMemory(state, KType::KTransferMemory) { + if (address && !utils::PageAligned(address)) + throw exception("KTransferMemory was created with non-page-aligned address: 0x{:X}", address); BlockDescriptor block{ .size = size, .permission = permission, @@ -23,13 +25,14 @@ namespace skyline::kernel::type { chunk.blockList.front().address = address; hostChunk = chunk; } else { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x2 = static_cast(permission.Get()); - fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); - fregs.x4 = static_cast(-1); - fregs.x8 = __NR_mmap; + Registers fregs{ + .x0 = address, + .x1 = size, + .x2 = static_cast(permission.Get()), + .x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)), + .x4 = static_cast(-1), + .x8 = __NR_mmap, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while mapping shared region in child process"); @@ -41,6 +44,8 @@ namespace skyline::kernel::type { } u64 KTransferMemory::Transfer(bool mHost, u64 nAddress, u64 nSize) { + if (nAddress && !utils::PageAligned(nAddress)) + throw exception("KTransferMemory was transferred to a non-page-aligned address: 0x{:X}", nAddress); nSize = nSize ? nSize : size; ChunkDescriptor chunk = host ? hostChunk : *state.os->memory.GetChunk(address); chunk.address = nAddress; @@ -49,13 +54,14 @@ namespace skyline::kernel::type { for (auto &block : chunk.blockList) { block.address = nAddress + (block.address - address); if ((mHost && !host) || (!mHost && !host)) { - Registers fregs{}; - fregs.x0 = block.address; - fregs.x1 = block.size; - fregs.x2 = (block.permission.w) ? static_cast(block.permission.Get()) : (PROT_READ | PROT_WRITE); - fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0)); - fregs.x4 = static_cast(-1); - fregs.x8 = __NR_mmap; + Registers fregs{ + .x0 = block.address, + .x1 = block.size, + .x2 = (block.permission.w) ? static_cast(block.permission.Get()) : (PROT_READ | PROT_WRITE), + .x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0)), + .x4 = static_cast(-1), + .x8 = __NR_mmap, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while mapping transfer memory in child process"); @@ -80,11 +86,12 @@ namespace skyline::kernel::type { if (mprotect(reinterpret_cast(block.address), block.size, block.permission.Get()) == reinterpret_cast(MAP_FAILED)) throw exception("An error occurred while remapping transfer memory: {}", strerror(errno)); } else { - Registers fregs{}; - fregs.x0 = block.address; - fregs.x1 = block.size; - fregs.x2 = static_cast(block.permission.Get()); - fregs.x8 = __NR_mprotect; + Registers fregs{ + .x0 = block.address, + .x1 = block.size, + .x2 = static_cast(block.permission.Get()), + .x8 = __NR_mprotect, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while updating transfer memory's permissions in guest"); @@ -103,10 +110,11 @@ namespace skyline::kernel::type { state.os->memory.InsertChunk(chunk); } if ((mHost && !host) || (!mHost && !host)) { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x8 = __NR_munmap; + Registers fregs{ + .x0 = address, + .x1 = size, + .x8 = __NR_munmap, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while unmapping transfer memory in child process"); @@ -125,11 +133,12 @@ namespace skyline::kernel::type { if (mremap(reinterpret_cast(address), size, nSize, 0) == MAP_FAILED) throw exception("An error occurred while remapping transfer memory in host: {}", strerror(errno)); } else { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x2 = nSize; - fregs.x8 = __NR_mremap; + Registers fregs{ + .x0 = address, + .x1 = size, + .x2 = nSize, + .x8 = __NR_mremap, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while remapping transfer memory in guest"); @@ -150,11 +159,12 @@ namespace skyline::kernel::type { throw exception("An occurred while remapping transfer memory: {}", strerror(errno)); MemoryManager::InsertBlock(&hostChunk, block); } else { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x2 = static_cast(permission.Get()); - fregs.x8 = __NR_mprotect; + Registers fregs{ + .x0 = address, + .x1 = size, + .x2 = static_cast(permission.Get()), + .x8 = __NR_mprotect, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while updating transfer memory's permissions in guest"); @@ -168,10 +178,11 @@ namespace skyline::kernel::type { munmap(reinterpret_cast(address), size); else if (state.process) { try { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x8 = __NR_munmap; + Registers fregs{ + .x0 = address, + .x1 = size, + .x8 = __NR_munmap, + }; state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.os->memory.DeleteChunk(address); } catch (const std::exception &) {