From 9d96752fcd8ec333335d2517a3e2ccb0e7e1fb5e Mon Sep 17 00:00:00 2001 From: fdfawef324 <287205748+fdfawef324@users.noreply.github.com> Date: Sun, 11 Oct 2026 02:31:11 -0400 Subject: [PATCH] fix(libkernel): let munmap succeed over a range with nothing mapped munmap and sceKernelMunmap over a range in which no page is mapped failed: munmap returned EINVAL and sceKernelMunmap let the registry's exception escape. Both now return 0. The console returns 0 for sceKernelMunmap of a range it just unmapped and of a never-mapped address, and EINVAL only for a zero length, as measured by prosper's console oracle (kernelx.golden.tsv, kx_fmem_unmap_again, kx_fmem_munmap_unmapped). The other half of #3196, a range with an unmapped page in the middle, is already fixed on main by 04ed61957; the test covers both. Co-Authored-By: Claude Opus 5.5 --- .../libkernel/DirectMemory/DirectMemory.cpp | 1 + core/libs/tests/GuestMemory.cpp | 25 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp b/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp index 1fae6ec14..4f261f154 100644 --- a/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp +++ b/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp @@ -923,6 +923,7 @@ int DoMunmap(void* addr, size_t len) { Trace("unmap %p+0x%zx", addr, len); if (len == 0 || (len & (PS5_PAGE_SIZE - 1)) != 0 || !addr) return SCE_KERNEL_ERROR_EINVAL; GuestAllocations::Mutation mutation; + if (!mutation.Overlaps(addr, len)) return 0; UnmapRegistered(mutation, addr, len); return 0; } diff --git a/core/libs/tests/GuestMemory.cpp b/core/libs/tests/GuestMemory.cpp index bfab22098..4897c7008 100644 --- a/core/libs/tests/GuestMemory.cpp +++ b/core/libs/tests/GuestMemory.cpp @@ -1672,6 +1672,28 @@ static void CheckAvailableDirectMemoryIsLargestFreeBlock() { Require(start == top && size == static_cast(window)); } +static void CheckMunmapOverUnmappedPages() { + constexpr std::size_t page = 0x4000; + const auto failed = reinterpret_cast(static_cast(-1)); + void* raw = mmap_nid_postfix(nullptr, page * 3, 3, 0x1002, -1, 0); + Require(raw != failed); + auto* memory = static_cast(raw); + memory[0] = 1; + memory[page * 2] = 2; + Require(munmap_nid_postfix(memory + page, page) == 0); + Require(munmap_nid_postfix(memory, page * 3) == 0); + { + GuestAllocations::Mutation mutation; + Require(!mutation.Overlaps(memory, page * 3)); + } + VirtualQueryInfo info{}; + Require(sceKernelVirtualQuery(memory, 0, &info, sizeof(info)) == SCE_KERNEL_ERROR_EACCES); + Require(sceKernelVirtualQuery(memory + page * 2, 0, &info, sizeof(info)) == SCE_KERNEL_ERROR_EACCES); + Require(munmap_nid_postfix(memory, page * 3) == 0); + Require(sceKernelMunmap(memory, page * 3) == 0); + Require(sceKernelMunmap(memory, 0) == SCE_KERNEL_ERROR_EINVAL); +} + int main() { #ifdef _WIN32 _putenv_s("APS5_PIN_WAIT_MS", "1000"); @@ -1681,6 +1703,7 @@ int main() { CheckVirtualQueryForNonReadableGuestRange(); CheckReleaseFlexibleMemory(); CheckVirtualQueryPartialMunmap(); + CheckMunmapOverUnmappedPages(); CheckNamedAndHintedMappings(); CheckInternalNamedFlexibleMapping(); CheckBatchMapStopsAtInvalidEntry(); @@ -1793,7 +1816,7 @@ int main() { Require(munmap_nid_postfix(memory, page) == 0); Require(memory[page * 2] == 73); Require(munmap_nid_postfix(memory + page * 2, page) == 0); - Require(munmap_nid_postfix(memory, page) == -1); + Require(munmap_nid_postfix(memory, page) == 0); Require(madvise_nid_postfix(memory, page, 4) == 0); *__error_nid_postfix() = 0; Require(mprotect_nid_postfix(memory, page, 1) == -1 && *__error_nid_postfix() == 22);