Skip to content

Commit d2f8d4c

Browse files
committed
auto merge of #17563 : brson/rust/wintcbfix, r=thestinger
This is the bare minimum to stop using split stacks on Windows, fixing #13259 and #14742, by turning on stack probes for all functions and disabling compiler and runtime support for split stacks on Windows. It does not restore the out-of-stack error message, which requires more runtime work. This includes a test that the Windows TCB is no longer being clobbered, but the out-of-stack test itself is pretty weak, only testing that the program exits abnormally, not that it isn't writing to bogus memory, so I haven't truly verified that this is providing the safety we claim. A more complete solution is in #16388, which has some unresolved issues yet. cc @Zoxc @klutzy @vadimcn
2 parents 7409050 + bdeb1d7 commit d2f8d4c

File tree

6 files changed

+63
-98
lines changed

6 files changed

+63
-98
lines changed

src/librustc/middle/trans/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
546546
// but it could be enabled (with patched LLVM)
547547
pub fn is_split_stack_supported(&self) -> bool {
548548
let ref cfg = self.sess().targ_cfg;
549-
cfg.os != abi::OsiOS || cfg.arch != abi::Arm
549+
(cfg.os != abi::OsiOS || cfg.arch != abi::Arm) && cfg.os != abi::OsWindows
550550
}
551551

552552

src/librustdoc/lib.rs

+1-41
Original file line numberDiff line numberDiff line change
@@ -89,47 +89,7 @@ local_data_key!(pub analysiskey: core::CrateAnalysis)
8989
type Output = (clean::Crate, Vec<plugins::PluginJson> );
9090

9191
pub fn main() {
92-
// Why run rustdoc in a separate task? That's a good question!
93-
//
94-
// We first begin our adventure at the ancient commit of e7c4fb69. In this
95-
// commit it was discovered that the stack limit frobbing on windows ended
96-
// up causing some syscalls to fail. This was worked around manually in the
97-
// relevant location.
98-
//
99-
// Our journey now continues with #13259 where it was discovered that this
100-
// stack limit frobbing has the ability to affect nearly any syscall. Note
101-
// that the key idea here is that there is currently no knowledge as to why
102-
// this is happening or how to preserve it, fun times!
103-
//
104-
// Now we continue along to #16275 where it was discovered that --test on
105-
// windows didn't work at all! Yet curiously rustdoc worked without --test.
106-
// The exact reason that #16275 cropped up is that during the expansion
107-
// phase the compiler attempted to open libstd to read out its macros. This
108-
// invoked the LLVMRustOpenArchive shim which in turned went to LLVM to go
109-
// open a file and read it. Lo and behold this function returned an error!
110-
// It was then discovered that when the same fix mentioned in #13259 was
111-
// applied, the error went away. The plot thickens!
112-
//
113-
// Remember that rustdoc works without --test, which raises the question of
114-
// how because the --test and non --test paths are almost identical. The
115-
// first thing both paths do is parse and expand a crate! It turns out that
116-
// the difference is that --test runs on the *main task* while the normal
117-
// path runs in subtask. It turns out that running --test in a sub task also
118-
// fixes the problem!
119-
//
120-
// So, in summary, it is unknown why this is necessary, what it is
121-
// preventing, or what the actual bug is. In the meantime, this allows
122-
// --test to work on windows, which seems good, right? Fun times.
123-
let (tx, rx) = channel();
124-
spawn(proc() {
125-
std::os::set_exit_status(main_args(std::os::args().as_slice()));
126-
tx.send(());
127-
});
128-
129-
// If the task failed, set an error'd exit status
130-
if rx.recv_opt().is_err() {
131-
std::os::set_exit_status(std::rt::DEFAULT_ERROR_CODE);
132-
}
92+
std::os::set_exit_status(main_args(std::os::args().as_slice()));
13393
}
13494

