@@ -205,8 +205,10 @@ impl Drop for Thread {
205205 not( target_os = "solaris" ) ) ) ]
206206#[ cfg_attr( test, allow( dead_code) ) ]
207207pub mod guard {
208- pub unsafe fn current ( ) -> Option < usize > { None }
209- pub unsafe fn init ( ) -> Option < usize > { None }
208+ use ops:: Range ;
209+ pub type Guard = Range < usize > ;
210+ pub unsafe fn current ( ) -> Option < Guard > { None }
211+ pub unsafe fn init ( ) -> Option < Guard > { None }
210212}
211213
212214
@@ -222,14 +224,43 @@ pub mod guard {
222224 use libc;
223225 use libc:: mmap;
224226 use libc:: { PROT_NONE , MAP_PRIVATE , MAP_ANON , MAP_FAILED , MAP_FIXED } ;
227+ use ops:: Range ;
225228 use sys:: os;
226229
227- #[ cfg( any( target_os = "macos" ,
228- target_os = "bitrig" ,
229- target_os = "openbsd" ,
230- target_os = "solaris" ) ) ]
230+ // This is initialized in init() and only read from after
231+ static mut PAGE_SIZE : usize = 0 ;
232+
233+ pub type Guard = Range < usize > ;
234+
235+ #[ cfg( target_os = "solaris" ) ]
236+ unsafe fn get_stack_start ( ) -> Option < * mut libc:: c_void > {
237+ let mut current_stack: libc:: stack_t = :: mem:: zeroed ( ) ;
238+ assert_eq ! ( libc:: stack_getbounds( & mut current_stack) , 0 ) ;
239+ Some ( current_stack. ss_sp )
240+ }
241+
242+ #[ cfg( target_os = "macos" ) ]
231243 unsafe fn get_stack_start ( ) -> Option < * mut libc:: c_void > {
232- current ( ) . map ( |s| s as * mut libc:: c_void )
244+ let stackaddr = libc:: pthread_get_stackaddr_np ( libc:: pthread_self ( ) ) as usize -
245+ libc:: pthread_get_stacksize_np ( libc:: pthread_self ( ) ) ;
246+ Some ( stackaddr as * mut libc:: c_void )
247+ }
248+
249+ #[ cfg( any( target_os = "openbsd" , target_os = "bitrig" ) ) ]
250+ unsafe fn get_stack_start ( ) -> Option < * mut libc:: c_void > {
251+ let mut current_stack: libc:: stack_t = :: mem:: zeroed ( ) ;
252+ assert_eq ! ( libc:: pthread_stackseg_np( libc:: pthread_self( ) ,
253+ & mut current_stack) , 0 ) ;
254+
255+ let extra = if cfg ! ( target_os = "bitrig" ) { 3 } else { 1 } * PAGE_SIZE ;
256+ let stackaddr = if libc:: pthread_main_np ( ) == 1 {
257+ // main thread
258+ current_stack. ss_sp as usize - current_stack. ss_size + extra
259+ } else {
260+ // new thread
261+ current_stack. ss_sp as usize - current_stack. ss_size
262+ } ;
263+ Some ( stackaddr as * mut libc:: c_void )
233264 }
234265
235266 #[ cfg( any( target_os = "android" , target_os = "freebsd" ,
@@ -253,8 +284,9 @@ pub mod guard {
253284 ret
254285 }
255286
256- pub unsafe fn init ( ) -> Option < usize > {
257- let psize = os:: page_size ( ) ;
287+ pub unsafe fn init ( ) -> Option < Guard > {
288+ PAGE_SIZE = os:: page_size ( ) ;
289+
258290 let mut stackaddr = get_stack_start ( ) ?;
259291
260292 // Ensure stackaddr is page aligned! A parent process might
@@ -263,9 +295,9 @@ pub mod guard {
263295 // stackaddr < stackaddr + stacksize, so if stackaddr is not
264296 // page-aligned, calculate the fix such that stackaddr <
265297 // new_page_aligned_stackaddr < stackaddr + stacksize
266- let remainder = ( stackaddr as usize ) % psize ;
298+ let remainder = ( stackaddr as usize ) % PAGE_SIZE ;
267299 if remainder != 0 {
268- stackaddr = ( ( stackaddr as usize ) + psize - remainder)
300+ stackaddr = ( ( stackaddr as usize ) + PAGE_SIZE - remainder)
269301 as * mut libc:: c_void ;
270302 }
271303
@@ -280,60 +312,42 @@ pub mod guard {
280312 // Instead, we'll just note where we expect rlimit to start
281313 // faulting, so our handler can report "stack overflow", and
282314 // trust that the kernel's own stack guard will work.
283- Some ( stackaddr as usize )
315+ let stackaddr = stackaddr as usize ;
316+ Some ( stackaddr - PAGE_SIZE ..stackaddr)
284317 } else {
285318 // Reallocate the last page of the stack.
286319 // This ensures SIGBUS will be raised on
287320 // stack overflow.
288- let result = mmap ( stackaddr, psize , PROT_NONE ,
321+ let result = mmap ( stackaddr, PAGE_SIZE , PROT_NONE ,
289322 MAP_PRIVATE | MAP_ANON | MAP_FIXED , -1 , 0 ) ;
290323
291324 if result != stackaddr || result == MAP_FAILED {
292325 panic ! ( "failed to allocate a guard page" ) ;
293326 }
294327
328+ let guardaddr = stackaddr as usize ;
295329 let offset = if cfg ! ( target_os = "freebsd" ) {
296330 2
297331 } else {
298332 1
299333 } ;
300334
301- Some ( stackaddr as usize + offset * psize )
335+ Some ( guardaddr..guardaddr + offset * PAGE_SIZE )
302336 }
303337 }
304338
305- #[ cfg( target_os = "solaris" ) ]
306- pub unsafe fn current ( ) -> Option < usize > {
307- let mut current_stack: libc:: stack_t = :: mem:: zeroed ( ) ;
308- assert_eq ! ( libc:: stack_getbounds( & mut current_stack) , 0 ) ;
309- Some ( current_stack. ss_sp as usize )
310- }
311-
312- #[ cfg( target_os = "macos" ) ]
313- pub unsafe fn current ( ) -> Option < usize > {
314- Some ( libc:: pthread_get_stackaddr_np ( libc:: pthread_self ( ) ) as usize -
315- libc:: pthread_get_stacksize_np ( libc:: pthread_self ( ) ) )
316- }
317-
318- #[ cfg( any( target_os = "openbsd" , target_os = "bitrig" ) ) ]
319- pub unsafe fn current ( ) -> Option < usize > {
320- let mut current_stack: libc:: stack_t = :: mem:: zeroed ( ) ;
321- assert_eq ! ( libc:: pthread_stackseg_np( libc:: pthread_self( ) ,
322- & mut current_stack) , 0 ) ;
323-
324- let extra = if cfg ! ( target_os = "bitrig" ) { 3 } else { 1 } * os:: page_size ( ) ;
325- Some ( if libc:: pthread_main_np ( ) == 1 {
326- // main thread
327- current_stack. ss_sp as usize - current_stack. ss_size + extra
328- } else {
329- // new thread
330- current_stack. ss_sp as usize - current_stack. ss_size
331- } )
339+ #[ cfg( any( target_os = "macos" ,
340+ target_os = "bitrig" ,
341+ target_os = "openbsd" ,
342+ target_os = "solaris" ) ) ]
343+ pub unsafe fn current ( ) -> Option < Guard > {
344+ let stackaddr = get_stack_start ( ) ? as usize ;
345+ Some ( stackaddr - PAGE_SIZE ..stackaddr)
332346 }
333347
334348 #[ cfg( any( target_os = "android" , target_os = "freebsd" ,
335349 target_os = "linux" , target_os = "netbsd" , target_os = "l4re" ) ) ]
336- pub unsafe fn current ( ) -> Option < usize > {
350+ pub unsafe fn current ( ) -> Option < Guard > {
337351 let mut ret = None ;
338352 let mut attr: libc:: pthread_attr_t = :: mem:: zeroed ( ) ;
339353 assert_eq ! ( libc:: pthread_attr_init( & mut attr) , 0 ) ;
@@ -352,12 +366,23 @@ pub mod guard {
352366 assert_eq ! ( libc:: pthread_attr_getstack( & attr, & mut stackaddr,
353367 & mut size) , 0 ) ;
354368
369+ let stackaddr = stackaddr as usize ;
355370 ret = if cfg ! ( target_os = "freebsd" ) {
356- Some ( stackaddr as usize - guardsize)
371+ // FIXME does freebsd really fault *below* the guard addr?
372+ let guardaddr = stackaddr - guardsize;
373+ Some ( guardaddr - PAGE_SIZE ..guardaddr)
357374 } else if cfg ! ( target_os = "netbsd" ) {
358- Some ( stackaddr as usize )
375+ Some ( stackaddr - guardsize..stackaddr)
376+ } else if cfg ! ( all( target_os = "linux" , target_env = "gnu" ) ) {
377+ // glibc used to include the guard area within the stack, as noted in the BUGS
378+ // section of `man pthread_attr_getguardsize`. This has been corrected starting
379+ // with glibc 2.27, and in some distro backports, so the guard is now placed at the
380+ // end (below) the stack. There's no easy way for us to know which we have at
381+ // runtime, so we'll just match any fault in the range right above or below the
382+ // stack base to call that fault a stack overflow.
383+ Some ( stackaddr - guardsize..stackaddr + guardsize)
359384 } else {
360- Some ( stackaddr as usize + guardsize)
385+ Some ( stackaddr..stackaddr + guardsize)
361386 } ;
362387 }
363388 assert_eq ! ( libc:: pthread_attr_destroy( & mut attr) , 0 ) ;
0 commit comments