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

Use stack probes instead of split stacks for overflow protection on windows #17563

Merged
merged 5 commits into from
Sep 30, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
// but it could be enabled (with patched LLVM)
pub fn is_split_stack_supported(&self) -> bool {
let ref cfg = self.sess().targ_cfg;
cfg.os != abi::OsiOS || cfg.arch != abi::Arm
(cfg.os != abi::OsiOS || cfg.arch != abi::Arm) && cfg.os != abi::OsWindows
}


Expand Down
42 changes: 1 addition & 41 deletions src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,47 +89,7 @@ local_data_key!(pub analysiskey: core::CrateAnalysis)
type Output = (clean::Crate, Vec<plugins::PluginJson> );

pub fn main() {
// Why run rustdoc in a separate task? That's a good question!
//
// We first begin our adventure at the ancient commit of e7c4fb69. In this
// commit it was discovered that the stack limit frobbing on windows ended
// up causing some syscalls to fail. This was worked around manually in the
// relevant location.
//
// Our journey now continues with #13259 where it was discovered that this
// stack limit frobbing has the ability to affect nearly any syscall. Note
// that the key idea here is that there is currently no knowledge as to why
// this is happening or how to preserve it, fun times!
//
// Now we continue along to #16275 where it was discovered that --test on
// windows didn't work at all! Yet curiously rustdoc worked without --test.
// The exact reason that #16275 cropped up is that during the expansion
// phase the compiler attempted to open libstd to read out its macros. This
// invoked the LLVMRustOpenArchive shim which in turned went to LLVM to go
// open a file and read it. Lo and behold this function returned an error!
// It was then discovered that when the same fix mentioned in #13259 was
// applied, the error went away. The plot thickens!
//
// Remember that rustdoc works without --test, which raises the question of
// how because the --test and non --test paths are almost identical. The
// first thing both paths do is parse and expand a crate! It turns out that
// the difference is that --test runs on the *main task* while the normal
// path runs in subtask. It turns out that running --test in a sub task also
// fixes the problem!
//
// So, in summary, it is unknown why this is necessary, what it is
// preventing, or what the actual bug is. In the meantime, this allows
// --test to work on windows, which seems good, right? Fun times.
let (tx, rx) = channel();
spawn(proc() {
std::os::set_exit_status(main_args(std::os::args().as_slice()));
tx.send(());
});

// If the task failed, set an error'd exit status
if rx.recv_opt().is_err() {
std::os::set_exit_status(std::rt::DEFAULT_ERROR_CODE);
}
std::os::set_exit_status(main_args(std::os::args().as_slice()));
}

