1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use crate::rt::{guard, ContextStack};

use crate::yield_::yield_now;
use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSEGV};
use std::mem;
use std::mem::MaybeUninit;
use std::ptr::null_mut;
use std::sync::Once;

static mut SIG_ACTION: MaybeUninit<sigaction> = MaybeUninit::uninit();

// Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
// (unmapped pages) at the end of every thread's stack, so if a thread ends
// up running into the guard page it'll trigger this handler. We want to
// detect these cases and print out a helpful error saying that the stack
// has overflowed. All other signals, however, should go back to what they
// were originally supposed to do.
//
// If this is not a stack overflow, the handler un-registers itself and
// then returns (to allow the original signal to be delivered again).
// Returning from this kind of signal handler is technically not defined
// to work when reading the POSIX spec strictly, but in practice it turns
// out many large systems and all implementations allow returning from a
// signal handler to work. For a more detailed explanation see the
// comments on https://github.com/rust-lang/rust/issues/26458.
unsafe extern "C" fn signal_handler(
    signum: libc::c_int,
    info: *mut libc::siginfo_t,
    ctx: *mut libc::ucontext_t,
) {
    let _ctx = &mut *ctx;
    let addr = (*info).si_addr() as usize;
    let stack_guard = guard::current();

    if !stack_guard.contains(&addr) {
        println!("{}", std::backtrace::Backtrace::force_capture());
        // SIG_ACTION is available after we registered our handler
        sigaction(signum, SIG_ACTION.assume_init_ref(), null_mut());

        // we are unable to handle this
        return;
    }

    eprintln!(
        "\ncoroutine in thread '{}' has overflowed its stack\n",
        std::thread::current().name().unwrap_or("<unknown>")
    );

    ContextStack::current().top().err = Some(Box::new(crate::Error::StackErr));

    let mut sigset: libc::sigset_t = mem::zeroed();
    libc::sigemptyset(&mut sigset);
    libc::sigaddset(&mut sigset, signum);
    libc::sigprocmask(libc::SIG_UNBLOCK, &sigset, null_mut());

    yield_now();

    std::process::abort();
}

#[cold]
unsafe fn init() {
    let mut action: sigaction = mem::zeroed();

    action.sa_flags = SA_SIGINFO | SA_ONSTACK;
    action.sa_sigaction = signal_handler as sighandler_t;

    for signal in [SIGSEGV, SIGBUS] {
        sigaction(signal, &action, SIG_ACTION.as_mut_ptr());
    }
}

pub fn init_once() {
    static INIT_ONCE: Once = Once::new();

    INIT_ONCE.call_once(|| unsafe {
        init();
    })
}