@@ -205,8 +205,10 @@ impl Drop for Thread {
205
205
not( target_os = "solaris" ) ) ) ]
206
206
#[ cfg_attr( test, allow( dead_code) ) ]
207
207
pub 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 }
210
212
}
211
213
212
214
@@ -222,14 +224,43 @@ pub mod guard {
222
224
use libc;
223
225
use libc:: mmap;
224
226
use libc:: { PROT_NONE , MAP_PRIVATE , MAP_ANON , MAP_FAILED , MAP_FIXED } ;
227
+ use ops:: Range ;
225
228
use sys:: os;
226
229
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" ) ]
231
243
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 )
233
264
}
234
265
235
266
#[ cfg( any( target_os = "android" , target_os = "freebsd" ,
@@ -253,8 +284,9 @@ pub mod guard {
253
284
ret
254
285
}
255
286
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
+
258
290
let mut stackaddr = get_stack_start ( ) ?;
259
291
260
292
// Ensure stackaddr is page aligned! A parent process might
@@ -263,9 +295,9 @@ pub mod guard {
263
295
// stackaddr < stackaddr + stacksize, so if stackaddr is not
264
296
// page-aligned, calculate the fix such that stackaddr <
265
297
// new_page_aligned_stackaddr < stackaddr + stacksize
266
- let remainder = ( stackaddr as usize ) % psize ;
298
+ let remainder = ( stackaddr as usize ) % PAGE_SIZE ;
267
299
if remainder != 0 {
268
- stackaddr = ( ( stackaddr as usize ) + psize - remainder)
300
+ stackaddr = ( ( stackaddr as usize ) + PAGE_SIZE - remainder)
269
301
as * mut libc:: c_void ;
270
302
}
271
303
@@ -280,60 +312,42 @@ pub mod guard {
280
312
// Instead, we'll just note where we expect rlimit to start
281
313
// faulting, so our handler can report "stack overflow", and
282
314
// 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)
284
317
} else {
285
318
// Reallocate the last page of the stack.
286
319
// This ensures SIGBUS will be raised on
287
320
// stack overflow.
288
- let result = mmap ( stackaddr, psize , PROT_NONE ,
321
+ let result = mmap ( stackaddr, PAGE_SIZE , PROT_NONE ,
289
322
MAP_PRIVATE | MAP_ANON | MAP_FIXED , -1 , 0 ) ;
290
323
291
324
if result != stackaddr || result == MAP_FAILED {
292
325
panic ! ( "failed to allocate a guard page" ) ;
293
326
}
294
327
328
+ let guardaddr = stackaddr as usize ;
295
329
let offset = if cfg ! ( target_os = "freebsd" ) {
296
330
2
297
331
} else {
298
332
1
299
333
} ;
300
334
301
- Some ( stackaddr as usize + offset * psize )
335
+ Some ( guardaddr..guardaddr + offset * PAGE_SIZE )
302
336
}
303
337
}
304
338
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)
332
346
}
333
347
334
348
#[ cfg( any( target_os = "android" , target_os = "freebsd" ,
335
349
target_os = "linux" , target_os = "netbsd" , target_os = "l4re" ) ) ]
336
- pub unsafe fn current ( ) -> Option < usize > {
350
+ pub unsafe fn current ( ) -> Option < Guard > {
337
351
let mut ret = None ;
338
352
let mut attr: libc:: pthread_attr_t = :: mem:: zeroed ( ) ;
339
353
assert_eq ! ( libc:: pthread_attr_init( & mut attr) , 0 ) ;
@@ -352,12 +366,23 @@ pub mod guard {
352
366
assert_eq ! ( libc:: pthread_attr_getstack( & attr, & mut stackaddr,
353
367
& mut size) , 0 ) ;
354
368
369
+ let stackaddr = stackaddr as usize ;
355
370
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)
357
374
} 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)
359
384
} else {
360
- Some ( stackaddr as usize + guardsize)
385
+ Some ( stackaddr..stackaddr + guardsize)
361
386
} ;
362
387
}
363
388
assert_eq ! ( libc:: pthread_attr_destroy( & mut attr) , 0 ) ;
0 commit comments