pub fn opts() -> Vec<getopts::OptGroup> {
Expand Down
19 changes: 4 additions & 15 deletions src/librustrt/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,7 @@ pub unsafe fn record_sp_limit(limit: uint) {
asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
}
#[cfg(target_arch = "x86_64", target_os = "windows")] #[inline(always)]
unsafe fn target_record_sp_limit(limit: uint) {
// see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
// store this inside of the "arbitrary data slot", but double the size
// because this is 64 bit instead of 32 bit
asm!("movq $0, %gs:0x28" :: "r"(limit) :: "volatile")
unsafe fn target_record_sp_limit(_: uint) {
}
#[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
unsafe fn target_record_sp_limit(limit: uint) {
Expand All @@ -228,10 +224,7 @@ pub unsafe fn record_sp_limit(limit: uint) {
asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
}
#[cfg(target_arch = "x86", target_os = "windows")] #[inline(always)]
unsafe fn target_record_sp_limit(limit: uint) {
// see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
// store this inside of the "arbitrary data slot"
asm!("movl $0, %fs:0x14" :: "r"(limit) :: "volatile")
unsafe fn target_record_sp_limit(_: uint) {
}

// mips, arm - Some brave soul can port these to inline asm, but it's over
Expand Down Expand Up @@ -282,9 +275,7 @@ pub unsafe fn get_sp_limit() -> uint {
}
#[cfg(target_arch = "x86_64", target_os = "windows")] #[inline(always)]
unsafe fn target_get_sp_limit() -> uint {
let limit;
asm!("movq %gs:0x28, $0" : "=r"(limit) ::: "volatile");
return limit;
return 1024;
}
#[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
unsafe fn target_get_sp_limit() -> uint {
Expand Down Expand Up @@ -318,9 +309,7 @@ pub unsafe fn get_sp_limit() -> uint {
}
#[cfg(target_arch = "x86", target_os = "windows")] #[inline(always)]
unsafe fn target_get_sp_limit() -> uint {
let limit;
asm!("movl %fs:0x14, $0" : "=r"(limit) ::: "volatile");
return limit;
return 1024;
}

// mips, arm - Some brave soul can port these to inline asm, but it's over
Expand Down
40 changes: 1 addition & 39 deletions src/libstd/rand/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ mod imp {
use os;
use rand::Rng;
use result::{Ok, Err};
use rt::stack;
use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
use self::libc::types::os::arch::extra::{LONG_PTR};
use slice::MutableSlice;
Expand All @@ -159,7 +158,6 @@ mod imp {
static PROV_RSA_FULL: DWORD = 1;
static CRYPT_SILENT: DWORD = 64;
static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
static NTE_BAD_SIGNATURE: DWORD = 0x80090006;

#[allow(non_snake_case)]
extern "system" {
Expand All @@ -178,48 +176,12 @@ mod imp {
/// Create a new `OsRng`.
pub fn new() -> IoResult<OsRng> {
let mut hcp = 0;
let mut ret = unsafe {
let ret = unsafe {
CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
};

// FIXME #13259:
// It turns out that if we can't acquire a context with the
// NTE_BAD_SIGNATURE error code, the documentation states:
//
// The provider DLL signature could not be verified. Either the
// DLL or the digital signature has been tampered with.
//
// Sounds fishy, no? As it turns out, our signature can be bad
// because our Thread Information Block (TIB) isn't exactly what it
// expects. As to why, I have no idea. The only data we store in the
// TIB is the stack limit for each thread, but apparently that's
// enough to make the signature valid.
//
// Furthermore, this error only happens the *first* time we call
// CryptAcquireContext, so we don't have to worry about future
// calls.
//
// Anyway, the fix employed here is that if we see this error, we
// pray that we're not close to the end of the stack, temporarily
// set the stack limit to 0 (what the TIB originally was), acquire a
// context, and then reset the stack limit.
//
// Again, I'm not sure why this is the fix, nor why we're getting
// this error. All I can say is that this seems to allow libnative
// to progress where it otherwise would be hindered. Who knew?
if ret == 0 && os::errno() as DWORD == NTE_BAD_SIGNATURE {
unsafe {
let limit = stack::get_sp_limit();
stack::record_sp_limit(0);
ret = CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
stack::record_sp_limit(limit);
}
}

if ret == 0 {
Err(IoError::last_error())
} else {
Expand Down
48 changes: 48 additions & 0 deletions src/test/run-pass/issue-13259-windows-tcb-trash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate libc;

#[cfg(windows)]
mod imp {
use libc::{c_void, LPVOID, DWORD};
use libc::types::os::arch::extra::LPWSTR;

extern "system" {
fn FormatMessageW(flags: DWORD,
lpSrc: LPVOID,
msgId: DWORD,
langId: DWORD,
buf: LPWSTR,
nsize: DWORD,
args: *const c_void)
-> DWORD;
}

pub fn test() {
let mut buf: [u16, ..50] = [0, ..50];
let ret = unsafe {
FormatMessageW(0x1000, 0 as *mut c_void, 1, 0x400,
buf.as_mut_ptr(), buf.len() as u32, 0 as *const c_void)
};
// On some 32-bit Windowses (Win7-8 at least) this will fail with segmented
// stacks taking control of pvArbitrary
assert!(ret != 0);
}
}

#[cfg(not(windows))]
mod imp {
pub fn test() { }
}

fn main() {
imp::test()
}
10 changes: 8 additions & 2 deletions src/test/run-pass/out-of-stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,17 @@ fn main() {
let silent = Command::new(args[0].as_slice()).arg("silent").output().unwrap();
assert!(!silent.status.success());
let error = String::from_utf8_lossy(silent.error.as_slice());
assert!(error.as_slice().contains("has overflowed its stack"));
// FIXME #17562: Windows is using stack probes and isn't wired up to print an error
if !cfg!(windows) {
assert!(error.as_slice().contains("has overflowed its stack"));
}

let loud = Command::new(args[0].as_slice()).arg("loud").output().unwrap();
assert!(!loud.status.success());
let error = String::from_utf8_lossy(silent.error.as_slice());
assert!(error.as_slice().contains("has overflowed its stack"));
// FIXME #17562: Windows is using stack probes and isn't wired up to print an error
if !cfg!(windows) {
assert!(error.as_slice().contains("has overflowed its stack"));
}
}
}