Hello, [email protected], le mar. 03 mars 2026 04:08:15 +0000, a ecrit: > #### S-2: `sigtimedwait` leaks preemptor on timeout (CRITICAL) > > **File**: `sysdeps/mach/hurd/sigtimedwait.c` > > When `__sigtimedwait` times out, the timeout path (lines 144-148) falls > through > to `all_done` without removing the signal preemptor from `ss->preemptors`. > The preemptor is a stack-local variable, so after the function returns, > `ss->preemptors` contains a dangling pointer. Subsequent signal delivery will > dereference freed stack memory. > > Additionally, the timeout path never restores `ss->blocked` (saved at line > 128, > modified at line 129), so temporarily unblocked signals remain unblocked. It > also > calls `_hurd_sigstate_unlock` at `all_done` without holding the lock > (released at > line 131), which is undefined behavior.
Indeed, I guess this was never actually tested. > #### S-2 Proposed Fix: Clean up preemptor and blocked mask on timeout > > The timeout path (lines 144-148, inside the `if (!setjmp (buf))` block after > `mach_msg` returns) needs to re-lock sigstate, remove the preemptor, and > restore the blocked mask. Replace lines 144-148: > > ```c > if (!(option & MACH_RCV_TIMEOUT)) > abort (); > > /* Timed out. */ > signo = __hurd_fail (EAGAIN); > ``` > > with: > > ```c > if (!(option & MACH_RCV_TIMEOUT)) > abort (); > > /* Timed out. Clean up the preemptor and restore blocked mask. */ > _hurd_sigstate_lock (ss); > assert (ss->preemptors == &preemptor); > ss->preemptors = preemptor.next; > ss->blocked = blocked; > signo = __hurd_fail (EAGAIN); > ``` > > All three paths through the function (already-pending, signal delivery via > `longjmp`, and timeout) now reach `all_done` holding `ss->lock`, so the > existing `_hurd_sigstate_unlock` at `all_done` handles all of them correctly. I have rather simply let both codepaths use the same cleanup code. I have also added some test, which was indeed crashing before the fix. Thanks! Samuel
