Skip to content

Commit df6d452

Browse files
authored
Block signals when freeing the stack for a detached thread (#126)
When a detached thread exits, it frees its own stack, creating a window between the time when the stack is freed and the thread exits. If a signal is delivered to the thread in that window, it will attempt to run on the freed stack. To avoid this hazard, use `rustix::runtime::sigprocmask` to block all signals on the thread before freeing the stack. This causes all process-directed signals to be delivered to a different thread in the process. A slight infelicity is that thread-directed symbols directed at the detached thread that arrive during this time will be silently ignored. However, since such signals would already be racing with the thread exit, so there's no guarantee they won't be delivered after the exit and therefore to a different thread that reuses the tid. So this problem is arguably unobservable. Fixes #125.
1 parent 076876f commit df6d452

1 file changed

Lines changed: 10 additions & 0 deletions

File tree

src/thread/linux_raw.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ use rustix::mm::{mmap_anonymous, mprotect, MapFlags, MprotectFlags, ProtFlags};
2727
use rustix::param::{linux_execfn, page_size};
2828
use rustix::process::{getrlimit, Resource};
2929
use rustix::runtime::{exe_phdrs, set_tid_address};
30+
#[cfg(feature = "signal")]
31+
use rustix::runtime::{sigprocmask, How, Sigset};
3032
use rustix::thread::gettid;
3133

3234
/// An opaque pointer to a thread.
@@ -713,6 +715,14 @@ unsafe fn exit(return_value: Option<NonNull<c_void>>) -> ! {
713715
// memory that we've freed trying to clear our tid when we exit.
714716
let _ = set_tid_address(null_mut());
715717

718+
// In preparation for freeing the stack, block all signals, so that
719+
// no signals for the process are delivered to this thread.
720+
#[cfg(feature = "signal")]
721+
{
722+
let all = Sigset { sig: [!0] };
723+
sigprocmask(How::BLOCK, Some(&all)).ok();
724+
}
725+
716726
// `munmap` the memory, which also frees the stack we're currently
717727
// on, and do an `exit` carefully without touching the stack.
718728
let map = current_stack_addr.cast::<u8>().sub(current_guard_size);

0 commit comments

Comments
 (0)