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:
whitequark 2016-07-17 00:34:26 +00:00 committed by edef
parent 75ca6f66e1
commit ebd9ca8dec
5 changed files with 67 additions and 40 deletions

View File

@ -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"

View File

@ -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())
}
}
} }
} }

View File

@ -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())
}
} }

View File

@ -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(())
}
} }

13
tests/stack.rs Normal file
View File

@ -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; }
}