In a payload process on a PS5 (firmware 12.70), sysctlbyname returns -1
with ENOENT for thirteen names that threw here, and leaves the buffer and
the length alone, for a read, a size query, a null length and a null
buffer with a null length. sysctl does the same for the fixed MIBs of
seven of them. sysctlnametomib gives those seven MIBs and returns ENOENT
for hw.cacheline_size and hw.usermem, which threw too.
machdep.cacheline_size already returned ENOENT and is now tested with
the others. A write to the new names and MIBs throws, as the console's
answer to a write was not tried. The hw.realmem and vm.overcommit debt
entries now say that a payload process gets ENOENT for them as well.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GetModuleHandleExW and GetModuleFileNameW take loader locks the suspended
target may hold. While the target is suspended only VirtualQuery and a
lock-free cache of image bases are consulted; an image not yet cached is
classified after the target is resumed, and the target is sampled again.
The statics the checks use are initialized before the first suspend. The
host-code wait is recorded in the libkernel debt file, and the test raises
100 times so it fails on every run without the change.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
In a payload process on a PS5 (firmware 12.70), getrlimit returns
0x200000 for RLIMIT_STACK, infinity for RLIMIT_RSS and RLIMIT_MEMLOCK,
and 1604 for RLIMIT_NPROC, each as both soft and hard limit. These four
threw here. Whether a title process reads the same limits is not
measured.
setrlimit reads getrlimit first, so it now accepts RLIMIT_STACK,
RLIMIT_RSS and RLIMIT_MEMLOCK when the values do not change, as the
payload process does. RLIMIT_NPROC throws: in the payload process
setting it to its current value succeeds, but getrlimit then reads 1443,
and the limit is not stored here. Any other change still throws. The
getrlimit and setrlimit debt entries also record the payload process's
RLIMIT_DATA and RLIMIT_NOFILE readings, its 12555 clamp of RLIMIT_NOFILE
and its RLIMIT_NPROC reading after a same-value set.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a payload process on a PS5 (firmware 12.70),
sysctlbyname("kern.rng_pseudo") answers a size query with 64 and gives
at most 64 bytes per call, up to a 65536-byte buffer. A buffer smaller
than 64 bytes gets as many bytes as its length and a null length gets
none, both with -1 and ENOMEM; a null buffer with a null length returns
0, and errno is kept when the call succeeds. Here the whole buffer was
filled, a size query threw and the length was never changed. The 64
random bytes are now drawn only for a read that passes both a buffer
and a length.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows, sceKernelRaiseException suspends the target and redirects it to
the guest handler wherever it stopped, except inside NtContinue or
winpthreads. A target stopped inside host code (a C runtime call, a host
library) can hold a host lock there; a handler that then waits, as a
collector's thread-stop handler does, keeps it held, and every host thread
that needs the lock stops with it. SONIC SUPERSTARS stopped at frame 65 this
way: its "UDS" thread was redirected inside the stderr write of a trophy
message and parked in sceKernelWaitEventFlag holding the stream lock, and the
driver's logging threads blocked on it.
A target running code of a module other than the executable or a guest
module is now resumed and suspended again until it is back in guest code,
for up to the existing retry limit; a target blocked in an ntdll wait is
still redirected there, as before.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
In a payload process on a PS5 (firmware 12.70), I386_GET_FSBASE (7) and
I386_GET_GSBASE (9) wrote 4 bytes, the low 32 bits of the FS and GS
bases, and failed with EFAULT for a null argument; both threw here. On
Linux x86-64 hosts they now do the same.
The nine operation numbers tried that FreeBSD 11's amd64 sysarch has no
case for (2, 5, 6, 12, 127, 133, 134, 1000 and -1), each with a null
argument, failed with EINVAL there, as its default case does, and threw
here. Every such number now returns EINVAL on every host.
Threads made with pthread_create and scePthreadCreate started with their
creator's GS base there; the test now checks that a guest thread does so
on Linux. The sysarch debt entry records these measurements and what
AMD64_GET_XFPUSTATE and I386_GET_XFPUSTATE returned there; both still
throw here.
FreeBSD alarm() is setitimer(ITIMER_REAL), and setitimer fails with EINVAL
for it_value.tv_sec above INT32_MAX / 2, so alarm() returns -1 and leaves
the pending alarm alone. alarm(UINT_MAX) armed an alarm decades away and
returned the old remaining time instead.
(cherry picked from commit 2dea16d07d4e0b36e4a3d1eebe3b5c3ffb739807)
sigprocmask and _sigprocmask threw std::invalid_argument for a how other
than SIG_BLOCK, SIG_UNBLOCK or SIG_SETMASK, which ends the title. FreeBSD
returns -1 with EINVAL and leaves the old mask uncopied, and ignores how
when set is null; pthread_sigmask here already returns EINVAL. The check
now comes first, before the previous mask is written.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An ITIMER_REAL or alarm expiry while SIGALRM is blocked is marked on the
pending set of sigtimedwait instead of throwing, as kern_psignal leaves a
blocked signal pending. sigwait, sigwaitinfo and sigtimedwait take it,
repeated expiries merge into one pending signal, and unblocking it runs a
guest handler on the unblocking thread or, with the default disposition,
exits the process with status 142 as FreeBSD's default action does.
The expiry rechecks the mask after marking the signal, so an unblock that
races it does not leave SIGALRM pending while unblocked, and TakePending
clears the bit with the value it read so one signal is taken once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sysarch's AMD64_GET_FSBASE, AMD64_GET_GSBASE and AMD64_SET_GSBASE now
follow FreeBSD 11's amd64 sysarch on Linux x86-64 through arch_prctl: the
GS base is the calling thread's, a base at or above VM_MAXUSER_ADDRESS
returns EINVAL and a null argument EFAULT. AMD64_SET_FSBASE, whose base
guest and host code share for their TLS, other operations and other hosts
still throw. Wine sets its TEB this way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sysctl {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, pid} for the calling process
returns packed FreeBSD 11 kinfo_vmentry records, one per used range of the
guest arena, with the protection sceKernelVirtualQuery reports. Other pids
return ESRCH, a write EPERM, a short buffer ENOMEM. GuestArena gains a
query for the used range at or after an address.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setitimer refuses to arm the CPU interval timers and already reads and
disarms them as zero, so they are never armed. getitimer threw for them
instead of reporting that state, as FreeBSD's kern_getitimer does for a
timer that is not set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FreeBSD's getrusage rejects an unknown `who` with EINVAL before it
copies anything out and returns EFAULT when the result can't be
written. The HLE threw std::invalid_argument for both, which ends the
title.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PPSA16833 imports it from libkernel, and its executable does not load
while no library exports the NID. The signature is unknown, so it throws
if called, like sceCoredumpWriteUserString.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On macOS the RLIMIT_NOFILE soft limit is not a bound on the descriptor table: setrlimit accepts soft limits above kern.maxfilesperproc, RLIM_INFINITY included, and getrlimit reports what was set, so returning the guest soft limit alone could report up to INT_MAX for a table of about 122880 descriptors, and a closefrom-style loop up to that value would run billions of iterations instead of about 122k. Return the smaller of the guest soft limit and the host's ::getdtablesize(), which is the FreeBSD sys_getdtablesize result. The guest test derives its expectation from the host call instead of the implementation's formula, and the debt entry no longer claims the limit is raised when libkernel loads outside Linux. Reported by jotitadev in review of the getdtablesize implementation.