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]>
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]>
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]>
The checked release tested that the range was allocated, released the pool lock and then
unmapped a snapshot of its views. Two concurrent checked releases of one range both passed the
test and took the same snapshot; the second then unmapped views the first had already removed
and terminated on "unmap address is not registered" instead of returning ENOENT.
A release now claims its pages in the pool under the pool lock before it looks at the views,
and frees them, dropping the claim, after the unmap. A checked release of pages another release
has claimed returns ENOENT as if they were already free; a plain release waits for that release
to finish. The pages stay allocated while claimed, so they still cannot be reallocated during
the unmap's lease wait.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A shared mapping is a view of a file section created on the descriptor's
handle, read-write when the descriptor is, otherwise read-only. A private
mapping is a copy of the file read at mmap, as POSIX allows. Anonymous
shm_open objects and JIT shared memory descriptors are temporary files on
Windows, so mmap now maps them too (it failed with EOPNOTSUPP before).
mprotect of a mapped view above the view's protection fails with EACCES
before any bookkeeping changes. The cases Windows cannot represent are
listed in the libkernel debt.
Dusklight (PPSA99305) uses SQLite in WAL mode, which maps its -shm file.
On Windows main f0634088b every start logged "Failed to start
transaction: disk I/O error" for its Dawn and pipeline caches (3 of 3
runs); with this change the caches initialise and their WAL files fill
(4 of 4 runs). The title reaches its menu either way.
A shared mapping may end inside the 16 KiB page that holds the end of
the file, as POSIX mmap allows: the view ends with the file's last 4 KiB
host page, which Windows zero-fills past the end, the rest of the guest
page is private zero pages, and the file never grows. A mapping that
reaches a whole page past the end still fails with EOPNOTSUPP.
PS5LM Plus (PPSA99581) loads its model with llama.cpp, which maps the
whole GGUF file shared and read-only. With a 491,400,032-byte model,
Windows main 5de7a46e0 and this change without the last page logged
"llama_model_load: error loading model: mmap failed: Operation not
supported" (3 of 3 runs); now the model loads and the completion of
"The capital of France is" starts with " Paris" (3 of 3 runs).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
sceKernelReleaseDirectMemory and sceKernelCheckedReleaseDirectMemory returned the range to the
allocator before unmapping its views. The unmap waits for GPU leases on the views, and during
that wait another allocation could take the freed pages and map them while the old view was
still mapped and host-imported, so GPU writes through it landed in the new owner's memory.
Both calls now unmap the views first; the checked call still reports ENOENT for an unallocated
range before it touches any view.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Retain the regression originally supplied by Pipodi in closed PR5851 (22091410a), adapted here through e1c6af65b. Current-main-equivalent libc/kernel source returns an alias during the open HostWrite and fails this assertion five times. This test is a reproduction, not a new implementation or an upstream PR.
(cherry picked from commit 312170cd0c461104174d655911de876be341cf6a)
guest_memory reserves with a hint above the guest arena and with a hint
on the arena's last page for two pages, and checks that both land inside
the arena, the second below its hint.