Skip to content

Commit 9977f8e

Browse files
committed
Use mmap and create a guard page
1 parent 298b853 commit 9977f8e

File tree

1 file changed

+64
-16
lines changed

1 file changed

+64
-16
lines changed

src/lib.rs

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ cfg_if! {
8282
fn __stacker_switch_stacks(dataptr: *mut u8,
8383
fnptr: *const u8,
8484
new_stack: usize);
85+
fn getpagesize() -> libc::c_int;
8586
}
8687

8788
thread_local! {
@@ -99,19 +100,70 @@ cfg_if! {
99100
STACK_LIMIT.with(|s| s.set(l))
100101
}
101102

103+
struct StackSwitch {
104+
map: *mut libc::c_void,
105+
stack_size: usize,
106+
old_stack_limit: usize,
107+
}
108+
109+
impl Drop for StackSwitch {
110+
fn drop(&mut self) {
111+
unsafe {
112+
libc::munmap(self.map, self.stack_size);
113+
}
114+
set_stack_limit(self.old_stack_limit);
115+
}
116+
}
117+
102118
fn _grow(stack_size: usize, mut f: &mut FnMut()) {
103-
// Align to 16-bytes (see below for why)
104-
let stack_size = (stack_size + 15) / 16 * 16;
119+
let page_size = unsafe { getpagesize() } as usize;
105120

106-
// Allocate some new stack for oureslves
107-
let mut stack = Vec::<u8>::with_capacity(stack_size);
108-
let new_limit = stack.as_ptr() as usize + 32 * 1024;
121+
// Round the stack size up to a multiple of page_size
122+
let rem = stack_size % page_size;
123+
let stack_size = if rem == 0 {
124+
stack_size
125+
} else {
126+
stack_size.checked_add((page_size - rem))
127+
.expect("stack size calculation overflowed")
128+
};
109129

110-
// Save off the old stack limits
111-
let old_limit = get_stack_limit();
130+
// We need at least 2 page
131+
let stack_size = std::cmp::max(stack_size, page_size);
132+
133+
// Add a guard page
134+
let stack_size = stack_size.checked_add(page_size)
135+
.expect("stack size calculation overflowed");
136+
137+
// Allocate some new stack for ourselves
138+
let map = unsafe {
139+
libc::mmap(std::ptr::null_mut(),
140+
stack_size,
141+
libc::PROT_NONE,
142+
libc::MAP_PRIVATE |
143+
libc::MAP_ANON,
144+
0,
145+
0)
146+
};
147+
if map == -1isize as _ {
148+
panic!("unable to allocate stack")
149+
}
150+
let _switch = StackSwitch {
151+
map,
152+
stack_size,
153+
old_stack_limit: get_stack_limit(),
154+
};
155+
let result = unsafe {
156+
libc::mprotect((map as usize + page_size) as *mut libc::c_void,
157+
stack_size - page_size,
158+
libc::PROT_READ | libc::PROT_WRITE)
159+
};
160+
if result == -1 {
161+
panic!("unable to set stack permissions")
162+
}
163+
let stack_low = map as usize;
112164

113165
// Prepare stack limits for the stack switch
114-
set_stack_limit(new_limit);
166+
set_stack_limit(stack_low);
115167

116168
// Make sure the stack is 16-byte aligned which should be enough for all
117169
// platforms right now. Allocations on 64-bit are already 16-byte aligned
@@ -127,12 +179,10 @@ cfg_if! {
127179
unsafe {
128180
__stacker_switch_stacks(&mut f as *mut &mut FnMut() as *mut u8,
129181
doit as usize as *const _,
130-
stack.as_mut_ptr() as usize + stack_size - offset);
182+
stack_low + stack_size - offset);
131183
}
132184

133-
// Once we've returned reset bothe stack limits and then return value same
134-
// value the closure returned.
135-
set_stack_limit(old_limit);
185+
// Dropping `switch` frees the memory mapping and restores the old stack limit
136186
}
137187
}
138188
}
@@ -181,11 +231,11 @@ cfg_if! {
181231
},
182232
};
183233
if info.parent_fiber == 0i32 as _ {
184-
panic!("Unable to convert thread to fiber");
234+
panic!("unable to convert thread to fiber");
185235
}
186236
let fiber = kernel32::CreateFiber(stack_size as _, Some(fiber_proc), &mut info as *mut FiberInfo as *mut _);
187237
if fiber == 0i32 as _ {
188-
panic!("Unable to allocate fiber");
238+
panic!("unable to allocate fiber");
189239
}
190240
kernel32::SwitchToFiber(fiber);
191241
kernel32::DeleteFiber(fiber);
@@ -253,8 +303,6 @@ cfg_if! {
253303
stackaddr as usize
254304
}
255305
} else if #[cfg(target_os = "macos")] {
256-
use libc::{c_void, pthread_t, size_t};
257-
258306
unsafe fn guess_os_stack_limit() -> usize {
259307
libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize -
260308
libc::pthread_get_stacksize_np(libc::pthread_self()) as usize

0 commit comments

Comments
 (0)