Allocate guard page under existing stack, not in it.
This fixes a segfault when the allocated stack is just one page long. This also refactors the fringe::os module to use Result consistently. close #22
This commit is contained in:
parent
75ca6f66e1
commit
ebd9ca8dec
|
@ -4,7 +4,7 @@ name = "fringe"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.1.6"
|
libc = "0.2.14"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
kernel32-sys = "0.2.2"
|
kernel32-sys = "0.2.2"
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// This file is part of libfringe, a low-level green threading library.
|
// This file is part of libfringe, a low-level green threading library.
|
||||||
// Copyright (c) edef <edef@edef.eu>
|
// Copyright (c) edef <edef@edef.eu>
|
||||||
// See the LICENSE file included in this distribution.
|
// See the LICENSE file included in this distribution.
|
||||||
|
|
||||||
extern crate std;
|
extern crate std;
|
||||||
use self::std::io::Error as IoError;
|
use self::std::io::Error as IoError;
|
||||||
use stack;
|
use stack;
|
||||||
|
|
||||||
mod sys;
|
mod sys;
|
||||||
|
|
||||||
/// This object represents a stack from the operating system's
|
/// This object represents a stack from the operating system's
|
||||||
|
@ -23,24 +23,22 @@ impl Stack {
|
||||||
pub fn new(size: usize) -> Result<Stack, IoError> {
|
pub fn new(size: usize) -> Result<Stack, IoError> {
|
||||||
let page_size = sys::page_size();
|
let page_size = sys::page_size();
|
||||||
|
|
||||||
// round the page size up,
|
// Round the length one page size up, using the fact that the page size
|
||||||
// using the fact that it is a power of two
|
// is a power of two.
|
||||||
let len = (size + page_size - 1) & !(page_size - 1);
|
let len = (size + page_size - 1) & !(page_size - 1);
|
||||||
|
|
||||||
let stack = unsafe {
|
// Increase the length to fit the guard page.
|
||||||
let ptr = try!(match sys::map_stack(size) {
|
let len = len + page_size;
|
||||||
None => Err(IoError::last_os_error()),
|
|
||||||
Some(ptr) => Ok(ptr)
|
|
||||||
});
|
|
||||||
|
|
||||||
Stack { ptr: ptr as *mut u8, len: len }
|
// Allocate a stack.
|
||||||
|
let stack = Stack {
|
||||||
|
ptr: try!(unsafe { sys::map_stack(len) }),
|
||||||
|
len: len
|
||||||
};
|
};
|
||||||
|
|
||||||
try!(if unsafe { sys::protect_stack(stack.ptr) } {
|
// Mark the guard page. If this fails, `stack` will be dropped,
|
||||||
Ok(())
|
// unmapping it.
|
||||||
} else {
|
try!(unsafe { sys::protect_stack(stack.ptr) });
|
||||||
Err(IoError::last_os_error())
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(stack)
|
Ok(stack)
|
||||||
}
|
}
|
||||||
|
@ -62,11 +60,6 @@ impl stack::Stack for Stack {
|
||||||
|
|
||||||
impl Drop for Stack {
|
impl Drop for Stack {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe { sys::unmap_stack(self.ptr, self.len) }.expect("cannot unmap stack")
|
||||||
if !sys::unmap_stack(self.ptr, self.len) {
|
|
||||||
panic!("munmap for stack {:p} of size {} failed: {}",
|
|
||||||
self.ptr, self.len, IoError::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
// This file is part of libfringe, a low-level green threading library.
|
// This file is part of libfringe, a low-level green threading library.
|
||||||
// Copyright (c) edef <edef@edef.eu>
|
// Copyright (c) edef <edef@edef.eu>
|
||||||
// See the LICENSE file included in this distribution.
|
// See the LICENSE file included in this distribution.
|
||||||
|
extern crate std;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
use self::std::io::Error as IoError;
|
||||||
use self::libc::{c_void, c_int, size_t};
|
use self::libc::{c_void, c_int, size_t};
|
||||||
use self::libc::{mmap, mprotect, munmap};
|
use self::libc::{mmap, mprotect, munmap};
|
||||||
use self::libc::MAP_FAILED;
|
use self::libc::MAP_FAILED;
|
||||||
|
@ -29,20 +31,27 @@ const STACK_FLAGS: c_int = libc::MAP_STACK
|
||||||
const STACK_FLAGS: c_int = libc::MAP_PRIVATE
|
const STACK_FLAGS: c_int = libc::MAP_PRIVATE
|
||||||
| libc::MAP_ANON;
|
| libc::MAP_ANON;
|
||||||
|
|
||||||
pub unsafe fn map_stack(len: usize) -> Option<*mut u8> {
|
pub unsafe fn map_stack(len: usize) -> Result<*mut u8, IoError> {
|
||||||
let ptr = mmap(ptr::null_mut(), len as size_t,
|
let ptr = mmap(ptr::null_mut(), len as size_t, STACK_PROT, STACK_FLAGS, -1, 0);
|
||||||
STACK_PROT, STACK_FLAGS, -1, 0);
|
|
||||||
if ptr == MAP_FAILED {
|
if ptr == MAP_FAILED {
|
||||||
None
|
Err(IoError::last_os_error())
|
||||||
} else {
|
} else {
|
||||||
Some(ptr as *mut u8)
|
Ok(ptr as *mut u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn protect_stack(ptr: *mut u8) -> bool {
|
pub unsafe fn protect_stack(ptr: *mut u8) -> Result<(), IoError> {
|
||||||
mprotect(ptr as *mut c_void, page_size() as size_t, GUARD_PROT) == 0
|
if mprotect(ptr as *mut c_void, page_size() as size_t, GUARD_PROT) == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(IoError::last_os_error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn unmap_stack(ptr: *mut u8, len: usize) -> bool {
|
pub unsafe fn unmap_stack(ptr: *mut u8, len: usize) -> Result<(), IoError> {
|
||||||
munmap(ptr as *mut c_void, len as size_t) == 0
|
if munmap(ptr as *mut c_void, len as size_t) == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(IoError::last_os_error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
// This file is part of libfringe, a low-level green threading library.
|
// This file is part of libfringe, a low-level green threading library.
|
||||||
// Copyright (c) edef <edef@edef.eu>
|
// Copyright (c) edef <edef@edef.eu>
|
||||||
// See the LICENSE file included in this distribution.
|
// See the LICENSE file included in this distribution.
|
||||||
|
extern crate std;
|
||||||
extern crate winapi;
|
extern crate winapi;
|
||||||
extern crate kernel32;
|
extern crate kernel32;
|
||||||
use core::{mem, ptr};
|
use self::std::io::Error as IoError;
|
||||||
|
use self::std::{mem, ptr};
|
||||||
use self::winapi::basetsd::SIZE_T;
|
use self::winapi::basetsd::SIZE_T;
|
||||||
use self::winapi::minwindef::{DWORD, LPVOID};
|
use self::winapi::minwindef::{DWORD, LPVOID};
|
||||||
use self::winapi::winnt::{MEM_COMMIT, MEM_RESERVE, MEM_RELEASE, PAGE_READWRITE, PAGE_NOACCESS};
|
use self::winapi::winnt::{MEM_COMMIT, MEM_RESERVE, MEM_RELEASE, PAGE_READWRITE, PAGE_NOACCESS};
|
||||||
use super::page_size;
|
use super::page_size;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cold]
|
||||||
pub fn sys_page_size() -> usize {
|
pub fn sys_page_size() -> usize {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut info = mem::zeroed();
|
let mut info = mem::zeroed();
|
||||||
|
@ -18,20 +20,30 @@ pub fn sys_page_size() -> usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn map_stack(len: usize) -> Option<*mut u8> {
|
pub unsafe fn map_stack(len: usize) -> Result<*mut u8, IoError> {
|
||||||
let ptr = kernel32::VirtualAlloc(ptr::null_mut(), len as SIZE_T, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
let ptr = kernel32::VirtualAlloc(ptr::null_mut(), len as SIZE_T,
|
||||||
|
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||||
if ptr == ptr::null_mut() {
|
if ptr == ptr::null_mut() {
|
||||||
None
|
Err(IoError::last_os_error())
|
||||||
} else {
|
} else {
|
||||||
Some(ptr as *mut u8)
|
Ok(ptr as *mut u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn protect_stack(ptr: *mut u8) -> bool {
|
pub unsafe fn protect_stack(ptr: *mut u8) -> Result<(), IoError> {
|
||||||
let mut old_prot: DWORD = 0;
|
let mut old_prot: DWORD = 0;
|
||||||
kernel32::VirtualProtect(ptr as LPVOID, page_size() as SIZE_T, PAGE_NOACCESS, &mut old_prot) != 0
|
if kernel32::VirtualProtect(ptr as LPVOID, page_size() as SIZE_T,
|
||||||
|
PAGE_NOACCESS, &mut old_prot) == 0 {
|
||||||
|
Err(IoError::last_os_error())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn unmap_stack(ptr: *mut u8, _len: usize) -> bool {
|
pub unsafe fn unmap_stack(ptr: *mut u8, _len: usize) -> Result<(), IoError> {
|
||||||
kernel32::VirtualFree(ptr as LPVOID, 0, MEM_RELEASE) != 0
|
if kernel32::VirtualFree(ptr as LPVOID, 0, MEM_RELEASE) == 0 {
|
||||||
|
Err(IoError::last_os_error())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
// This file is part of libfringe, a low-level green threading library.
|
||||||
|
// Copyright (c) whitequark <whitequark@whitequark.org>
|
||||||
|
// See the LICENSE file included in this distribution.
|
||||||
|
extern crate fringe;
|
||||||
|
|
||||||
|
use fringe::{Stack, OsStack};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_accessible() {
|
||||||
|
let stack = OsStack::new(4096).unwrap();
|
||||||
|
// Make sure the topmost page of the stack, at least, is accessible.
|
||||||
|
unsafe { *(stack.top().offset(-1)) = 0; }
|
||||||
|
}
|
Loading…
Reference in New Issue