@@ -44,6 +44,7 @@ mod imp {
4444 use crate :: ops:: Range ;
4545 use crate :: ptr;
4646 use crate :: sync:: atomic:: { AtomicBool , AtomicPtr , AtomicUsize , Ordering } ;
47+ use crate :: sync:: OnceLock ;
4748 use crate :: sys:: pal:: unix:: os;
4849 use crate :: thread;
4950
@@ -306,9 +307,8 @@ mod imp {
306307 ret
307308 }
308309
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 ( ) ? } ;
312312 let stackaddr = stackptr. addr ( ) ;
313313
314314 // Ensure stackaddr is page aligned! A parent process might
@@ -325,104 +325,133 @@ mod imp {
325325 } )
326326 }
327327
328+ #[ forbid( unsafe_op_in_unsafe_fn) ]
328329 unsafe fn install_main_guard ( ) -> Option < Range < usize > > {
329330 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 (
406435 stackptr,
407436 page_size,
408437 PROT_READ | PROT_WRITE ,
409438 MAP_PRIVATE | MAP_ANON | MAP_FIXED ,
410439 -1 ,
411440 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+ }
416446
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+ }
421451
422- let guardaddr = stackptr. addr ( ) ;
452+ let guardaddr = stackptr. addr ( ) ;
423453
424- Some ( guardaddr..guardaddr + page_size)
425- }
454+ Some ( guardaddr..guardaddr + page_size)
426455 }
427456
428457 #[ cfg( any( target_os = "macos" , target_os = "openbsd" , target_os = "solaris" ) ) ]
0 commit comments