Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guard pages #11756

Closed
wants to merge 11 commits into from
6 changes: 3 additions & 3 deletions src/libgreen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ use std::libc::c_void;
use std::uint;
use std::cast::{transmute, transmute_mut_unsafe,
transmute_region, transmute_mut_region};
use stack::Stack;
use std::unstable::stack;

use stack::StackSegment;

// FIXME #7761: Registers is boxed so that it is 16-byte aligned, for storing
// SSE regs. It would be marginally better not to do this. In C++ we
// use an attribute on a struct.
Expand All @@ -41,7 +40,7 @@ impl Context {
}

/// Create a new context that will resume execution by running proc()
pub fn new(start: proc(), stack: &mut StackSegment) -> Context {
pub fn new(start: proc(), stack: &mut Stack) -> Context {
// The C-ABI function that is the task entry point
//
// Note that this function is a little sketchy. We're taking a
Expand Down Expand Up @@ -79,6 +78,7 @@ impl Context {
// be passed to the spawn function. Another unfortunate
// allocation
let start = ~start;

initialize_call_frame(&mut *regs,
task_start_wrapper as *c_void,
unsafe { transmute(&*start) },
Expand Down
10 changes: 5 additions & 5 deletions src/libgreen/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use std::rt::env;

use context::Context;
use stack::{StackPool, StackSegment};
use stack::{StackPool, Stack};

/// A coroutine is nothing more than a (register context, stack) pair.
pub struct Coroutine {
Expand All @@ -24,7 +24,7 @@ pub struct Coroutine {
///
/// Servo needs this to be public in order to tell SpiderMonkey
/// about the stack bounds.
current_stack_segment: StackSegment,
current_stack_segment: Stack,

/// Always valid if the task is alive and not running.
saved_context: Context
Expand All @@ -39,7 +39,7 @@ impl Coroutine {
Some(size) => size,
None => env::min_stack()
};
let mut stack = stack_pool.take_segment(stack_size);
let mut stack = stack_pool.take_stack(stack_size);
let initial_context = Context::new(start, &mut stack);
Coroutine {
current_stack_segment: stack,
Expand All @@ -49,14 +49,14 @@ impl Coroutine {

pub fn empty() -> Coroutine {
Coroutine {
current_stack_segment: StackSegment::new(0),
current_stack_segment: unsafe { Stack::dummy_stack() },
saved_context: Context::empty()
}
}

/// Destroy coroutine and try to reuse std::stack segment.
pub fn recycle(self, stack_pool: &mut StackPool) {
let Coroutine { current_stack_segment, .. } = self;
stack_pool.give_segment(current_stack_segment);
stack_pool.give_stack(current_stack_segment);
}
}
131 changes: 104 additions & 27 deletions src/libgreen/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,109 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::vec;
use std::libc::{c_uint, uintptr_t};
use std::rt::env::max_cached_stacks;
use std::os::{errno, page_size, MemoryMap, MapReadable, MapWritable,
MapNonStandardFlags, MapVirtual};
#[cfg(not(windows))]
use std::libc::{MAP_STACK, MAP_PRIVATE, MAP_ANON};
use std::libc::{c_uint, c_int, c_void, uintptr_t};

pub struct StackSegment {
priv buf: ~[u8],
priv valgrind_id: c_uint
/// A task's stack. The name "Stack" is a vestige of segmented stacks.
pub struct Stack {
priv buf: MemoryMap,
priv min_size: uint,
priv valgrind_id: c_uint,
}

impl StackSegment {
pub fn new(size: uint) -> StackSegment {
unsafe {
// Crate a block of uninitialized values
let mut stack = vec::with_capacity(size);
stack.set_len(size);
// This is what glibc uses on linux. MAP_STACK can be 0 if any platform we decide to support doesn't
// have it defined. Considering also using MAP_GROWSDOWN
#[cfg(not(windows))]
static STACK_FLAGS: c_int = MAP_STACK | MAP_PRIVATE | MAP_ANON;
#[cfg(windows)]
static STACK_FLAGS: c_int = 0;

let mut stk = StackSegment {
buf: stack,
valgrind_id: 0
};
impl Stack {
/// Allocate a new stack of `size`. If size = 0, this will fail. Use
/// `dummy_stack` if you want a zero-sized stack.
pub fn new(size: uint) -> Stack {
// Map in a stack. Eventually we might be able to handle stack allocation failure, which
// would fail to spawn the task. But there's not many sensible things to do on OOM.
// Failure seems fine (and is what the old stack allocation did).
let stack = match MemoryMap::new(size, [MapReadable, MapWritable,
MapNonStandardFlags(STACK_FLAGS)]) {
Ok(map) => map,
Err(e) => fail!("Creating memory map for stack of size {} failed: {}", size, e)
};

// XXX: Using the FFI to call a C macro. Slow
stk.valgrind_id = rust_valgrind_stack_register(stk.start(), stk.end());
return stk;
// Change the last page to be inaccessible. This is to provide safety; when an FFI
// function overflows it will (hopefully) hit this guard page. It isn't guaranteed, but
// that's why FFI is unsafe. buf.data is guaranteed to be aligned properly.
if !protect_last_page(&stack) {
fail!("Could not memory-protect guard page. stack={:?}, errno={}",
stack, errno());
}

let mut stk = Stack {
buf: stack,
min_size: size,
valgrind_id: 0
};

// XXX: Using the FFI to call a C macro. Slow
stk.valgrind_id = unsafe { rust_valgrind_stack_register(stk.start(), stk.end()) };
return stk;
}

/// Create a 0-length stack which starts (and ends) at 0.
pub unsafe fn dummy_stack() -> Stack {
Stack {
buf: MemoryMap { data: 0 as *mut u8, len: 0, kind: MapVirtual },
min_size: 0,
valgrind_id: 0
}
}

/// Point to the low end of the allocated stack
pub fn start(&self) -> *uint {
self.buf.as_ptr() as *uint
self.buf.data as *uint
}

/// Point one word beyond the high end of the allocated stack
/// Point one uint beyond the high end of the allocated stack
pub fn end(&self) -> *uint {
unsafe {
self.buf.as_ptr().offset(self.buf.len() as int) as *uint
self.buf.data.offset(self.buf.len as int) as *uint
}
}
}

impl Drop for StackSegment {
// These use ToPrimitive so that we never need to worry about the sizes of whatever types these
// (which we would with scalar casts). It's either a wrapper for a scalar cast or failure: fast, or
// will fail during compilation.
#[cfg(unix)]
fn protect_last_page(stack: &MemoryMap) -> bool {
use std::libc::{mprotect, PROT_NONE, size_t};
unsafe {
// This may seem backwards: the start of the segment is the last page? Yes! The stack grows
// from higher addresses (the end of the allocated block) to lower addresses (the start of
// the allocated block).
let last_page = stack.data as *c_void;
mprotect(last_page, page_size() as size_t, PROT_NONE) != -1
}
}

#[cfg(windows)]
fn protect_last_page(stack: &MemoryMap) -> bool {
use std::libc::{VirtualProtect, PAGE_NOACCESS, SIZE_T, LPDWORD, DWORD};
unsafe {
// see above
let last_page = stack.data as *mut c_void;
let mut old_prot: DWORD = 0;
VirtualProtect(last_page, page_size() as SIZE_T, PAGE_NOACCESS,
&mut old_prot as LPDWORD) != 0
}
}

impl Drop for Stack {
fn drop(&mut self) {
unsafe {
// XXX: Using the FFI to call a C macro. Slow
Expand All @@ -56,16 +119,30 @@ impl Drop for StackSegment {
}
}

pub struct StackPool(());
pub struct StackPool {
// Ideally this would be some datastructure that preserved ordering on Stack.min_size.
priv stacks: ~[Stack],
}

impl StackPool {
pub fn new() -> StackPool { StackPool(()) }
pub fn new() -> StackPool {
StackPool {
stacks: ~[],
}
}

pub fn take_segment(&self, min_size: uint) -> StackSegment {
StackSegment::new(min_size)
pub fn take_stack(&mut self, min_size: uint) -> Stack {
// Ideally this would be a binary search
match self.stacks.iter().position(|s| s.min_size < min_size) {
Some(idx) => self.stacks.swap_remove(idx),
None => Stack::new(min_size)
}
}

pub fn give_segment(&self, _stack: StackSegment) {
pub fn give_stack(&mut self, stack: Stack) {
if self.stacks.len() <= max_cached_stacks() {
self.stacks.push(stack)
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/libstd/libc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2823,6 +2823,7 @@ pub mod consts {
pub static MAP_PRIVATE : c_int = 0x0002;
pub static MAP_FIXED : c_int = 0x0010;
pub static MAP_ANON : c_int = 0x1000;
pub static MAP_STACK : c_int = 0;

pub static MAP_FAILED : *c_void = -1 as *c_void;

Expand Down
Loading