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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#![cfg_attr(test, allow(dead_code))]

use self::imp::{drop_handler, make_handler};

pub use self::imp::cleanup;
pub use self::imp::init;

pub struct Handler {
    data: *mut libc::c_void,
}

impl Handler {
    pub unsafe fn new() -> Handler {
        make_handler()
    }

    fn null() -> Handler {
        Handler { data: crate::ptr::null_mut() }
    }
}

impl Drop for Handler {
    fn drop(&mut self) {
        unsafe {
            drop_handler(self.data);
        }
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "macos",
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "solaris",
    target_os = "illumos",
    target_os = "netbsd",
    target_os = "openbsd"
))]
mod imp {
    use super::Handler;
    use crate::io;
    use crate::mem;
    use crate::ptr;
    use crate::thread;

    use libc::MAP_FAILED;
    #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
    use libc::{mmap as mmap64, munmap};
    #[cfg(all(target_os = "linux", target_env = "gnu"))]
    use libc::{mmap64, munmap};
    use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL};
    use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE};
    use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV};

    use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
    use crate::sys::unix::os::page_size;
    use crate::sys_common::thread_info;

    // SIGSEGV 和 SIGBUS 处理程序的信号处理程序。每个线程栈的末尾都有保护页面 (未映射的页面),因此,如果线程最终运行到保护页面中,它将触发此处理程序。
    // 我们要检测这些情况并打印出一个有用的错误,指出栈已溢出。
    // 但是,所有其他信号应返回到它们最初应该执行的操作。
    //
    // 当前,此处理程序纯粹是为了在线程溢出其栈时打印提示性消息而存在。
    // 然后,我们中止以退出并指示崩溃,但要避免产生误导性的 SIGSEGV,这可能会导致用户认为不安全的代码已访问了无效的指针; 栈溢出时遇到的 SIGSEGV 是预期的,并且定义明确。
    //
    //
    // 如果这不是栈溢出,则处理程序将对其自身进行注销,然后返回 (以允许再次发送原始信号)。
    // 从技术上说,严格阅读 POSIX 规范时,从这种信号处理程序返回的函数没有定义为可以正常工作,但实际上,事实证明,这是许多大型系统,并且所有实现都允许从信号处理程序返回工作。
    // 有关更详细的说明,请参见 #26458 上的注释。
    //
    //
    //
    //
    //
    //
    //
    //
    //
    //
    unsafe extern "C" fn signal_handler(
        signum: libc::c_int,
        info: *mut libc::siginfo_t,
        _data: *mut libc::c_void,
    ) {
        let guard = thread_info::stack_guard().unwrap_or(0..0);
        let addr = (*info).si_addr() as usize;

        // 如果故障地址在保护页面内,则我们将显示一条消息,然后中止。
        //
        if guard.start <= addr && addr < guard.end {
            rtprintpanic!(
                "\nthread '{}' has overflowed its stack\n",
                thread::current().name().unwrap_or("<unknown>")
            );
            rtabort!("stack overflow");
        } else {
            // 通过恢复为默认行为来注销自己。
            let mut action: sigaction = mem::zeroed();
            action.sa_sigaction = SIG_DFL;
            sigaction(signum, &action, ptr::null_mut());

            // 有关为什么返回此函数的信息,请参见上面的注释。
        }
    }

    static MAIN_ALTSTACK: AtomicPtr<libc::c_void> = AtomicPtr::new(ptr::null_mut());
    static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false);

    pub unsafe fn init() {
        let mut action: sigaction = mem::zeroed();
        for &signal in &[SIGSEGV, SIGBUS] {
            sigaction(signal, ptr::null_mut(), &mut action);
            // 如果尚未设置信号处理程序,请进行配置。
            if action.sa_sigaction == SIG_DFL {
                action.sa_flags = SA_SIGINFO | SA_ONSTACK;
                action.sa_sigaction = signal_handler as sighandler_t;
                sigaction(signal, &action, ptr::null_mut());
                NEED_ALTSTACK.store(true, Ordering::Relaxed);
            }
        }

        let handler = make_handler();
        MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
        mem::forget(handler);
    }

    pub unsafe fn cleanup() {
        drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed));
    }

    unsafe fn get_stackp() -> *mut libc::c_void {
        // OpenBSD 需要此标志用于栈映射,否则所述映射将在大多数系统上作为无操作失败,并且在 FreeBSD 上具有不同的含义
        //
        //
        #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",))]
        let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK;
        #[cfg(not(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",)))]
        let flags = MAP_PRIVATE | MAP_ANON;
        let stackp =
            mmap64(ptr::null_mut(), SIGSTKSZ + page_size(), PROT_READ | PROT_WRITE, flags, -1, 0);
        if stackp == MAP_FAILED {
            panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error());
        }
        let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE);
        if guard_result != 0 {
            panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error());
        }
        stackp.add(page_size())
    }

    unsafe fn get_stack() -> libc::stack_t {
        libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
    }

    pub unsafe fn make_handler() -> Handler {
        if !NEED_ALTSTACK.load(Ordering::Relaxed) {
            return Handler::null();
        }
        let mut stack = mem::zeroed();
        sigaltstack(ptr::null(), &mut stack);
        // 如果尚未设置备用信号栈,则配置备用信号栈。
        if stack.ss_flags & SS_DISABLE != 0 {
            stack = get_stack();
            sigaltstack(&stack, ptr::null_mut());
            Handler { data: stack.ss_sp as *mut libc::c_void }
        } else {
            Handler::null()
        }
    }

    pub unsafe fn drop_handler(data: *mut libc::c_void) {
        if !data.is_null() {
            let stack = libc::stack_t {
                ss_sp: ptr::null_mut(),
                ss_flags: SS_DISABLE,
                // sigaltstack UNIX2003 的 macOS 实现中的错误的变通办法,当禁用 ss_size 小于 MINSIGSTKSZ 时,该栈在禁用栈时返回 ENOMEM。
                // 根据 POSIX,在这种情况下,应同时忽略 ss_sp 和 ss_size。
                //
                //
                ss_size: SIGSTKSZ,
            };
            sigaltstack(&stack, ptr::null_mut());
            // 从 `get_stackp` 中我们知道,我们安装的备用栈是早于一页开始的映射的一部分,因此请返回页面并从此处取消映射。
            //
            munmap(data.sub(page_size()), SIGSTKSZ + page_size());
        }
    }
}

#[cfg(not(any(
    target_os = "linux",
    target_os = "macos",
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "solaris",
    target_os = "illumos",
    target_os = "netbsd",
    target_os = "openbsd",
)))]
mod imp {
    pub unsafe fn init() {}

    pub unsafe fn cleanup() {}

    pub unsafe fn make_handler() -> super::Handler {
        super::Handler::null()
    }

    pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
}