13595
pub fn opts() -> Vec<getopts::OptGroup> {

src/librustrt/stack.rs

+4-15
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,7 @@ pub unsafe fn record_sp_limit(limit: uint) {
200200
asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
201201
}
202202
#[cfg(target_arch = "x86_64", target_os = "windows")] #[inline(always)]
203-
unsafe fn target_record_sp_limit(limit: uint) {
204-
// see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
205-
// store this inside of the "arbitrary data slot", but double the size
206-
// because this is 64 bit instead of 32 bit
207-
asm!("movq $0, %gs:0x28" :: "r"(limit) :: "volatile")
203+
unsafe fn target_record_sp_limit(_: uint) {
208204
}
209205
#[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
210206
unsafe fn target_record_sp_limit(limit: uint) {
@@ -228,10 +224,7 @@ pub unsafe fn record_sp_limit(limit: uint) {
228224
asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
229225
}
230226
#[cfg(target_arch = "x86", target_os = "windows")] #[inline(always)]
231-
unsafe fn target_record_sp_limit(limit: uint) {
232-
// see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
233-
// store this inside of the "arbitrary data slot"
234-
asm!("movl $0, %fs:0x14" :: "r"(limit) :: "volatile")
227+
unsafe fn target_record_sp_limit(_: uint) {
235228
}
236229

237230
// mips, arm - Some brave soul can port these to inline asm, but it's over
@@ -282,9 +275,7 @@ pub unsafe fn get_sp_limit() -> uint {
282275
}
283276
#[cfg(target_arch = "x86_64", target_os = "windows")] #[inline(always)]
284277
unsafe fn target_get_sp_limit() -> uint {
285-
let limit;
286-
asm!("movq %gs:0x28, $0" : "=r"(limit) ::: "volatile");
287-
return limit;
278+
return 1024;
288279
}
289280
#[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
290281
unsafe fn target_get_sp_limit() -> uint {
@@ -318,9 +309,7 @@ pub unsafe fn get_sp_limit() -> uint {
318309
}
319310
#[cfg(target_arch = "x86", target_os = "windows")] #[inline(always)]
320311
unsafe fn target_get_sp_limit() -> uint {
321-
let limit;
322-
asm!("movl %fs:0x14, $0" : "=r"(limit) ::: "volatile");
323-
return limit;
312+
return 1024;
324313
}
325314

326315
// mips, arm - Some brave soul can port these to inline asm, but it's over

src/libstd/rand/os.rs

+1-39
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ mod imp {
136136
use os;
137137
use rand::Rng;
138138
use result::{Ok, Err};
139-
use rt::stack;
140139
use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
141140
use self::libc::types::os::arch::extra::{LONG_PTR};
142141
use slice::MutableSlice;
@@ -159,7 +158,6 @@ mod imp {
159158
static PROV_RSA_FULL: DWORD = 1;
160159
static CRYPT_SILENT: DWORD = 64;
161160
static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
162-
static NTE_BAD_SIGNATURE: DWORD = 0x80090006;
163161

164162
#[allow(non_snake_case)]
165163
extern "system" {
@@ -178,48 +176,12 @@ mod imp {
178176
/// Create a new `OsRng`.
179177
pub fn new() -> IoResult<OsRng> {
180178
let mut hcp = 0;
181-
let mut ret = unsafe {
179+
let ret = unsafe {
182180
CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
183181
PROV_RSA_FULL,
184182
CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
185183
};
186184

187-
// FIXME #13259:
188-
// It turns out that if we can't acquire a context with the
189-
// NTE_BAD_SIGNATURE error code, the documentation states:
190-
//
191-
// The provider DLL signature could not be verified. Either the
192-
// DLL or the digital signature has been tampered with.
193-
//
194-
// Sounds fishy, no? As it turns out, our signature can be bad
195-
// because our Thread Information Block (TIB) isn't exactly what it
196-
// expects. As to why, I have no idea. The only data we store in the
197-
// TIB is the stack limit for each thread, but apparently that's
198-
// enough to make the signature valid.
199-
//
200-
// Furthermore, this error only happens the *first* time we call
201-
// CryptAcquireContext, so we don't have to worry about future
202-
// calls.
203-
//
204-
// Anyway, the fix employed here is that if we see this error, we
205-
// pray that we're not close to the end of the stack, temporarily
206-
// set the stack limit to 0 (what the TIB originally was), acquire a
207-
// context, and then reset the stack limit.
208-
//
209-
// Again, I'm not sure why this is the fix, nor why we're getting
210-
// this error. All I can say is that this seems to allow libnative
211-
// to progress where it otherwise would be hindered. Who knew?
212-
if ret == 0 && os::errno() as DWORD == NTE_BAD_SIGNATURE {
213-
unsafe {
214-
let limit = stack::get_sp_limit();
215-
stack::record_sp_limit(0);
216-
ret = CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
217-
PROV_RSA_FULL,
218-
CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
219-
stack::record_sp_limit(limit);
220-
}
221-
}
222-
223185
if ret == 0 {
224186
Err(IoError::last_error())
225187
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
extern crate libc;
12+
13+
#[cfg(windows)]
14+
mod imp {
15+
use libc::{c_void, LPVOID, DWORD};
16+
use libc::types::os::arch::extra::LPWSTR;
17+
18+
extern "system" {
19+
fn FormatMessageW(flags: DWORD,
20+
lpSrc: LPVOID,
21+
msgId: DWORD,
22+
langId: DWORD,
23+
buf: LPWSTR,
24+
nsize: DWORD,
25+
args: *const c_void)
26+
-> DWORD;
27+
}
28+
29+
pub fn test() {
30+
let mut buf: [u16, ..50] = [0, ..50];
31+
let ret = unsafe {
32+
FormatMessageW(0x1000, 0 as *mut c_void, 1, 0x400,
33+
buf.as_mut_ptr(), buf.len() as u32, 0 as *const c_void)
34+
};
35+
// On some 32-bit Windowses (Win7-8 at least) this will fail with segmented
36+
// stacks taking control of pvArbitrary
37+
assert!(ret != 0);
38+
}
39+
}
40+
41+
#[cfg(not(windows))]
42+
mod imp {
43+
pub fn test() { }
44+
}
45+
46+
fn main() {
47+
imp::test()
48+
}

src/test/run-pass/out-of-stack.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,17 @@ fn main() {
4242
let silent = Command::new(args[0].as_slice()).arg("silent").output().unwrap();
4343
assert!(!silent.status.success());
4444
let error = String::from_utf8_lossy(silent.error.as_slice());
45-
assert!(error.as_slice().contains("has overflowed its stack"));
45+
// FIXME #17562: Windows is using stack probes and isn't wired up to print an error
46+
if !cfg!(windows) {
47+
assert!(error.as_slice().contains("has overflowed its stack"));
48+
}
4649

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

0 commit comments

Comments
 (0)