PS5_PayloadSDK measured in a title (firmware 12.09) that
sceKernelMapDirectMemory with read-execute or read-write-execute returns
0x80020016, and that sceKernelMprotect then grants execute to a
read-write mapping. The map granted execute directly.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The same-range/same-protection fast path in RemapFixedIntoRegistered
returned without touching host pages, but DoMprotect/DoMtypeprotect
change host protection without updating the recorded nativeProtection,
so a stale record could leave guest pages with the wrong host
permissions. The shortcut now reissues the per-block backing map with
records skipped, keeping the bookkeeping optimization while reapplying
the requested host protection.
CreateDirectMemoryBacking registered g_physBlocks before building the
per-page map, so a bad_alloc during page construction left an orphaned
physical block that poisoned later allocations; the block now publishes
after the page map is built.
CheckFixedRemapReprotectsAfterMprotect covers both shortcut directions
with stale records and verifies the host protection via VirtualQuery /
proc maps, and the existing remap test gains the current uint64 Munmap
call.
CheckMlock reads VmLck and expects ENOMEM from mlock of reserved and unmapped ranges. ThreadSanitizer, like AddressSanitizer, replaces mlock and munlock with interceptors that return 0 without locking anything, so skip the same checks that ANYPS5_ENABLE_ASAN already skips. The guest arena range check stays as is: a ThreadSanitizer build keeps the default range.
Prospero Win measured on firmware 12.02 that the PS5 kernel accepts
MAP_EXCL but ignores it: MAP_FIXED | MAP_EXCL over an existing mapping
replaces it without error. Fixed anonymous mmap now does the same instead
of FreeBSD's ENOMEM. MAP_EXCL without MAP_FIXED stays EINVAL.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CheckRemappedDirectMemoryIsChargedOnce compared the system-wide commit
total (K32GetPerformanceInfo) before and after mapping, unmapping and
remapping 256 MiB of direct memory, and required the growth to stay under
384 MiB in one of three attempts. Every other process on the host changes
that total, so guest_memory failed at GuestMemory.cpp:2164 whenever a
parallel test or a compiler committed and freed more than 128 MiB during
each attempt.
Measure the private and shared commit of the test process instead
(PrivateUsage and SharedCommitUsage of PROCESS_MEMORY_COUNTERS_EX2), which
covers both the private resident run and the direct memory section. The
remap grows it by 265 MiB on every run, so the attempts are dropped.
The unchecked release passed its whole range to ForgetDirectMemory,
which throws on any page that is not allocated, so releasing a range
that is partly or wholly free aborted the process. Release only the
allocated runs, as shadPS4's unchecked free does; the checked variant
still returns ENOENT for them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
munmap failed with EINVAL for an address that is not page aligned.
FreeBSD 11's sys_munmap rejects only a zero length and an address wrap;
it moves the address down to its page and grows the length by the same
offset before rounding it up, so the pages the range touches are
unmapped.
Resolve ImportMappedRanges onto the lazy lease from #7465: ranges of
0x10000 bytes or less still take no lease, the first larger range takes
the narrow lease over the mapped runs instead of a full one, and an import
that would replace another (with the GPU mutex held) re-acquires a full
lease. The adopting path keeps its full lease for refreshImports.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Measured on firmware 13.60: an address or length aligned to 4 KiB but
not to 16 KiB returns EINVAL.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Measured on firmware 13.60: the released range stays mapped with its
protection and contents, and a fixed no-overwrite map over it fails
with ENOMEM; a null or unaligned address, an unaligned length or a
range that wraps returns EINVAL, checked before a zero length, which
returns 0, as does a range with nothing mapped.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Measured on firmware 13.60: submits take 1 to 128 requests at priority
1 to 3, the id lists of poll, wait, cancel and delete take 1 to 128 ids,
wait takes mode 1 or 2, and sceKernelAioSetParam takes a window and a
delayed count of 0 to 128, a split flag of 0 or 1 and, with split on,
split and chunk sizes of 1 to 0x1000000; everything else returns EINVAL
without writing. sceKernelAioInitializeParam writes the console's three
default scheduling structs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A clean resident run was made read-only so that the next collect
could skip GetWriteWatch, and the first guest store into it was
resumed on the guest thread. The exception frame Windows builds for
that fault overwrites the System V red zone below rsp (#361), which
is what #5596 removed for singly mapped direct memory. Resident runs
are now never armed and are collected with GetWriteWatch only.
Refs #9136
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Measured on firmware 13.60: a start whose rounding to 16 KiB overflows
finds nothing, and of two equal free ranges the lowest is returned.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Measured on firmware 13.60: an alignment that is not zero or a power of
two returns EINVAL before anything else; bounds with nothing between
them return ENOMEM, a negative start searches from the bottom, only the
start is rounded up to 16 KiB and the end is used as given; null outputs
are skipped; errors leave both outputs untouched.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The title allocates, maps and releases about 48 direct-memory blocks per frame.
Each allocation created a pagefile-backed section and each release closed it.
A released section whose pages were never stored to (resident pages keep their
data in private memory and saved buffers, not in the section) is still blank,
so up to 256 such sections of at most 16 MiB, 256 MiB in total, are kept by
size and handed to the next allocation of that size instead of a new
CreateFileMappingW. APS5_NO_DIRECT_MEMORY_SECTION_POOL keeps the old behaviour.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FreeBSD 11.4 kern_mprotect masks the argument with VM_PROT_ALL instead of
refusing it (sys/vm/vm_mmap.c:613). sceKernelMprotect already does this
since #5770; the POSIX mprotect export still failed with EINVAL.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
FreeBSD 11.4 kern_mmap maps anonymous memory whenever MAP_ANON is set, with
or without MAP_PRIVATE (sys/vm/vm_mmap.c:339), and fails with EINVAL when
both MAP_SHARED and MAP_PRIVATE are set (vm_mmap.c:252). The guest failed
the first with ENOTSUP (45) and the second with ENOTSUP as well.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A search range with no free direct memory returned EAGAIN (0x80020023).
A PS5 returns ENOMEM (0x8002000c) for an occupied range, as the
PS5_RetroArch author's console capture records, and PS5_PayloadSDK's
ps5_memory_query, which RetroArch uses for its memory status, gives up
on any other error.
LargestFree now returns the error: ENOMEM when no block is found, and
EAGAIN as before for a negative start, an end at or below the start and
an alignment that overflows when rounded to a page. guest_memory expects
ENOMEM for an occupied range and EAGAIN for those arguments. The
unmeasured argument handling is recorded in the libkernel debt file.
Co-Authored-By: Claude Opus 5.5 <[email protected]>