pthread_kill(thread, 0) returned 0 for every handle other than the caller's before it asked whether the thread was still alive. FreeBSD's _pthread_kill looks the thread up with _thr_find_thread before it checks for signal 0, so the call answers ESRCH for a thread that has ended or was never a thread. The liveness check now runs before the signal 0 shortcut; the calling thread still answers 0.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Since #8512 a raise redirects a target running host code once 50 ms have
passed since the raise began. A target inside a system call that is not a
wait, such as the NtWriteFile of an fwrite that holds its stream lock, can
stay there longer; the redirect then ran the handler with the lock held.
SONIC SUPERSTARS froze at frame 67 again on main this way, with its GC
raise delivered at ntdll!ZwWriteFile+20.
A target stopped right after a syscall instruction in ntdll now restarts
the 50 ms host-code window, so the raise keeps sampling until the call
returns and the target leaves host code, bounded by the 1 s retry limit.
guest_raise_in_system_call holds a writer in NtWriteFile on a slowly
drained pipe; it times out on main and passes with this change.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
GetThreadContext can fail on a target that was just suspended. A raise
used to throw "cannot read the target thread context" at the first
failure; it now resumes the target and tries again, within the existing
1 s retry limit, before throwing. SONIC SUPERSTARS hit the throw once in
three runs. guest_raise_in_host_code prints the longest single raise.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Follow-up to #7652. A raise on a thread running host code waited up to the
1 s RetryLimit before falling back, so Windows guest_raise_exception (about
220 host-resident raises) went from ~15 s to 17-30 s. Host code now has its
own HostCodeLimit of 50 ms; RetryLimit still guards NtContinue and winpthreads.
A signal that is ignored is forgotten when it is sent: the call succeeds
(sys/kern/kern_sig.c:2178-2187). That covers SIG_IGN and the signals whose
default action is SA_IGNORE, SIGURG, SIGCONT, SIGCHLD, SIGIO, SIGWINCH and
SIGINFO (kern_sig.c:205-240); SIGCONT stays pending while blocked. The guest
failed with EINVAL for every such signal that has no host equivalent.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
FreeBSD 11.4 raise is thr_kill(thr_self(), sig) (lib/libc/gen/raise.c:52),
and thr_kill with a thread id treats signal 0 as a pure existence check
that returns 0 (sys/kern/kern_thr.c:425). The guest returned EINVAL.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
FreeBSD 11.4 kern_sigaction fails with EINVAL when a handler other than
SIG_DFL and SIG_IGN comes with flags outside SA_ONSTACK, SA_RESTART,
SA_RESETHAND, SA_NOCLDSTOP, SA_NODEFER, SA_NOCLDWAIT and SA_SIGINFO
(sys/kern/kern_sig.c:691-695). The guest accepted any flag word.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
FreeBSD 11.4 kern_sigprocmask applies SIG_CANTMASK to the set for SIG_BLOCK
and SIG_SETMASK (sys/kern/kern_sig.c:1056, 1067), and sigsuspend sets its
mask through the same call (kern_sig.c:1534). The guest mask kept both bits,
so sigprocmask(SIG_BLOCK, {SIGKILL}) read back as blocked.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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)