Skip to content

Commit

Permalink
Auto merge of #43072 - cuviper:linux-stack-guard, r=alexcrichton
Browse files Browse the repository at this point in the history
Skip the main thread's manual stack guard on Linux

Linux doesn't allocate the whole stack right away, and the kernel has its own stack-guard mechanism to fault when growing too close to an existing mapping.  If we map our own guard, then the kernel starts enforcing a rather large gap above that, rendering much of the possible stack space useless.

Instead, we'll just note where we expect rlimit to start faulting, so our handler can report "stack overflow", and trust that the kernel's own stack guard will work.

Fixes #43052.
r? @alexcrichton

### Kernel compatibility:

Strictly speaking, Rust claims support for Linux kernels >= 2.6.18, and stack guards were only added to mainline in 2.6.36 for [CVE-2010-2240].  But since that vulnerability was so severe, the guards were backported to many stable branches, and Red Hat patched this all the way back to RHEL3's 2.4.21!  I think it's reasonable for us to assume that any *supportable* kernel should have these stack guards.

At that time, the kernel only enforced one page of padding between the stack and other mappings, but thanks to [Stack Clash] that padding is now much larger, causing #43052.  The kernel side of those fixes are in [CVE-2017-1000364], which Red Hat has backported to at least RHEL5's 2.6.18 so far.

[CVE-2010-2240]: https://access.redhat.com/security/cve/CVE-2010-2240
[CVE-2017-1000364]: https://access.redhat.com/security/cve/CVE-2017-1000364
[Stack Clash]: https://access.redhat.com/security/vulnerabilities/stackguard
  • Loading branch information
bors committed Jul 8, 2017
2 parents fb4fa60 + be509b3 commit 4b6af97
Showing 1 changed file with 29 additions and 15 deletions.
44 changes: 29 additions & 15 deletions src/libstd/sys/unix/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,23 +264,37 @@ pub mod guard {
as *mut libc::c_void;
}

// Rellocate the last page of the stack.
// This ensures SIGBUS will be raised on
// stack overflow.
let result = mmap(stackaddr, psize, PROT_NONE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);

if result != stackaddr || result == MAP_FAILED {
panic!("failed to allocate a guard page");
}

let offset = if cfg!(any(target_os = "linux", target_os = "freebsd")) {
2
if cfg!(target_os = "linux") {
// Linux doesn't allocate the whole stack right away, and
// the kernel has its own stack-guard mechanism to fault
// when growing too close to an existing mapping. If we map
// our own guard, then the kernel starts enforcing a rather
// large gap above that, rendering much of the possible
// stack space useless. See #43052.
//
// Instead, we'll just note where we expect rlimit to start
// faulting, so our handler can report "stack overflow", and
// trust that the kernel's own stack guard will work.
Some(stackaddr as usize)
} else {
1
};
// Reallocate the last page of the stack.
// This ensures SIGBUS will be raised on
// stack overflow.
let result = mmap(stackaddr, psize, PROT_NONE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);

if result != stackaddr || result == MAP_FAILED {
panic!("failed to allocate a guard page");
}

Some(stackaddr as usize + offset * psize)
let offset = if cfg!(target_os = "freebsd") {
2
} else {
1
};

Some(stackaddr as usize + offset * psize)
}
}

#[cfg(target_os = "solaris")]
Expand Down

0 comments on commit 4b6af97

Please sign in to comment.