mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +02:00
memory: preserve live POSIX mappings during partial unmap
Co-authored-by: Ali Almohaya <AliAlmohaya@gmail.com>
This commit is contained in:
@@ -346,7 +346,9 @@ public:
|
|||||||
auto entry = std::prev(next);
|
auto entry = std::prev(next);
|
||||||
if (vaddr + size <= entry->second.vaddr + entry->second.size) {
|
if (vaddr + size <= entry->second.vaddr + entry->second.size) {
|
||||||
old = entry->second;
|
old = entry->second;
|
||||||
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
m_maps.erase(entry);
|
m_maps.erase(entry);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,6 +357,12 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint64_t left_size = vaddr - old.vaddr;
|
||||||
|
const uint64_t right_addr = vaddr + size;
|
||||||
|
const uint64_t old_end = old.vaddr + old.size;
|
||||||
|
const uint64_t right_size = old_end - right_addr;
|
||||||
|
|
||||||
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
const bool exact_unmap = (vaddr == old.vaddr && size == old.size);
|
const bool exact_unmap = (vaddr == old.vaddr && size == old.size);
|
||||||
if (exact_unmap) {
|
if (exact_unmap) {
|
||||||
const bool unmapped =
|
const bool unmapped =
|
||||||
@@ -370,12 +378,6 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64_t left_size = vaddr - old.vaddr;
|
|
||||||
const uint64_t right_addr = vaddr + size;
|
|
||||||
const uint64_t old_end = old.vaddr + old.size;
|
|
||||||
const uint64_t right_size = old_end - right_addr;
|
|
||||||
|
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
|
||||||
if (UnmapViewPreservePlaceholder(reinterpret_cast<void*>(old.vaddr), old.size)) {
|
if (UnmapViewPreservePlaceholder(reinterpret_cast<void*>(old.vaddr), old.size)) {
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
if (left_size != 0 && left_size != old.size) {
|
if (left_size != 0 && left_size != old.size) {
|
||||||
@@ -418,39 +420,28 @@ public:
|
|||||||
m_maps.emplace(old.vaddr, old);
|
m_maps.emplace(old.vaddr, old);
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
if (!UnmapViewPreservePlaceholder(reinterpret_cast<void*>(old.vaddr), old.size)) {
|
// Replace only the requested range so concurrent accesses and GPU watch
|
||||||
std::lock_guard lock(m_mutex);
|
// protections on the surviving mappings remain intact.
|
||||||
m_maps.emplace(old.vaddr, old);
|
if (!UnmapViewPreservePlaceholder(reinterpret_cast<void*>(vaddr), size)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = true;
|
std::lock_guard lock(m_mutex);
|
||||||
|
m_maps.erase(old.vaddr);
|
||||||
if (left_size != 0) {
|
if (left_size != 0) {
|
||||||
ok = MapExistingPlaceholderFixed(old.vaddr, left_size, old.backing_offset, old.mode) &&
|
m_maps.emplace(old.vaddr,
|
||||||
ok;
|
MapEntry {old.vaddr, left_size, old.backing_offset, old.mode});
|
||||||
}
|
}
|
||||||
if (right_size != 0) {
|
if (right_size != 0) {
|
||||||
ok = MapExistingPlaceholderFixed(right_addr, right_size,
|
m_maps.emplace(right_addr,
|
||||||
old.backing_offset + (right_addr - old.vaddr),
|
MapEntry {right_addr, right_size,
|
||||||
old.mode) &&
|
old.backing_offset + right_addr - old.vaddr, old.mode});
|
||||||
ok;
|
|
||||||
}
|
|
||||||
if (!ok) {
|
|
||||||
if (left_size != 0 && Contains(old.vaddr, left_size)) {
|
|
||||||
EXIT_IF(!UnmapOne(old.vaddr, left_size));
|
|
||||||
}
|
|
||||||
if (right_size != 0 && Contains(right_addr, right_size)) {
|
|
||||||
EXIT_IF(!UnmapOne(right_addr, right_size));
|
|
||||||
}
|
|
||||||
EXIT_IF(
|
|
||||||
!MapExistingPlaceholderFixed(old.vaddr, old.size, old.backing_offset, old.mode));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placeholder_preserved != nullptr) {
|
if (placeholder_preserved != nullptr) {
|
||||||
*placeholder_preserved = true;
|
*placeholder_preserved = true;
|
||||||
}
|
}
|
||||||
return ok;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,11 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
#ifndef NOMINMAX
|
#ifndef NOMINMAX
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
@@ -1163,6 +1168,43 @@ void TestDirectPartialProtectUnmapPreservesNeighbors() {
|
|||||||
std::printf("[host] %-48s ok\n", test);
|
std::printf("[host] %-48s ok\n", test);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
void TestPartialUnmapPreservesHostPermissions() {
|
||||||
|
const char* test = "PartialUnmapPreservesHostPermissions";
|
||||||
|
const auto base = MapNamedFlexible(test, SceKernelPageSize * 3, SceKernelProtCpuRw,
|
||||||
|
"unmap_host_permissions");
|
||||||
|
using Common::VirtualMemory::Mode;
|
||||||
|
Check(test, Libs::LibKernel::Memory::ProtectGuestHostMemory(base, SceKernelPageSize, Mode::Read),
|
||||||
|
"could not protect the left survivor from writes");
|
||||||
|
Check(test,
|
||||||
|
Libs::LibKernel::Memory::ProtectGuestHostMemory(base + SceKernelPageSize * 2,
|
||||||
|
SceKernelPageSize, Mode::NoAccess),
|
||||||
|
"could not protect the right survivor from reads");
|
||||||
|
CheckOk(test,
|
||||||
|
Libs::LibKernel::Memory::KernelMunmap(base + SceKernelPageSize, SceKernelPageSize),
|
||||||
|
"KernelMunmap(middle)");
|
||||||
|
|
||||||
|
// Probe actual host access without taking a signal or changing the page protections.
|
||||||
|
uint64_t value = 0;
|
||||||
|
iovec local {&value, sizeof(value)};
|
||||||
|
iovec left {reinterpret_cast<void*>(base), sizeof(value)};
|
||||||
|
iovec right {reinterpret_cast<void*>(base + SceKernelPageSize * 2), sizeof(value)};
|
||||||
|
Check(test, process_vm_readv(getpid(), &local, 1, &left, 1, 0) == sizeof(value),
|
||||||
|
"partial unmap removed read access to the left survivor");
|
||||||
|
Check(test, process_vm_writev(getpid(), &local, 1, &left, 1, 0) == -1,
|
||||||
|
"partial unmap removed the left survivor's write protection");
|
||||||
|
Check(test, process_vm_readv(getpid(), &local, 1, &right, 1, 0) == -1,
|
||||||
|
"partial unmap removed the right survivor's read protection");
|
||||||
|
|
||||||
|
CheckOk(test, Libs::LibKernel::Memory::KernelMunmap(base, SceKernelPageSize),
|
||||||
|
"KernelMunmap(left cleanup)");
|
||||||
|
CheckOk(test,
|
||||||
|
Libs::LibKernel::Memory::KernelMunmap(base + SceKernelPageSize * 2, SceKernelPageSize),
|
||||||
|
"KernelMunmap(right cleanup)");
|
||||||
|
std::printf("[host] %-48s ok\n", test);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void TestDirectMapValidationBeforeOwnerMutation() {
|
void TestDirectMapValidationBeforeOwnerMutation() {
|
||||||
const char* test = "DirectMapValidationBeforeOwnerMutation";
|
const char* test = "DirectMapValidationBeforeOwnerMutation";
|
||||||
int64_t invalid = -1;
|
int64_t invalid = -1;
|
||||||
@@ -2529,6 +2571,9 @@ int main(int argc, char** argv) {
|
|||||||
RunTest(TestMunmapAcrossAdjacentFlexibleMappings);
|
RunTest(TestMunmapAcrossAdjacentFlexibleMappings);
|
||||||
RunTest(TestDirectMapQueryOffsetAndPartialMunmap);
|
RunTest(TestDirectMapQueryOffsetAndPartialMunmap);
|
||||||
RunTest(TestDirectPartialProtectUnmapPreservesNeighbors);
|
RunTest(TestDirectPartialProtectUnmapPreservesNeighbors);
|
||||||
|
#if defined(__linux__)
|
||||||
|
RunTest(TestPartialUnmapPreservesHostPermissions);
|
||||||
|
#endif
|
||||||
RunTest(TestDirectMapValidationBeforeOwnerMutation);
|
RunTest(TestDirectMapValidationBeforeOwnerMutation);
|
||||||
RunTest(TestDirectReleaseRollbackRestoresOwnerMapping);
|
RunTest(TestDirectReleaseRollbackRestoresOwnerMapping);
|
||||||
RunTest(TestDirectReleaseContracts);
|
RunTest(TestDirectReleaseContracts);
|
||||||
|
|||||||
Reference in New Issue
Block a user