@@ -50,7 +50,7 @@ mod imp {
50
50
#[ cfg( all( target_os = "linux" , target_env = "gnu" ) ) ]
51
51
use libc:: { mmap64, munmap} ;
52
52
use libc:: { sigaction, sighandler_t, SA_ONSTACK , SA_SIGINFO , SIGBUS , SIG_DFL } ;
53
- use libc:: { sigaltstack, SIGSTKSZ , SS_DISABLE } ;
53
+ use libc:: { sigaltstack, SS_DISABLE } ;
54
54
use libc:: { MAP_ANON , MAP_PRIVATE , PROT_NONE , PROT_READ , PROT_WRITE , SIGSEGV } ;
55
55
56
56
use crate :: sync:: atomic:: { AtomicBool , AtomicPtr , Ordering } ;
@@ -129,28 +129,36 @@ mod imp {
129
129
drop_handler ( MAIN_ALTSTACK . load ( Ordering :: Relaxed ) ) ;
130
130
}
131
131
132
- unsafe fn get_stackp ( ) -> * mut libc:: c_void {
132
+ unsafe fn get_stack ( ) -> libc:: stack_t {
133
133
// OpenBSD requires this flag for stack mapping
134
134
// otherwise the said mapping will fail as a no-op on most systems
135
135
// and has a different meaning on FreeBSD
136
136
#[ cfg( any( target_os = "openbsd" , target_os = "netbsd" , target_os = "linux" , ) ) ]
137
137
let flags = MAP_PRIVATE | MAP_ANON | libc:: MAP_STACK ;
138
138
#[ cfg( not( any( target_os = "openbsd" , target_os = "netbsd" , target_os = "linux" , ) ) ) ]
139
139
let flags = MAP_PRIVATE | MAP_ANON ;
140
- let stackp =
141
- mmap64 ( ptr:: null_mut ( ) , SIGSTKSZ + page_size ( ) , PROT_READ | PROT_WRITE , flags, -1 , 0 ) ;
140
+
141
+ let sigstack_size = sigstack_size ( ) ;
142
+ let page_size = page_size ( ) ;
143
+
144
+ let stackp = mmap64 (
145
+ ptr:: null_mut ( ) ,
146
+ sigstack_size + page_size,
147
+ PROT_READ | PROT_WRITE ,
148
+ flags,
149
+ -1 ,
150
+ 0 ,
151
+ ) ;
142
152
if stackp == MAP_FAILED {
143
153
panic ! ( "failed to allocate an alternative stack: {}" , io:: Error :: last_os_error( ) ) ;
144
154
}
145
- let guard_result = libc:: mprotect ( stackp, page_size ( ) , PROT_NONE ) ;
155
+ let guard_result = libc:: mprotect ( stackp, page_size, PROT_NONE ) ;
146
156
if guard_result != 0 {
147
157
panic ! ( "failed to set up alternative stack guard page: {}" , io:: Error :: last_os_error( ) ) ;
148
158
}
149
- stackp. add ( page_size ( ) )
150
- }
159
+ let stackp = stackp. add ( page_size) ;
151
160
152
- unsafe fn get_stack ( ) -> libc:: stack_t {
153
- libc:: stack_t { ss_sp : get_stackp ( ) , ss_flags : 0 , ss_size : SIGSTKSZ }
161
+ libc:: stack_t { ss_sp : stackp, ss_flags : 0 , ss_size : sigstack_size }
154
162
}
155
163
156
164
pub unsafe fn make_handler ( ) -> Handler {
@@ -171,21 +179,41 @@ mod imp {
171
179
172
180
pub unsafe fn drop_handler ( data : * mut libc:: c_void ) {
173
181
if !data. is_null ( ) {
182
+ let sigstack_size = sigstack_size ( ) ;
183
+ let page_size = page_size ( ) ;
174
184
let stack = libc:: stack_t {
175
185
ss_sp : ptr:: null_mut ( ) ,
176
186
ss_flags : SS_DISABLE ,
177
187
// Workaround for bug in macOS implementation of sigaltstack
178
188
// UNIX2003 which returns ENOMEM when disabling a stack while
179
189
// passing ss_size smaller than MINSIGSTKSZ. According to POSIX
180
190
// both ss_sp and ss_size should be ignored in this case.
181
- ss_size : SIGSTKSZ ,
191
+ ss_size : sigstack_size ,
182
192
} ;
183
193
sigaltstack ( & stack, ptr:: null_mut ( ) ) ;
184
194
// We know from `get_stackp` that the alternate stack we installed is part of a mapping
185
195
// that started one page earlier, so walk back a page and unmap from there.
186
- munmap ( data. sub ( page_size ( ) ) , SIGSTKSZ + page_size ( ) ) ;
196
+ munmap ( data. sub ( page_size) , sigstack_size + page_size) ;
187
197
}
188
198
}
199
+
200
+ /// Modern kernels on modern hardware can have dynamic signal stack sizes.
201
+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
202
+ fn sigstack_size ( ) -> usize {
203
+ // FIXME: reuse const from libc when available?
204
+ const AT_MINSIGSTKSZ : crate :: ffi:: c_ulong = 51 ;
205
+ let dynamic_sigstksz = unsafe { libc:: getauxval ( AT_MINSIGSTKSZ ) } ;
206
+ // If getauxval couldn't find the entry, it returns 0,
207
+ // so take the higher of the "constant" and auxval.
208
+ // This transparently supports older kernels which don't provide AT_MINSIGSTKSZ
209
+ libc:: SIGSTKSZ . max ( dynamic_sigstksz as _ )
210
+ }
211
+
212
+ /// Not all OS support hardware where this is needed.
213
+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
214
+ fn sigstack_size ( ) -> usize {
215
+ libc:: SIGSTKSZ
216
+ }
189
217
}
190
218
191
219
#[ cfg( not( any(
0 commit comments