pthread_cond_wait and pthread_cond_timedwait, and their sce counterparts, threw a
host exception when the mutex was unlocked, locked by another thread, or not yet
initialized, and dereferenced the adaptive static initializer. FreeBSD returns
EPERM for these (EINVAL for a destroyed mutex) before touching the condition.
(cherry picked from commit a78bb8a855ca3b277f128c6a9e52ff3949b9cb80)
The detached alarm thread waits on a condition variable that was a
namespace-scope static. At exit its destructor ran with the thread still
waiting, and glibc's pthread_cond_destroy blocks then, so a process that
called alarm never exited on Linux. The mutex, condition variable, deadline
and started flag now live in one structure allocated once and never
destroyed.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RetroArch (PPSA99169) stops at alarm on main: `FAIL: unresolved ELF import DQXJraCc1rA from libSceLibcInternal.prx`.
alarm(seconds) arms one process-wide real-time alarm and returns the seconds left on the previous one, rounded up like FreeBSD's alarm.c (any partial second counts); alarm(0) cancels it. When it expires, SIGALRM's disposition as recorded by sigaction or signal applies: ignored, nothing happens; default, the process terminates as on FreeBSD (exit status 128 + 14); a guest handler throws, because asynchronous delivery to a guest handler is not implemented.
The timer lives in libkernel next to the signal dispositions (GuestAlarm_nid_no_patch); libSceLibcInternal, which the titles import alarm from, forwards to it.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The registry calls an observer on the mapping thread, after the registry
lock is released, with the ranges a kernel mapping gave GPU access:
direct and flexible maps with it, and fixed remaps with it over a
registered range (a reservation). The mutation looks them up while it
still holds the lock and hands them over weakly, with the registry
generation it ended at, so the observer does not keep a range alive.
The driver imports those ranges on its current device, so an
address-based build finds the import instead of pinning the memory on
the queue that first uses it. A range that gains GPU access through
sceKernelMprotect, and AnyPS5's own libc heap, are still imported on
first use.
The import at map does not reconcile the other imports with the
registry, so its cost does not grow with the number of imports. It skips
a range that is gone, and any range when the imports were reconciled
with a newer registry since; the next lookup imports those as before.
It installs the pin waiter as the other import paths do, so unmapping a
range imported at map retires its import. The live import bytes are kept as a
running total instead of being summed over every import. Once the import
table belongs to the current device the import takes only the table's
lock; the first import on a device (which runs the write-watch probe)
and a device that was just replaced go through the GPU lock. Clearing
the observer waits for calls already running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PokeMMO Prospero (PPSA98001) stops at siglongjmp (pebqbE5ws8s) from
libkernel. The title links its own FreeBSD sigsetjmp, so siglongjmp
follows FreeBSD's amd64 __siglongjmp and its sigjmp_buf layout: when the
save-mask flag at offset 88 is set it restores the mask saved at 72
through _sigprocmask, then reloads rbx, rsp, rbp, r12-r15 and the x87
control word and returns to the saved address with val, or 1 for 0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FreeBSD's unlink ignores the file's own permission bits, so it succeeds
on a file the guest made read-only with chmod(0400). On Windows the guest
chmod sets the read-only attribute and _wunlink then failed with EACCES,
which sceKernelUnlink and unlink reported. When the path is a regular file
without write permission, make it writable before the single _wunlink and
restore the read-only attribute if the unlink fails, as remove() does
since #2342.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mutex attribute now keeps its protocol and priority ceiling the way
FreeBSD's libthr does: the ceiling can be set and read only under
PTHREAD_PRIO_PROTECT (EINVAL otherwise), and setting a protocol resets it
to the highest SCHED_RR priority. Mutexes still do not apply priority
protection; that is listed as a silent stub.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d10c024f8fdbcdfced0187550f272333264f07df)
A map without MAP_FIXED whose hint lies wholly inside a range still
reserved by sceKernelReserveVirtualRange is placed at the hint, as a fixed
map into the reservation would be. Hints outside reservations and ranges
that are partly mapped keep their hint behaviour. The reservation is
checked under the registry mutation, so it cannot be released and its
address reused between the check and the map (MarcZX100, #2687).
Fixes#2687
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2cY3SsTYnh4u4ZtEr2Adx
A guest path whose bytes the host cannot convert to a native path made
ResolvePath throw std::filesystem::filesystem_error, which aborted the
process. No file can have such a name on the host, so stat reports
ENOENT, as for any other missing file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VLC (PPSA85300) imports execvp (0NJDcQ2B8vE) from libkernel and fails
to load because no library exports it. Replacing the process image is
not supported, so it throws if called, like sceSystemServiceLoadExec
does for executable replacement. VLC itself cannot reach the call: its
fork and vfork fail with ENOSYS, so the child that would call execvp is
never created.
sceKernelSignalSema threw invalid_argument for a count of 0. Marvel's
Wolverine (PPSA03671) signals with a count computed as min(a, b) - c,
which can be 0, and ignores the result, so the throw ended the title a
few minutes in. A zero count now adds no tokens and returns SCE_OK, as
in shadPS4 and Kyty. A null semaphore and a negative count still throw.
guest_kernel_errors signals 0 on a full and on an empty semaphore and
checks that the count is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7191d34c4d79622ba71087f0da2b75eb81ad2c23)
sceKernelMkdir checked that the parent of the resolved path exists, but
for "dir/" std::filesystem::path reports "dir" itself as the parent, so
the call failed with ENOENT and created nothing, while FreeBSD's mkdir
(and the POSIX mkdir export) accept it. Strip trailing separators first,
and report ENOTDIR when the parent exists but is not a directory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 5f74ddba8b346e6c552cf02554d47382708206f3)
Add the guest socket descriptor guard to sceKernelFdatasync before any
native syscall, matching the existing sceKernelFchmod pattern. This
prevents high guest socket descriptors (e.g. 0x10000000) from reaching
the Windows CRT _commit, which would trigger the invalid-parameter
handler, and ensures the correct SCE error is returned on all platforms.
Refactor fdatasync_nid_postfix to delegate to sceKernelFdatasync via
PosixResult, removing the duplicated socket guard and native syscall.
Extend GuestFilesystem tests to cover:
- sceKernelFdatasync on a valid guest socket returns SCE EINVAL (0x80020016)
- sceKernelFdatasync on a closed guest socket returns SCE EBADF (0x80020009)
- sceKernelFdatasync on a high invalid descriptor (0x7fffffff) returns SCE EBADF
- closed-ordinary-descriptor tests now run on Windows (verified _commit on
a closed descriptor returns EBADF without provoking the CRT handler)
Without MAP_FIXED the address is a hint, as for FreeBSD's mmap: the reservation goes at the hint when that range is free, else at the first free range above it. The hint was dropped, so every hinted reservation landed at the lowest free address.
RetroArch (PPSA99169) reserves its cores' direct-memory heap near 0x2000000000 outside the GPU window (0x200000000-0x300000000) and refuses a range inside it; with every reservation at 0x200400000 the heap was never made, and Dolphin's first aligned allocation returned null.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>