@@ -36,9 +36,9 @@ pub struct TlsData<'tcx> {
36
36
/// pthreads-style thread-local storage.
37
37
keys : BTreeMap < TlsKey , TlsEntry < ' tcx > > ,
38
38
39
- /// A single per thread destructor of the thread local storage (that's how
40
- /// things work on macOS) with a data argument .
41
- macos_thread_dtors : BTreeMap < ThreadId , ( ty:: Instance < ' tcx > , Scalar ) > ,
39
+ /// On macOS, each thread holds a list of destructor functions with their
40
+ /// respective data arguments .
41
+ macos_thread_dtors : BTreeMap < ThreadId , Vec < ( ty:: Instance < ' tcx > , Scalar ) > > ,
42
42
}
43
43
44
44
impl < ' tcx > Default for TlsData < ' tcx > {
@@ -119,26 +119,18 @@ impl<'tcx> TlsData<'tcx> {
119
119
}
120
120
}
121
121
122
- /// Set the thread wide destructor of the thread local storage for the given
123
- /// thread. This function is used to implement `_tlv_atexit` shim on MacOS.
124
- ///
125
- /// Thread wide dtors are available only on MacOS. There is one destructor
126
- /// per thread as can be guessed from the following comment in the
127
- /// [`_tlv_atexit`
128
- /// implementation](https://github.com/opensource-apple/dyld/blob/195030646877261f0c8c7ad8b001f52d6a26f514/src/threadLocalVariables.c#L389):
129
- ///
130
- /// NOTE: this does not need locks because it only operates on current thread data
131
- pub fn set_macos_thread_dtor (
122
+ /// Add a thread local storage for the given thread. This function is used to
123
+ /// implement `_tlv_atexit` shim on MacOS.
124
+ /// FIXME: also implement `__cxa_thread_atexit_impl` for Linux using this, see
125
+ /// https://sourceware.org/glibc/wiki/Destructor%20support%20for%20thread_local%20variables
126
+ pub fn add_macos_thread_dtor (
132
127
& mut self ,
133
128
thread : ThreadId ,
134
129
dtor : ty:: Instance < ' tcx > ,
135
130
data : Scalar ,
136
131
) -> InterpResult < ' tcx > {
137
- if self . macos_thread_dtors . insert ( thread, ( dtor, data) ) . is_some ( ) {
138
- throw_unsup_format ! (
139
- "setting more than one thread local storage destructor for the same thread is not supported"
140
- ) ;
141
- }
132
+ let dtors = self . macos_thread_dtors . entry ( thread) . or_default ( ) ;
133
+ dtors. push ( ( dtor, data) ) ;
142
134
Ok ( ( ) )
143
135
}
144
136
@@ -212,7 +204,7 @@ impl VisitProvenance for TlsData<'_> {
212
204
for scalar in keys. values ( ) . flat_map ( |v| v. data . values ( ) ) {
213
205
scalar. visit_provenance ( visit) ;
214
206
}
215
- for ( _, scalar) in macos_thread_dtors. values ( ) {
207
+ for ( _, scalar) in macos_thread_dtors. values ( ) . flatten ( ) {
216
208
scalar. visit_provenance ( visit) ;
217
209
}
218
210
}
@@ -245,7 +237,7 @@ impl<'tcx> TlsDtorsState<'tcx> {
245
237
"macos" => {
246
238
// The macOS thread wide destructor runs "before any TLS slots get
247
239
// freed", so do that first.
248
- this. schedule_macos_tls_dtor ( ) ?;
240
+ this. schedule_macos_tls_dtors ( ) ?;
249
241
// When that destructor is done, go on with the pthread dtors.
250
242
break ' new_state PthreadDtors ( Default :: default ( ) ) ;
251
243
}
@@ -328,21 +320,26 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
328
320
Ok ( ( ) )
329
321
}
330
322
331
- /// Schedule the MacOS thread destructor of the thread local storage to be
332
- /// executed.
333
- fn schedule_macos_tls_dtor ( & mut self ) -> InterpResult < ' tcx > {
323
+ /// Schedule the macOS thread local storage destructors to be executed.
324
+ fn schedule_macos_tls_dtors ( & mut self ) -> InterpResult < ' tcx > {
334
325
let this = self . eval_context_mut ( ) ;
335
326
let thread_id = this. active_thread ( ) ;
336
- if let Some ( ( instance, data) ) = this. machine . tls . macos_thread_dtors . remove ( & thread_id) {
337
- trace ! ( "Running macos dtor {:?} on {:?} at {:?}" , instance, data, thread_id) ;
338
-
339
- this. call_function (
340
- instance,
341
- Abi :: C { unwind : false } ,
342
- & [ data. into ( ) ] ,
343
- None ,
344
- StackPopCleanup :: Root { cleanup : true } ,
345
- ) ?;
327
+ if let Some ( dtors) = this. machine . tls . macos_thread_dtors . remove ( & thread_id) {
328
+ // macOS runs TLS destructors in the reverse order of their registration.
329
+ // See https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/DyldRuntimeState.cpp#L2277
330
+ // FIXME: Old macOS versions had a use-after-free bug that triggered when
331
+ // _tlv_atexit was run inside a destructor. Maybe miri should catch that?
332
+ for ( instance, data) in dtors. into_iter ( ) . rev ( ) {
333
+ trace ! ( "Running macos dtor {:?} on {:?} at {:?}" , instance, data, thread_id) ;
334
+
335
+ this. call_function (
336
+ instance,
337
+ Abi :: C { unwind : false } ,
338
+ & [ data. into ( ) ] ,
339
+ None ,
340
+ StackPopCleanup :: Root { cleanup : true } ,
341
+ ) ?;
342
+ }
346
343
}
347
344
Ok ( ( ) )
348
345
}
0 commit comments