@@ -25,15 +25,36 @@ impl Drop for Handler {
25
25
}
26
26
}
27
27
28
- #[ cfg( any(
29
- target_os = "linux" ,
30
- target_os = "freebsd" ,
31
- target_os = "hurd" ,
32
- target_os = "macos" ,
33
- target_os = "netbsd" ,
34
- target_os = "openbsd" ,
35
- target_os = "solaris" ,
36
- target_os = "illumos" ,
28
+ #[ cfg( all(
29
+ not( miri) ,
30
+ any(
31
+ target_os = "linux" ,
32
+ target_os = "freebsd" ,
33
+ target_os = "hurd" ,
34
+ target_os = "macos" ,
35
+ target_os = "netbsd" ,
36
+ target_os = "openbsd" ,
37
+ target_os = "solaris" ,
38
+ target_os = "illumos" ,
39
+ ) ,
40
+ ) ) ]
41
+ mod thread_info;
42
+
43
+ // miri doesn't model signals nor stack overflows and this code has some
44
+ // synchronization properties that we don't want to expose to user code,
45
+ // hence we disable it on miri.
46
+ #[ cfg( all(
47
+ not( miri) ,
48
+ any(
49
+ target_os = "linux" ,
50
+ target_os = "freebsd" ,
51
+ target_os = "hurd" ,
52
+ target_os = "macos" ,
53
+ target_os = "netbsd" ,
54
+ target_os = "openbsd" ,
55
+ target_os = "solaris" ,
56
+ target_os = "illumos" ,
57
+ )
37
58
) ) ]
38
59
mod imp {
39
60
use libc:: {
@@ -46,22 +67,13 @@ mod imp {
46
67
use libc:: { mmap64, mprotect, munmap} ;
47
68
48
69
use super :: Handler ;
49
- use crate :: cell :: Cell ;
70
+ use super :: thread_info :: { delete_current_info , set_current_info , with_current_info } ;
50
71
use crate :: ops:: Range ;
51
72
use crate :: sync:: OnceLock ;
52
73
use crate :: sync:: atomic:: { Atomic , AtomicBool , AtomicPtr , AtomicUsize , Ordering } ;
53
74
use crate :: sys:: pal:: unix:: os;
54
- use crate :: { io, mem, ptr, thread} ;
55
-
56
- // We use a TLS variable to store the address of the guard page. While TLS
57
- // variables are not guaranteed to be signal-safe, this works out in practice
58
- // since we make sure to write to the variable before the signal stack is
59
- // installed, thereby ensuring that the variable is always allocated when
60
- // the signal handler is called.
61
- thread_local ! {
62
- // FIXME: use `Range` once that implements `Copy`.
63
- static GUARD : Cell <( usize , usize ) > = const { Cell :: new( ( 0 , 0 ) ) } ;
64
- }
75
+ use crate :: thread:: with_current_name;
76
+ use crate :: { io, mem, panic, ptr} ;
65
77
66
78
// Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
67
79
// (unmapped pages) at the end of every thread's stack, so if a thread ends
@@ -93,29 +105,35 @@ mod imp {
93
105
info : * mut libc:: siginfo_t ,
94
106
_data : * mut libc:: c_void ,
95
107
) {
96
- let ( start, end) = GUARD . get ( ) ;
97
108
// SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`.
98
- let addr = unsafe { ( * info) . si_addr ( ) . addr ( ) } ;
109
+ let fault_addr = unsafe { ( * info) . si_addr ( ) . addr ( ) } ;
110
+
111
+ // `with_current_info` expects that the process aborts after it is
112
+ // called. If the signal was not caused by a memory access, this might
113
+ // not be true. We detect this by noticing that the `si_addr` field is
114
+ // zero if the signal is synthetic.
115
+ if fault_addr != 0 {
116
+ with_current_info ( |thread_info| {
117
+ // If the faulting address is within the guard page, then we print a
118
+ // message saying so and abort.
119
+ if let Some ( thread_info) = thread_info
120
+ && thread_info. guard_page_range . contains ( & fault_addr)
121
+ {
122
+ let name = thread_info. thread_name . as_deref ( ) . unwrap_or ( "<unknown>" ) ;
123
+ rtprintpanic ! ( "\n thread '{name}' has overflowed its stack\n " ) ;
124
+ rtabort ! ( "stack overflow" ) ;
125
+ }
126
+ } )
127
+ }
99
128
100
- // If the faulting address is within the guard page, then we print a
101
- // message saying so and abort.
102
- if start <= addr && addr < end {
103
- thread:: with_current_name ( |name| {
104
- let name = name. unwrap_or ( "<unknown>" ) ;
105
- rtprintpanic ! ( "\n thread '{name}' has overflowed its stack\n " ) ;
106
- } ) ;
129
+ // Unregister ourselves by reverting back to the default behavior.
130
+ // SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
131
+ let mut action: sigaction = unsafe { mem:: zeroed ( ) } ;
132
+ action. sa_sigaction = SIG_DFL ;
133
+ // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction
134
+ unsafe { sigaction ( signum, & action, ptr:: null_mut ( ) ) } ;
107
135
108
- rtabort ! ( "stack overflow" ) ;
109
- } else {
110
- // Unregister ourselves by reverting back to the default behavior.
111
- // SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
112
- let mut action: sigaction = unsafe { mem:: zeroed ( ) } ;
113
- action. sa_sigaction = SIG_DFL ;
114
- // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction
115
- unsafe { sigaction ( signum, & action, ptr:: null_mut ( ) ) } ;
116
-
117
- // See comment above for why this function returns.
118
- }
136
+ // See comment above for why this function returns.
119
137
}
120
138
121
139
static PAGE_SIZE : Atomic < usize > = AtomicUsize :: new ( 0 ) ;
@@ -128,9 +146,7 @@ mod imp {
128
146
pub unsafe fn init ( ) {
129
147
PAGE_SIZE . store ( os:: page_size ( ) , Ordering :: Relaxed ) ;
130
148
131
- // Always write to GUARD to ensure the TLS variable is allocated.
132
- let guard = unsafe { install_main_guard ( ) . unwrap_or ( 0 ..0 ) } ;
133
- GUARD . set ( ( guard. start , guard. end ) ) ;
149
+ let mut guard_page_range = unsafe { install_main_guard ( ) } ;
134
150
135
151
// SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
136
152
let mut action: sigaction = unsafe { mem:: zeroed ( ) } ;
@@ -145,7 +161,13 @@ mod imp {
145
161
let handler = unsafe { make_handler ( true ) } ;
146
162
MAIN_ALTSTACK . store ( handler. data , Ordering :: Relaxed ) ;
147
163
mem:: forget ( handler) ;
164
+
165
+ if let Some ( guard_page_range) = guard_page_range. take ( ) {
166
+ let thread_name = with_current_name ( |name| name. map ( Box :: from) ) ;
167
+ set_current_info ( guard_page_range, thread_name) ;
168
+ }
148
169
}
170
+
149
171
action. sa_flags = SA_SIGINFO | SA_ONSTACK ;
150
172
action. sa_sigaction = signal_handler as sighandler_t ;
151
173
// SAFETY: only overriding signals if the default is set
@@ -214,9 +236,10 @@ mod imp {
214
236
}
215
237
216
238
if !main_thread {
217
- // Always write to GUARD to ensure the TLS variable is allocated.
218
- let guard = unsafe { current_guard ( ) } . unwrap_or ( 0 ..0 ) ;
219
- GUARD . set ( ( guard. start , guard. end ) ) ;
239
+ if let Some ( guard_page_range) = unsafe { current_guard ( ) } {
240
+ let thread_name = with_current_name ( |name| name. map ( Box :: from) ) ;
241
+ set_current_info ( guard_page_range, thread_name) ;
242
+ }
220
243
}
221
244
222
245
// SAFETY: assuming stack_t is zero-initializable
@@ -261,6 +284,8 @@ mod imp {
261
284
// a mapping that started one page earlier, so walk back a page and unmap from there.
262
285
unsafe { munmap ( data. sub ( page_size) , sigstack_size + page_size) } ;
263
286
}
287
+
288
+ delete_current_info ( ) ;
264
289
}
265
290
266
291
/// Modern kernels on modern hardware can have dynamic signal stack sizes.
@@ -590,17 +615,20 @@ mod imp {
590
615
// usually have fewer qualms about forwards compatibility, since the runtime
591
616
// is shipped with the OS):
592
617
// <https://github.com/apple/swift/blob/swift-5.10-RELEASE/stdlib/public/runtime/CrashHandlerMacOS.cpp>
593
- #[ cfg( not( any(
594
- target_os = "linux" ,
595
- target_os = "freebsd" ,
596
- target_os = "hurd" ,
597
- target_os = "macos" ,
598
- target_os = "netbsd" ,
599
- target_os = "openbsd" ,
600
- target_os = "solaris" ,
601
- target_os = "illumos" ,
602
- target_os = "cygwin" ,
603
- ) ) ) ]
618
+ #[ cfg( any(
619
+ miri,
620
+ not( any(
621
+ target_os = "linux" ,
622
+ target_os = "freebsd" ,
623
+ target_os = "hurd" ,
624
+ target_os = "macos" ,
625
+ target_os = "netbsd" ,
626
+ target_os = "openbsd" ,
627
+ target_os = "solaris" ,
628
+ target_os = "illumos" ,
629
+ target_os = "cygwin" ,
630
+ ) )
631
+ ) ) ]
604
632
mod imp {
605
633
pub unsafe fn init ( ) { }
606
634
0 commit comments