fix(libc): keep resident mappings to direct memory backings on Windows
Map made every singly mapped section page private resident memory. A JIT or shm_open section is file backed and committed: its bytes live in the file, and its alias descriptors map duplicated handles, which the physical page slots do not match, so a resident copy diverged from the file, and committing its view on a demote failed with access denied (guest_jit_shared_memory). Only direct memory backings, mapped through the new GuestArenaMapDirect, now become resident; other sections keep main's shared views. Shared pages are committed only while their section page is reserved, as on main. The socket host write tests map their direct memory a second time, so the write-protected shared path they check is still the one they exercise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
This commit is contained in:
parent
d9852df2a2
commit
71da166a18
|
|
@ -41,6 +41,7 @@ bool GuestArenaHostRegionOverlaps_nid_postfix(std::uintptr_t address, std::size_
|
|||
std::uint64_t GuestArenaCommitGeneration_nid_postfix();
|
||||
void GuestArenaSetPrivateMappingObserver_nid_postfix(void (*callback)(std::uintptr_t address, std::size_t bytes, std::uint64_t generation));
|
||||
void GuestArenaMap_nid_postfix(void* pointer, std::size_t bytes, void* section, std::uint64_t offset, std::uint32_t protection);
|
||||
void GuestArenaMapDirect_nid_postfix(void* pointer, std::size_t bytes, void* section, std::uint64_t offset, std::uint32_t protection);
|
||||
void* GuestArenaMapAlias_nid_postfix(std::uintptr_t address, std::size_t bytes);
|
||||
void GuestArenaUnmapAlias_nid_postfix(void* alias);
|
||||
bool GuestArenaResidentSpan_nid_postfix(std::uintptr_t address, std::size_t bytes, std::uintptr_t* start, std::uintptr_t* end);
|
||||
|
|
|
|||
|
|
@ -75,11 +75,11 @@ public:
|
|||
reset(reinterpret_cast<std::uintptr_t>(address), bytes);
|
||||
}
|
||||
|
||||
void Map(void* address, std::size_t bytes, HANDLE section, std::uint64_t offset, DWORD protection, bool watched) {
|
||||
void Map(void* address, std::size_t bytes, HANDLE section, std::uint64_t offset, DWORD protection, bool watched = false, bool resident = false) {
|
||||
std::lock_guard lock(mutex);
|
||||
auto cursor = reinterpret_cast<std::uintptr_t>(address);
|
||||
reset(cursor, bytes);
|
||||
watchResident = watched;
|
||||
if (resident) watchResident = watched;
|
||||
HANDLE duplicate = nullptr;
|
||||
if (!DuplicateHandle(GetCurrentProcess(), section, GetCurrentProcess(), &duplicate, 0, FALSE, DUPLICATE_SAME_ACCESS)) fail("keep shared guest section");
|
||||
const auto owned = std::make_shared<Section>(duplicate);
|
||||
|
|
@ -95,7 +95,7 @@ public:
|
|||
const auto single = [&](std::size_t done) {
|
||||
const auto& slot = slotOf(done);
|
||||
const auto shared = slot.page.lock();
|
||||
return !slot.stored && (!shared || shared->aliases.empty());
|
||||
return resident && !slot.stored && (!shared || shared->aliases.empty());
|
||||
};
|
||||
for (std::size_t done = 0; done < bytes;) {
|
||||
const auto base = cursor + done;
|
||||
|
|
@ -589,7 +589,7 @@ private:
|
|||
void mapShared(std::uintptr_t base, HANDLE section, std::uint64_t offset, DWORD protection) {
|
||||
void* page = reinterpret_cast<void*>(base);
|
||||
if (!map(section, GetCurrentProcess(), page, offset, pageBytes, MEM_REPLACE_PLACEHOLDER, PAGE_EXECUTE_READWRITE, nullptr, 0)) fail("map shared guest page");
|
||||
if (!VirtualAlloc(page, pageBytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) fail("commit shared guest page");
|
||||
if (query(base).State == MEM_RESERVE && !VirtualAlloc(page, pageBytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) fail("commit shared guest page");
|
||||
DWORD previous;
|
||||
if (!VirtualProtect(page, pageBytes, protection, &previous)) fail("protect shared guest page");
|
||||
}
|
||||
|
|
@ -610,7 +610,7 @@ private:
|
|||
if (!VirtualProtect(reinterpret_cast<void*>(start), stop - start, PAGE_READONLY, &previous)) fail("fence resident guest pages");
|
||||
const auto section = views.at(start).section;
|
||||
auto window = open(section->handle, views.at(start).offset, stop - start);
|
||||
if (!VirtualAlloc(window.bytes, stop - start, MEM_COMMIT, PAGE_READWRITE)) fail("commit direct memory pages");
|
||||
if (query(reinterpret_cast<std::uintptr_t>(window.bytes)).State == MEM_RESERVE && !VirtualAlloc(window.bytes, stop - start, MEM_COMMIT, PAGE_READWRITE)) fail("commit direct memory pages");
|
||||
std::memcpy(window.bytes, reinterpret_cast<const void*>(start), stop - start);
|
||||
close(window);
|
||||
if (!VirtualFree(reinterpret_cast<void*>(start), stop - start, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) fail("release resident guest pages");
|
||||
|
|
|
|||
|
|
@ -378,7 +378,12 @@ void GuestArenaReset_nid_postfix(void* pointer, std::size_t bytes) {
|
|||
|
||||
void GuestArenaMap_nid_postfix(void* pointer, std::size_t bytes, void* section, std::uint64_t offset, std::uint32_t protection) {
|
||||
if (!Arena::Get().Contains(pointer, bytes)) throw OutsideArena("shared mapping", pointer, bytes);
|
||||
WindowsMappings::Get().Map(pointer, bytes, section, offset, protection, Arena::Get().WriteWatched());
|
||||
WindowsMappings::Get().Map(pointer, bytes, section, offset, protection, Arena::Get().WriteWatched(), false);
|
||||
}
|
||||
|
||||
void GuestArenaMapDirect_nid_postfix(void* pointer, std::size_t bytes, void* section, std::uint64_t offset, std::uint32_t protection) {
|
||||
if (!Arena::Get().Contains(pointer, bytes)) throw OutsideArena("direct memory mapping", pointer, bytes);
|
||||
WindowsMappings::Get().Map(pointer, bytes, section, offset, protection, Arena::Get().WriteWatched(), true);
|
||||
}
|
||||
|
||||
void* GuestArenaMapAlias_nid_postfix(std::uintptr_t address, std::size_t bytes) {
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ public:
|
|||
|
||||
void Map(std::uintptr_t address, std::size_t bytes, std::uint64_t offset, int protection) const {
|
||||
#ifdef _WIN32
|
||||
GuestArena::GuestArenaMap_nid_postfix(reinterpret_cast<void*>(address), bytes, section, offset, WinProtFromPosix(protection));
|
||||
GuestArena::GuestArenaMapDirect_nid_postfix(reinterpret_cast<void*>(address), bytes, section, offset, WinProtFromPosix(protection));
|
||||
#else
|
||||
if (::mmap(reinterpret_cast<void*>(address), bytes, protection, MAP_SHARED | MAP_FIXED, file, static_cast<off_t>(fileOffset + offset)) == MAP_FAILED) throw std::system_error(errno, std::generic_category(), "map direct memory backing");
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -141,6 +141,8 @@ int main(int argc, char** argv) {
|
|||
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page * 2, 0, 0, &physical) == 0, "allocate direct memory");
|
||||
void* mapping = nullptr;
|
||||
Require(sceKernelMapDirectMemory(&mapping, page * 2, 3, 0, physical, 0) == 0, "map read/write memory");
|
||||
void* twin = nullptr;
|
||||
Require(sceKernelMapDirectMemory(&twin, page * 2, 3, 0, physical, 0) == 0, "map read/write memory a second time");
|
||||
auto* buffer = static_cast<char*>(mapping) + page - 7;
|
||||
const auto collect = [&] {
|
||||
std::array<void*, 16> pages{};
|
||||
|
|
@ -199,6 +201,9 @@ int main(int argc, char** argv) {
|
|||
void* outputMapping = nullptr;
|
||||
Require(sceKernelMapDirectMemory(&outputMapping, page * 2, 3, 0, outputPhysical, 0) == 0,
|
||||
"map sender output memory");
|
||||
void* outputTwin = nullptr;
|
||||
Require(sceKernelMapDirectMemory(&outputTwin, page * 2, 3, 0, outputPhysical, 0) == 0,
|
||||
"map sender output memory a second time");
|
||||
const auto collectOutputs = [&] {
|
||||
std::array<void*, 16> pages{};
|
||||
std::size_t count = pages.size();
|
||||
|
|
@ -238,6 +243,7 @@ int main(int argc, char** argv) {
|
|||
if (mask & 1) Require(Protection(address) == PAGE_READONLY, "sender address rearmed");
|
||||
if (mask & 2) Require(Protection(length) == PAGE_READONLY, "sender length rearmed");
|
||||
}
|
||||
Require(sceKernelMunmap(outputTwin, page * 2) == 0, "unmap the second sender output mapping");
|
||||
Require(sceKernelMunmap(outputMapping, page * 2) == 0, "unmap sender outputs");
|
||||
Require(sceKernelReleaseDirectMemory(outputPhysical, page * 2) == 0, "release sender output memory");
|
||||
}
|
||||
|
|
@ -300,6 +306,7 @@ int main(int argc, char** argv) {
|
|||
Require(Protection(buffer) == PAGE_READONLY, "completed receive can be rearmed");
|
||||
Require((net ? sceNetSocketClose(sender) : close_nid_postfix(sender)) == 0, "close sender");
|
||||
Require((net ? sceNetSocketClose(receiver) : close_nid_postfix(receiver)) == 0, "close receiver");
|
||||
Require(sceKernelMunmap(twin, page * 2) == 0, "unmap the second direct memory mapping");
|
||||
Require(sceKernelMunmap(mapping, page * 2) == 0, "unmap direct memory");
|
||||
Require(sceKernelReleaseDirectMemory(physical, page * 2) == 0, "release direct memory");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ struct Region {
|
|||
|
||||
Region() {
|
||||
Require(section && first && alias, "cannot allocate shared test memory");
|
||||
Mappings::Get().Map(first, Bytes, section, 0, PAGE_READWRITE, true);
|
||||
Mappings::Get().Map(alias, Bytes, section, 0, PAGE_READWRITE, true);
|
||||
Mappings::Get().Map(first, Bytes, section, 0, PAGE_READWRITE);
|
||||
Mappings::Get().Map(alias, Bytes, section, 0, PAGE_READWRITE);
|
||||
}
|
||||
~Region() {
|
||||
Mappings::Get().Reset(first, Bytes);
|
||||
|
|
@ -113,7 +113,7 @@ void CheckAliasesAndCapacity() {
|
|||
Require(region.Collect(Collection::Armed) == Pages(0, 1), "alias write was missed or fresh neighbors were consumed");
|
||||
Require(region.Collect(Collection::Fresh) == Pages(1, 16), "alias collection changed another view's history");
|
||||
Mappings::Get().Reset(region.first, Bytes);
|
||||
Mappings::Get().Map(region.first, Bytes, region.section, 0, PAGE_READWRITE, true);
|
||||
Mappings::Get().Map(region.first, Bytes, region.section, 0, PAGE_READWRITE);
|
||||
Require(region.Collect(Collection::Armed).empty(), "remapped address kept old collection history");
|
||||
Require(region.Collect(Collection::Fresh) == Pages(0, 16), "remapped memory was not fresh");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue