diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 433d490e42..c9d74c1db7 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -187,7 +187,8 @@ - [sceKernelMapDirectMemory with execute](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) - a protection with CPU execute (bit 0x4) returns `EINVAL` (0x80020016) and maps nothing, and `sceKernelMprotect` grants execute afterwards, as [PS5_PayloadSDK measured](https://github.com/mihawk-99/PS5_PayloadSDK/blob/3e31e0b1b68ada28beca68adc480cf0c3d17b9df/platform/docs/PROBE.md#L28-L34) in a title on firmware 12.09 for read-execute and read-write-execute. `sceKernelMapDirectMemory2`, `sceKernelMapNamedDirectMemory`, and `sceKernelBatchMap` and `sceKernelBatchMap2` with operation 0 share the check here, which is not measured for them, nor for a title with more privileges than a homebrew one. Of 40 `sceKernelMapDirectMemory` calls in ten titles checked, 35 pass a constant protection without execute and five read it from a register or field (one each in PPSA01494, PPSA21564 and PPSA30839, two in PPSA18194). - [sceKernelMapDirectMemory over unallocated pages](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) - mapping a physical range that includes a page `sceKernelAllocateDirectMemory` has not allocated returns `ENOMEM` (0x8002000c) and maps nothing, as [shadPS4's `MemoryManager::MapMemory`](https://github.com/shadps4-emu/shadPS4/blob/82eb7cdf640990d2f5a2a3fa03b489f2b287b17e/src/core/memory.cpp#L543-L551) does; `sceKernelMapDirectMemory2`, `sceKernelMapNamedDirectMemory` and the map operation of `sceKernelBatchMap` go through the same check. The console's result is not measured. - [setitimer](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) follows FreeBSD's `sys_setitimer` and `kern_setitimer`: `EINVAL` for timers other than `ITIMER_REAL`, `ITIMER_VIRTUAL` and `ITIMER_PROF`, and for a value, or the interval of a non-zero value, with a negative field, `tv_usec` of 1000000 or more, or more than `INT32_MAX / 2` seconds; a null value reads the timer as `getitimer` does, with `EFAULT` when the output is null too. `ITIMER_REAL` shares the timer thread of `alarm`, which clears its interval: a zero value disarms it, the previous value is returned, and each expiry re-arms it by `it_interval`. With `SIG_IGN` an expiry is dropped. With `SIGALRM` blocked it is left on the pending set of `sigtimedwait`, as `kern_psignal` does, where `sigwait`, `sigtimedwait` and `sigwaitinfo` take it; unblocking it runs a guest handler on the unblocking thread, and with the default disposition exits the process with status 142. Unblocked, the default disposition exits the process with status 142 and a handler throws, since guest `SIGALRM` handlers are never run from the timer thread. The value is not raised to `tick / 16` as `itimerfix` does, and an interval below 1 ms throws, since the console's `hz`, and so that rounding, is unknown (1 ms covers any `hz` of 63 or more). `ITIMER_VIRTUAL` and `ITIMER_PROF` throw when armed, as process CPU time is not accounted, and read and disarm as zero. PPSA99420 imports it and fails to load without it -- [pthread_getaffinity_np](../../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel) - a set of 17 to 23 bytes throws, since the console's answer is not measured. As [rooklz measured](https://github.com/boykopovar/AnyPS5/pull/9156#issuecomment-6391607043) on a PS5, matching [PS5_PayloadSDK](https://github.com/mihawk-99/PS5_PayloadSDK/blob/3e31e0b1b68ada28beca68adc480cf0c3d17b9df/platform/docs/PROBE.md#L287-L297), a set of 8 to 16 bytes is answered and zero-filled past byte 8, and one below 8 bytes or from 24 returns `ERANGE`. `pthread_setaffinity_np` keeps FreeBSD's unmeasured 32-byte limit, and `cpuset_getaffinity` and `cpuset_setaffinity` still accept up to 32 bytes, where the console returns `ERANGE` from 24. A non-null mask pointer that cannot be read or written returns `EFAULT` (`-1` with errno 14 for `cpuset_*`, 14 for `pthread_*_np`) as a host-safety fallback; the console's answer for a non-null invalid pointer was not measured +- [pthread_getaffinity_np](../../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel) - a set of 17 to 23 bytes throws, since the console's answer is not measured. As [rooklz measured](https://github.com/boykopovar/AnyPS5/pull/9156#issuecomment-6391607043) on a PS5, matching [PS5_PayloadSDK](https://github.com/mihawk-99/PS5_PayloadSDK/blob/3e31e0b1b68ada28beca68adc480cf0c3d17b9df/platform/docs/PROBE.md#L287-L297), a set of 8 to 16 bytes is answered and zero-filled past byte 8, and one below 8 bytes or from 24 returns `ERANGE`. A non-null mask pointer that cannot be read or written returns `EFAULT` (`-1` with errno 14 for `cpuset_*`, 14 for `pthread_*_np`) as a host-safety fallback; the console's answer for a non-null invalid pointer was not measured +- [pthread_setaffinity_np, cpuset_getaffinity and cpuset_setaffinity](../../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel) - their set sizes still follow FreeBSD's 32-byte `cpuset_t`, not the console: `pthread_setaffinity_np` takes only 32 bytes, where [the console](https://github.com/boykopovar/AnyPS5/pull/7135#issuecomment-6390891591) took 8 and 16 and returned `ERANGE` for 32, and `cpuset_getaffinity` and `cpuset_setaffinity` accept up to 32 bytes, where [the console](https://github.com/boykopovar/AnyPS5/pull/9156#issuecomment-6391607043) returns `ERANGE` from 24 - On Linux, [direct memory](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) lives in one sparse memfd at file offset = physical address, so the [GPU imports](../../../core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp) can share one udmabuf buffer object per 64 MiB of it. Released direct memory is not punched out of the file, because a live import of its chunk pins the pages and the GPU would keep reading the old ones: its pages stay committed (the process keeps the high-water mark of the direct memory it used) and are zeroed when the range is allocated again. - [system](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) runs no command. It follows FreeBSD 11's `lib/libc/stdlib/system.c` for a sandbox without `/bin/sh`: a null command returns 1 unconditionally, as that version does, and any command returns the wait status of a child that could not exec the shell and called `_exit(127)` (`0x7f00`). Whether the console's application sandbox has no shell, and whether `vfork` succeeds there (FreeBSD returns -1 when it fails), is unverified - [sceKernelWriteThrottlingStatus](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) reports no write throttling: the host SSD has no write budget, so the first qword of the 32-byte status is set to `UINT32_MAX` and the rest is zeroed; an unwritable or null status buffer pointer returns `EFAULT` (`0x8002000e`) as a host-safety fallback, and console behaviour for unwritable memory has not been measured. PPSA17221 reads only that qword: it multiplies it by 64 KiB and compares the product with byte thresholds up to 0x4AF00000 to pick one of five tiers. When the call fails the title assumes 0x4B000001 bytes, which selects the same top tier as `UINT32_MAX`. What the value measures on hardware (a remaining budget or a rate) and the meaning of the other three qwords are unknown