base_coroutine/stack/
mod.rs

1// Copyright 2016 coroutine-rs Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use std::error::Error;
9use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
10use std::io;
11use std::ops::Deref;
12use std::os::raw::c_void;
13
14mod sys;
15
16/// Error type returned by stack allocation methods.
17#[derive(Debug)]
18pub enum StackError {
19    /// Contains the maximum amount of memory allowed to be allocated as stack space.
20    ExceedsMaximumSize(usize),
21
22    /// Returned if some kind of I/O error happens during allocation.
23    IoError(io::Error),
24}
25
26impl Display for StackError {
27    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
28        match *self {
29            StackError::ExceedsMaximumSize(size) => {
30                write!(
31                    fmt,
32                    "Requested more than max size of {} bytes for a stack",
33                    size
34                )
35            }
36            StackError::IoError(ref e) => std::fmt::Display::fmt(&e, fmt),
37        }
38    }
39}
40
41impl Error for StackError {
42    fn source(&self) -> Option<&(dyn Error + 'static)> {
43        match *self {
44            StackError::ExceedsMaximumSize(_) => None,
45            StackError::IoError(ref e) => Some(e),
46        }
47    }
48}
49
50/// Represents any kind of stack memory.
51///
52/// `FixedSizeStack` as well as `ProtectedFixedSizeStack`
53/// can be used to allocate actual stack space.
54#[derive(Debug)]
55pub struct Stack {
56    top: *mut c_void,
57    bottom: *mut c_void,
58}
59
60impl Stack {
61    /// Creates a (non-owning) representation of some stack memory.
62    ///
63    /// It is unsafe because it is your reponsibility to make sure that `top` and `buttom` are valid
64    /// addresses.
65    #[inline]
66    pub(crate) unsafe fn new(top: *mut c_void, bottom: *mut c_void) -> Stack {
67        debug_assert!(top >= bottom);
68
69        Stack { top, bottom }
70    }
71
72    /// Returns the top of the stack from which on it grows downwards towards bottom().
73    #[inline]
74    pub fn top(&self) -> *mut c_void {
75        self.top
76    }
77
78    /// Returns the bottom of the stack and thus it's end.
79    #[inline]
80    pub fn bottom(&self) -> *mut c_void {
81        self.bottom
82    }
83
84    /// Returns the size of the stack between top() and bottom().
85    #[inline]
86    pub fn len(&self) -> usize {
87        self.top as usize - self.bottom as usize
88    }
89
90    #[inline]
91    pub fn is_empty(&self) -> bool {
92        self.len() == 0
93    }
94
95    /// Returns the minimal stack size allowed by the current platform.
96    #[inline]
97    pub fn min_size() -> usize {
98        sys::min_stack_size()
99    }
100
101    /// Returns the maximum stack size allowed by the current platform.
102    #[inline]
103    pub fn max_size() -> usize {
104        sys::max_stack_size(true)
105    }
106
107    /// Returns a implementation defined default stack size.
108    ///
109    /// This value can vary greatly between platforms, but is usually only a couple
110    /// memory pages in size and enough for most use-cases with little recursion.
111    /// It's usually a better idea to specifiy an explicit stack size instead.
112    #[inline]
113    pub fn default_size() -> usize {
114        sys::default_stack_size()
115    }
116
117    /// Allocates a new stack of `size`.
118    fn allocate(mut size: usize) -> Result<Stack, StackError> {
119        let page_size = sys::page_size();
120        let min_stack_size = sys::min_stack_size();
121        let max_stack_size = sys::max_stack_size(false);
122        //unix环境使用jemalloc后不需要mprotect,这里add其实不用左移
123        //为了兼容windows环境,此处不做修改
124        let add = page_size << 1;
125
126        if size < min_stack_size {
127            size = min_stack_size;
128        }
129
130        size = (size - 1) & !(page_size - 1);
131
132        if let Some(size) = size.checked_add(add) {
133            if size <= max_stack_size {
134                let mut ret = unsafe { sys::allocate_stack(size) };
135
136                if let Ok(stack) = ret {
137                    ret = unsafe { sys::protect_stack(&stack) };
138                }
139
140                return ret.map_err(StackError::IoError);
141            }
142        }
143
144        Err(StackError::ExceedsMaximumSize(max_stack_size - add))
145    }
146}
147
148unsafe impl Send for Stack {}
149
150/// A more secure, but slightly slower version of `FixedSizeStack`.
151///
152/// Allocates stack space using virtual memory, whose pages will
153/// only be mapped to physical memory if they are used.
154///
155/// The additional guard page is made protected and inaccessible.
156/// Now if a stack overflow occurs it should (hopefully) hit this guard page and
157/// cause a segmentation fault instead letting the memory being overwritten silently.
158///
159/// _As a general rule it is recommended to use **this** struct to create stack memory._
160#[derive(Debug)]
161pub struct ProtectedFixedSizeStack(Stack);
162
163impl ProtectedFixedSizeStack {
164    /// Allocates a new stack of **at least** `size` bytes + one additional guard page.
165    ///
166    /// `size` is rounded up to a multiple of the size of a memory page and
167    /// does not include the size of the guard page itself.
168    pub fn new(size: usize) -> Result<ProtectedFixedSizeStack, StackError> {
169        Stack::allocate(size).map(ProtectedFixedSizeStack)
170    }
171}
172
173impl Deref for ProtectedFixedSizeStack {
174    type Target = Stack;
175
176    fn deref(&self) -> &Stack {
177        &self.0
178    }
179}
180
181impl Default for ProtectedFixedSizeStack {
182    fn default() -> ProtectedFixedSizeStack {
183        ProtectedFixedSizeStack::new(Stack::default_size()).unwrap_or_else(|err| {
184            panic!("Failed to allocate ProtectedFixedSizeStack with {:?}", err)
185        })
186    }
187}
188
189impl Drop for ProtectedFixedSizeStack {
190    fn drop(&mut self) {
191        let page_size = sys::page_size();
192        let guard = (self.0.bottom() as usize - page_size) as *mut c_void;
193        let size_with_guard = self.0.len() + page_size;
194        unsafe {
195            sys::deallocate_stack(guard, size_with_guard);
196        }
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    #[test]
205    fn stack_size_too_small() {
206        let stack = ProtectedFixedSizeStack::new(0).unwrap();
207        assert_eq!(stack.len(), Stack::min_size());
208        unsafe { std::ptr::write_bytes(stack.bottom() as *mut u8, 0x1d, stack.len()) };
209    }
210
211    #[test]
212    fn stack_size_too_large() {
213        let stack_size = Stack::max_size();
214
215        match ProtectedFixedSizeStack::new(stack_size) {
216            Err(StackError::ExceedsMaximumSize(..)) => panic!(),
217            _ => {}
218        }
219
220        let stack_size = stack_size + 1;
221
222        match ProtectedFixedSizeStack::new(stack_size) {
223            Err(StackError::ExceedsMaximumSize(..)) => {}
224            _ => panic!(),
225        }
226    }
227}