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 <noreply@anthropic.com>
This commit is contained in:
fdfawef324 2026-10-11 02:31:11 -04:00 committed by Arekatla Nishanth Chowdary
parent a11c8f7f70
commit 9d96752fcd
2 changed files with 25 additions and 1 deletions

View File

@ -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;
}

View File

@ -1672,6 +1672,28 @@ static void CheckAvailableDirectMemoryIsLargestFreeBlock() {
Require(start == top && size == static_cast<std::size_t>(window));
}
static void CheckMunmapOverUnmappedPages() {
constexpr std::size_t page = 0x4000;
const auto failed = reinterpret_cast<void*>(static_cast<std::uintptr_t>(-1));
void* raw = mmap_nid_postfix(nullptr, page * 3, 3, 0x1002, -1, 0);
Require(raw != failed);
auto* memory = static_cast<unsigned char*>(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);