SuspendThread fails with STATUS_THREAD_IS_TERMINATING for a thread that
has begun to exit while its handle is not signaled yet. TryDeliver took
only the signaled handle as an exit, so the raise deliverer, which keeps
sweeping a target in host code until delivery or exit, threw "cannot
suspend the target thread" and aborted the process when that target
exited in between. The suspension now reads the NTSTATUS from
NtSuspendThread and treats a terminating target as exited; other
failures still throw.
guest_raise_exiting_thread raises at 32 threads per round that block in
a host wait and exit after it, for 100 rounds.
waitpid translated the stop and termination signal of a child from the
Linux numbering to the FreeBSD one on every non-Windows host. Darwin
already numbers its signals like FreeBSD, so on macOS the translation
broke them: a child stopped by SIGSTOP (17) was reported as stopped by
20, SIGBUS (10) as 30, SIGSYS (12) as 31, and a child killed by SIGUSR1 (30)
made waitpid report a missing guest equivalent.
macOS now passes the host signal through. The spawn test finds its own
executable with _NSGetExecutablePath on macOS instead of /proc/self/exe,
and also checks a child killed by SIGUSR1, whose number differs between
Linux and FreeBSD and which macOS used to reject.
FreeBSD 11.4's sys___sysctl and userland_sysctl copy the name and the
old length in, and copy the value and the length out, so a bad pointer
gives EFAULT in that order. sysctl dereferenced them directly, and
sysctlbyname read its name with strcmp.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
sigreturn applies FreeBSD 11's amd64 sys_sigreturn checks:
- EFAULT for a null context;
- EINVAL for unknown mc_flags, and for rflags bits outside
PSL_USERCHANGE that differ from the current flags;
- set_fpcontext's FPU format and ownership cases.
It then restores the context through the path setcontext uses, which
now lives in RestoreContext. A non-user %cs and an extended FPU state
still throw.
The only conflict is sceKernelGettimezone in Time.cpp: main now reports the host zone, so the zone is built from that into a local and copied out with the checked write.
A raise delivered some other way (the deliverer sampled the target in guest
code and redirected it) disarms the return breakpoint by clearing DR0 and
DR7 with SetThreadContext. When the target reaches the armed return address
at the same moment, the debug exception is already raised but the context
Windows hands to the handler carries the cleared registers: DR0, DR6 and DR7
are 0. ReturnBreakHandler required DR6.B0, so it declined, and the unhandled
EXCEPTION_SINGLE_STEP ended the process with 0x80000004. Under parallel load
guest_raise_in_host_code failed this way 18 times in 900 runs; a diagnostic
in the declining branch showed every failure at an address armed earlier on
that same thread, with no live return break, DR0 = DR6 = DR7 = 0 and TF
clear.
The ring of armed addresses now records the thread with each address. A
single-step that has TF clear, no breakpoint hit in DR6 and no breakpoint
enabled in DR7, at an address this library armed on the faulting thread, is
taken as that disarmed return breakpoint and continues; any other
single-step is left to the next handler. A DR0 hit is likewise matched
against the addresses armed on the faulting thread.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A target in host code got the delivery as an APC as well as through the
return breakpoint and the sampling. An APC runs in any alertable wait,
so host code that waits alertably while holding a lock would have run
the guest handler inside that wait with the lock held. The APC is now
queued only while the target is in a libkernel wait (Pthread waitCount),
the point where the handler already ran before; a target elsewhere in
host code takes the delivery when it returns into guest code.
guest_raise_leaves_host_code adds a target in an alertable
WaitForSingleObjectEx: each raise must return in under 500 ms and the
handler must run in the executable after the wait was released. The
earlier behaviour (an APC for every host-code target) fails it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
RaiseOn suspended the target and, while it sat in host code, resampled
it until a 50 ms limit and then redirected it into the handler there.
A handler that waits, as Unity's GC thread-stop handler does, then held
whatever host lock the thread had: in Graveyard Keeper 2 a job worker
stopped inside shader specialization kept the shader registration lock,
another worker blocked on that lock never ran its own stop, and the
load froze with 39 of 40 threads acknowledged (about 1 in 15 loads).
A target in host code is never redirected now. The raise queues the
delivery as an APC (taken when the thread waits alertably), arms a
debug register 0 execute breakpoint at the frame's return address into
guest code (found with the image's own .pdata and RtlVirtualUnwind,
stopping at frames that push a machine frame, guarded against faults),
and hands the target to a host deliverer thread that keeps sampling it.
The first of the three to reach the thread delivers, through one taken
flag per delivery. A target in guest code is still redirected at once;
a target inside NtContinue still makes the raise throw after 1 s.
guest_raise_leaves_host_code checks that a handler never runs in host
code, for a target blocked in WaitForSingleObject and one busy in
MultiByteToWideChar, and that each raise of the blocked one returns in
under 500 ms.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FreeBSD 11.4's sys_sigaction, sys_sigprocmask and sys_sigaltstack copy
their input in before validating it and copy the old value out after
the change, and sys_sigpending copies out; a bad pointer gives EFAULT.
The guest pointers were dereferenced directly.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The only conflict is in Time.cpp with the guest timezone store of the
settimeofday change: gettimeofday reads the timezone under the mutex into
a local copy, releases it, then copies it out with the checked write.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>