@@ -44,6 +44,7 @@ mod imp {
44
44
use crate :: ops:: Range ;
45
45
use crate :: ptr;
46
46
use crate :: sync:: atomic:: { AtomicBool , AtomicPtr , AtomicUsize , Ordering } ;
47
+ use crate :: sync:: OnceLock ;
47
48
use crate :: sys:: pal:: unix:: os;
48
49
use crate :: thread;
49
50
@@ -306,9 +307,8 @@ mod imp {
306
307
ret
307
308
}
308
309
309
- unsafe fn get_stack_start_aligned ( ) -> Option < * mut libc:: c_void > {
310
- let page_size = PAGE_SIZE . load ( Ordering :: Relaxed ) ;
311
- let stackptr = get_stack_start ( ) ?;
310
+ fn stack_start_aligned ( page_size : usize ) -> Option < * mut libc:: c_void > {
311
+ let stackptr = unsafe { get_stack_start ( ) ? } ;
312
312
let stackaddr = stackptr. addr ( ) ;
313
313
314
314
// Ensure stackaddr is page aligned! A parent process might
@@ -325,104 +325,133 @@ mod imp {
325
325
} )
326
326
}
327
327
328
+ #[ forbid( unsafe_op_in_unsafe_fn) ]
328
329
unsafe fn install_main_guard ( ) -> Option < Range < usize > > {
329
330
let page_size = PAGE_SIZE . load ( Ordering :: Relaxed ) ;
330
- if cfg ! ( all( target_os = "linux" , not( target_env = "musl" ) ) ) {
331
- // Linux doesn't allocate the whole stack right away, and
332
- // the kernel has its own stack-guard mechanism to fault
333
- // when growing too close to an existing mapping. If we map
334
- // our own guard, then the kernel starts enforcing a rather
335
- // large gap above that, rendering much of the possible
336
- // stack space useless. See #43052.
337
- //
338
- // Instead, we'll just note where we expect rlimit to start
339
- // faulting, so our handler can report "stack overflow", and
340
- // trust that the kernel's own stack guard will work.
341
- let stackptr = get_stack_start_aligned ( ) ?;
342
- let stackaddr = stackptr. addr ( ) ;
343
- Some ( stackaddr - page_size..stackaddr)
344
- } else if cfg ! ( all( target_os = "linux" , target_env = "musl" ) ) {
345
- // For the main thread, the musl's pthread_attr_getstack
346
- // returns the current stack size, rather than maximum size
347
- // it can eventually grow to. It cannot be used to determine
348
- // the position of kernel's stack guard.
349
- None
350
- } else if cfg ! ( target_os = "freebsd" ) {
351
- // FreeBSD's stack autogrows, and optionally includes a guard page
352
- // at the bottom. If we try to remap the bottom of the stack
353
- // ourselves, FreeBSD's guard page moves upwards. So we'll just use
354
- // the builtin guard page.
355
- let stackptr = get_stack_start_aligned ( ) ?;
356
- let guardaddr = stackptr. addr ( ) ;
357
- // Technically the number of guard pages is tunable and controlled
358
- // by the security.bsd.stack_guard_page sysctl.
359
- // By default it is 1, checking once is enough since it is
360
- // a boot time config value.
361
- static PAGES : crate :: sync:: OnceLock < usize > = crate :: sync:: OnceLock :: new ( ) ;
362
-
363
- let pages = PAGES . get_or_init ( || {
364
- use crate :: sys:: weak:: dlsym;
365
- dlsym ! ( fn sysctlbyname( * const libc:: c_char, * mut libc:: c_void, * mut libc:: size_t, * const libc:: c_void, libc:: size_t) -> libc:: c_int) ;
366
- let mut guard: usize = 0 ;
367
- let mut size = crate :: mem:: size_of_val ( & guard) ;
368
- let oid = crate :: ffi:: CStr :: from_bytes_with_nul (
369
- b"security.bsd.stack_guard_page\0 " ,
370
- )
371
- . unwrap ( ) ;
372
- match sysctlbyname. get ( ) {
373
- Some ( fcn) => {
374
- if fcn ( oid. as_ptr ( ) , core:: ptr:: addr_of_mut!( guard) as * mut _ , core:: ptr:: addr_of_mut!( size) as * mut _ , crate :: ptr:: null_mut ( ) , 0 ) == 0 {
375
- guard
376
- } else {
377
- 1
378
- }
379
- } ,
380
- _ => 1 ,
381
- }
382
- } ) ;
383
- Some ( guardaddr..guardaddr + pages * page_size)
384
- } else if cfg ! ( any( target_os = "openbsd" , target_os = "netbsd" ) ) {
385
- // OpenBSD stack already includes a guard page, and stack is
386
- // immutable.
387
- // NetBSD stack includes the guard page.
388
- //
389
- // We'll just note where we expect rlimit to start
390
- // faulting, so our handler can report "stack overflow", and
391
- // trust that the kernel's own stack guard will work.
392
- let stackptr = get_stack_start_aligned ( ) ?;
393
- let stackaddr = stackptr. addr ( ) ;
394
- Some ( stackaddr - page_size..stackaddr)
395
- } else {
396
- // Reallocate the last page of the stack.
397
- // This ensures SIGBUS will be raised on
398
- // stack overflow.
399
- // Systems which enforce strict PAX MPROTECT do not allow
400
- // to mprotect() a mapping with less restrictive permissions
401
- // than the initial mmap() used, so we mmap() here with
402
- // read/write permissions and only then mprotect() it to
403
- // no permissions at all. See issue #50313.
404
- let stackptr = get_stack_start_aligned ( ) ?;
405
- let result = mmap64 (
331
+
332
+ unsafe {
333
+ // this way someone on any unix-y OS can check that all these compile
334
+ if cfg ! ( all( target_os = "linux" , not( target_env = "musl" ) ) ) {
335
+ install_main_guard_linux ( page_size)
336
+ } else if cfg ! ( all( target_os = "linux" , target_env = "musl" ) ) {
337
+ install_main_guard_linux_musl ( page_size)
338
+ } else if cfg ! ( target_os = "freebsd" ) {
339
+ install_main_guard_freebsd ( page_size)
340
+ } else if cfg ! ( any( target_os = "netbsd" , target_os = "openbsd" ) ) {
341
+ install_main_guard_bsds ( page_size)
342
+ } else {
343
+ install_main_guard_default ( page_size)
344
+ }
345
+ }
346
+ }
347
+
348
+ #[ forbid( unsafe_op_in_unsafe_fn) ]
349
+ unsafe fn install_main_guard_linux ( page_size : usize ) -> Option < Range < usize > > {
350
+ // Linux doesn't allocate the whole stack right away, and
351
+ // the kernel has its own stack-guard mechanism to fault
352
+ // when growing too close to an existing mapping. If we map
353
+ // our own guard, then the kernel starts enforcing a rather
354
+ // large gap above that, rendering much of the possible
355
+ // stack space useless. See #43052.
356
+ //
357
+ // Instead, we'll just note where we expect rlimit to start
358
+ // faulting, so our handler can report "stack overflow", and
359
+ // trust that the kernel's own stack guard will work.
360
+ let stackptr = stack_start_aligned ( page_size) ?;
361
+ let stackaddr = stackptr. addr ( ) ;
362
+ Some ( stackaddr - page_size..stackaddr)
363
+ }
364
+
365
+ #[ forbid( unsafe_op_in_unsafe_fn) ]
366
+ unsafe fn install_main_guard_linux_musl ( _page_size : usize ) -> Option < Range < usize > > {
367
+ // For the main thread, the musl's pthread_attr_getstack
368
+ // returns the current stack size, rather than maximum size
369
+ // it can eventually grow to. It cannot be used to determine
370
+ // the position of kernel's stack guard.
371
+ None
372
+ }
373
+
374
+ #[ forbid( unsafe_op_in_unsafe_fn) ]
375
+ unsafe fn install_main_guard_freebsd ( page_size : usize ) -> Option < Range < usize > > {
376
+ // FreeBSD's stack autogrows, and optionally includes a guard page
377
+ // at the bottom. If we try to remap the bottom of the stack
378
+ // ourselves, FreeBSD's guard page moves upwards. So we'll just use
379
+ // the builtin guard page.
380
+ let stackptr = stack_start_aligned ( page_size) ?;
381
+ let guardaddr = stackptr. addr ( ) ;
382
+ // Technically the number of guard pages is tunable and controlled
383
+ // by the security.bsd.stack_guard_page sysctl.
384
+ // By default it is 1, checking once is enough since it is
385
+ // a boot time config value.
386
+ static PAGES : OnceLock < usize > = OnceLock :: new ( ) ;
387
+
388
+ let pages = PAGES . get_or_init ( || {
389
+ use crate :: sys:: weak:: dlsym;
390
+ dlsym ! ( fn sysctlbyname( * const libc:: c_char, * mut libc:: c_void, * mut libc:: size_t, * const libc:: c_void, libc:: size_t) -> libc:: c_int) ;
391
+ let mut guard: usize = 0 ;
392
+ let mut size = mem:: size_of_val ( & guard) ;
393
+ let oid = c"security.bsd.stack_guard_page" ;
394
+ match sysctlbyname. get ( ) {
395
+ Some ( fcn) if unsafe {
396
+ fcn ( oid. as_ptr ( ) ,
397
+ ptr:: addr_of_mut!( guard) . cast ( ) ,
398
+ ptr:: addr_of_mut!( size) ,
399
+ ptr:: null_mut ( ) ,
400
+ 0 ) == 0
401
+ } => guard,
402
+ _ => 1 ,
403
+ }
404
+ } ) ;
405
+ Some ( guardaddr..guardaddr + pages * page_size)
406
+ }
407
+
408
+ #[ forbid( unsafe_op_in_unsafe_fn) ]
409
+ unsafe fn install_main_guard_bsds ( page_size : usize ) -> Option < Range < usize > > {
410
+ // OpenBSD stack already includes a guard page, and stack is
411
+ // immutable.
412
+ // NetBSD stack includes the guard page.
413
+ //
414
+ // We'll just note where we expect rlimit to start
415
+ // faulting, so our handler can report "stack overflow", and
416
+ // trust that the kernel's own stack guard will work.
417
+ let stackptr = stack_start_aligned ( page_size) ?;
418
+ let stackaddr = stackptr. addr ( ) ;
419
+ Some ( stackaddr - page_size..stackaddr)
420
+ }
421
+
422
+ #[ forbid( unsafe_op_in_unsafe_fn) ]
423
+ unsafe fn install_main_guard_default ( page_size : usize ) -> Option < Range < usize > > {
424
+ // Reallocate the last page of the stack.
425
+ // This ensures SIGBUS will be raised on
426
+ // stack overflow.
427
+ // Systems which enforce strict PAX MPROTECT do not allow
428
+ // to mprotect() a mapping with less restrictive permissions
429
+ // than the initial mmap() used, so we mmap() here with
430
+ // read/write permissions and only then mprotect() it to
431
+ // no permissions at all. See issue #50313.
432
+ let stackptr = stack_start_aligned ( page_size) ?;
433
+ let result = unsafe {
434
+ mmap64 (
406
435
stackptr,
407
436
page_size,
408
437
PROT_READ | PROT_WRITE ,
409
438
MAP_PRIVATE | MAP_ANON | MAP_FIXED ,
410
439
-1 ,
411
440
0 ,
412
- ) ;
413
- if result != stackptr || result == MAP_FAILED {
414
- panic ! ( "failed to allocate a guard page: {}" , io:: Error :: last_os_error( ) ) ;
415
- }
441
+ )
442
+ } ;
443
+ if result != stackptr || result == MAP_FAILED {
444
+ panic ! ( "failed to allocate a guard page: {}" , io:: Error :: last_os_error( ) ) ;
445
+ }
416
446
417
- let result = mprotect ( stackptr, page_size, PROT_NONE ) ;
418
- if result != 0 {
419
- panic ! ( "failed to protect the guard page: {}" , io:: Error :: last_os_error( ) ) ;
420
- }
447
+ let result = unsafe { mprotect ( stackptr, page_size, PROT_NONE ) } ;
448
+ if result != 0 {
449
+ panic ! ( "failed to protect the guard page: {}" , io:: Error :: last_os_error( ) ) ;
450
+ }
421
451
422
- let guardaddr = stackptr. addr ( ) ;
452
+ let guardaddr = stackptr. addr ( ) ;
423
453
424
- Some ( guardaddr..guardaddr + page_size)
425
- }
454
+ Some ( guardaddr..guardaddr + page_size)
426
455
}
427
456
428
457
#[ cfg( any( target_os = "macos" , target_os = "openbsd" , target_os = "solaris" ) ) ]
0 commit comments