fix(libc): leave resident runs under an open host write out of MapAlias

demote() fences a resident run read-only, copies it into the section and
replaces its pages with views. A host write between BeginHostWrite and
EndHostWrite runs outside the mapping lock, so one in flight on the run
could fail on the fence (an APR file read raised ios_base::failure) or land
in the private pages after the copy and be lost. MapAlias now returns null
without demoting when any page of a run it would demote has an open host
write; the AGC driver then imports the range without an alias, as before
this change. A blocking host write such as a socket receive must not stall
the import, so MapAlias does not wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Alex ColliniandClaude Opus 5.5 committed 2026-10-14 16:42:24 +02:00
1 parent 2d8de2d187
commit 22091410a6
2 files changed
+31

No files matched your search

@@ -256,6 +256,14 @@ public:
return std::runtime_error(text);
};
if (address % pageBytes != 0 || bytes % pageBytes != 0 || bytes == 0) throw refuse("the range is not made of whole shared pages");
for (std::size_t done = 0; done < bytes; done += pageBytes) {
const auto found = views.find(address + done);
if (found == views.end() || !found->second.resident) continue;
const auto run = std::prev(residentRuns.upper_bound(address + done));
for (auto page = views.find(run->first); page != views.end() && page->first < run->second.end; ++page) {
if (page->second.hostWrites != 0) return nullptr;
}
}
for (std::size_t done = 0; done < bytes; done += pageBytes) {
const auto found = views.find(address + done);
if (found != views.end() && found->second.resident) demote(address + done);
+23
View File
@@ -1189,6 +1189,28 @@ static void CheckAliasAcrossDirectAllocations() {
Require(sceKernelReleaseDirectMemory(first, page * 3) == 0);
}
static void CheckAliasLeavesResidentRunsUnderAHostWrite() {
constexpr std::size_t page = 0x4000;
std::int64_t phys = 0;
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page * 2, 0, 0, &phys) == 0);
void* mapped = nullptr;
Require(sceKernelMapDirectMemory(&mapped, page * 2, 3, 0, phys, 0) == 0);
auto* bytes = static_cast<volatile unsigned char*>(mapped);
bytes[5] = 0x11;
{
const GuestArena::HostWrite open(static_cast<unsigned char*>(mapped) + page, page);
Require(open.Open());
Require(GuestArena::GuestArenaMapAlias_nid_postfix(reinterpret_cast<std::uintptr_t>(mapped), page * 2) == nullptr);
bytes[page + 5] = 0x22;
}
auto* alias = static_cast<volatile unsigned char*>(GuestArena::GuestArenaMapAlias_nid_postfix(reinterpret_cast<std::uintptr_t>(mapped), page * 2));
Require(alias != nullptr && alias[5] == 0x11 && alias[page + 5] == 0x22);
GuestArena::GuestArenaUnmapAlias_nid_postfix(const_cast<unsigned char*>(alias));
Require(bytes[5] == 0x11 && bytes[page + 5] == 0x22);
Require(sceKernelMunmap(mapped, page * 2) == 0);
Require(sceKernelReleaseDirectMemory(phys, page * 2) == 0);
}
static void CheckAliasRefusesPagesThatAreNotSharedViews() {
constexpr std::size_t page = 0x4000;
std::int64_t phys = 0;
@@ -2574,6 +2596,7 @@ int main(int argc, char** argv) {
CheckAliasAcrossDirectAllocations();
CheckAliasOfReorderedSharedPages();
CheckAliasRefusesPagesThatAreNotSharedViews();
CheckAliasLeavesResidentRunsUnderAHostWrite();
#endif
CheckUnhintedKernelMappingsLandInTheWindow();
#if defined(__linux__)