@@ -36,9 +36,9 @@ pub struct TlsData<'tcx> {
3636 /// pthreads-style thread-local storage. 
3737  keys :  BTreeMap < TlsKey ,  TlsEntry < ' tcx > > , 
3838
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 ) > > , 
4242} 
4343
4444impl < ' tcx >  Default  for  TlsData < ' tcx >  { 
@@ -119,26 +119,18 @@ impl<'tcx> TlsData<'tcx> {
119119 } 
120120 } 
121121
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 ( 
132127 & mut  self , 
133128 thread :  ThreadId , 
134129 dtor :  ty:: Instance < ' tcx > , 
135130 data :  Scalar , 
136131 )  -> 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) ) ; 
142134 Ok ( ( ) ) 
143135 } 
144136
@@ -212,7 +204,7 @@ impl VisitProvenance for TlsData<'_> {
212204 for  scalar in  keys. values ( ) . flat_map ( |v| v. data . values ( ) )  { 
213205 scalar. visit_provenance ( visit) ; 
214206 } 
215-  for  ( _,  scalar)  in  macos_thread_dtors. values ( )  { 
207+  for  ( _,  scalar)  in  macos_thread_dtors. values ( ) . flatten ( )  { 
216208 scalar. visit_provenance ( visit) ; 
217209 } 
218210 } 
@@ -245,7 +237,7 @@ impl<'tcx> TlsDtorsState<'tcx> {
245237 "macos"  => { 
246238 // The macOS thread wide destructor runs "before any TLS slots get 
247239 // freed", so do that first. 
248-  this. schedule_macos_tls_dtor ( ) ?; 
240+  this. schedule_macos_tls_dtors ( ) ?; 
249241 // When that destructor is done, go on with the pthread dtors. 
250242 break  ' new_state PthreadDtors ( Default :: default ( ) ) ; 
251243 } 
@@ -328,21 +320,26 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
328320 Ok ( ( ) ) 
329321 } 
330322
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 >  { 
334325 let  this = self . eval_context_mut ( ) ; 
335326 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+  } 
346343 } 
347344 Ok ( ( ) ) 
348345 } 
0 commit comments