@@ -10,6 +10,11 @@ use super::messages::{Confirmation, MemEvents, StartFfiInfo, TraceRequest};
1010use super :: parent:: { ChildListener , sv_loop} ;
1111use crate :: alloc:: isolated_alloc:: IsolatedAlloc ;
1212
13+ /// A handle to the single, shared supervisor process across all `MiriMachine`s.
14+ /// Since it would be very difficult to trace multiple FFI calls in parallel, we
15+ /// need to ensure that either (a) only one `MiriMachine` is performing an FFI call
16+ /// at any given time, or (b) there are distinct supervisor and child processes for
17+ /// each machine. The former was chosen here.
1318static SUPERVISOR : std:: sync:: Mutex < Option < Supervisor > > = std:: sync:: Mutex :: new ( None ) ;
1419
1520/// The main means of communication between the child and parent process,
@@ -24,6 +29,12 @@ pub struct Supervisor {
2429 event_rx : ipc:: IpcReceiver < MemEvents > ,
2530}
2631
32+ pub struct SvFfiGuard < ' a > {
33+ alloc : & ' a Rc < RefCell < IsolatedAlloc > > ,
34+ sv_guard : std:: sync:: MutexGuard < ' static , Option < Supervisor > > ,
35+ cb_stack : Option < * mut [ u8 ; CALLBACK_STACK_SIZE ] > ,
36+ }
37+
2738/// Marker representing that an error occurred during creation of the supervisor.
2839#[ derive( Debug ) ]
2940pub struct SvInitError ;
@@ -46,20 +57,24 @@ impl Supervisor {
4657 /// after the desired call has concluded.
4758 pub unsafe fn start_ffi (
4859 alloc : & Rc < RefCell < IsolatedAlloc > > ,
49- ) -> ( std :: sync :: MutexGuard < ' static , Option < Supervisor > > , Option < * mut [ u8 ; CALLBACK_STACK_SIZE ] > )
60+ ) -> SvFfiGuard < ' _ >
5061 {
5162 let mut sv_guard = SUPERVISOR . lock ( ) . unwrap ( ) ;
5263 // If the supervisor is not initialised for whatever reason, fast-return.
5364 // This might be desired behaviour, as even on platforms where ptracing
5465 // is not implemented it enables us to enforce that only one FFI call
5566 // happens at a time.
5667 let Some ( sv) = sv_guard. as_mut ( ) else {
57- return ( sv_guard, None ) ;
68+ return SvFfiGuard {
69+ alloc,
70+ sv_guard,
71+ cb_stack : None ,
72+ } ;
5873 } ;
5974
6075 // Get pointers to all the pages the supervisor must allow accesses in
6176 // and prepare the callback stack.
62- let page_ptrs = alloc. borrow ( ) . pages ( ) ;
77+ let page_ptrs = alloc. borrow ( ) . pages ( ) . collect ( ) ;
6378 let raw_stack_ptr: * mut [ u8 ; CALLBACK_STACK_SIZE ] =
6479 Box :: leak ( Box :: new ( [ 0u8 ; CALLBACK_STACK_SIZE ] ) ) . as_mut_ptr ( ) . cast ( ) ;
6580 let stack_ptr = raw_stack_ptr. expose_provenance ( ) ;
@@ -86,7 +101,11 @@ impl Supervisor {
86101 // modifications to our memory - simply waiting on the recv() doesn't
87102 // count.
88103 signal:: raise ( signal:: SIGSTOP ) . unwrap ( ) ;
89- ( sv_guard, Some ( raw_stack_ptr) )
104+ SvFfiGuard {
105+ alloc,
106+ sv_guard,
107+ cb_stack : Some ( raw_stack_ptr) ,
108+ }
90109 }
91110
92111 /// Undoes FFI-related preparations, allowing Miri to continue as normal, then
@@ -98,10 +117,12 @@ impl Supervisor {
98117 /// received by a prior call to `start_ffi`, and the allocator must be the
99118 /// one passed to it also.
100119 pub unsafe fn end_ffi (
101- alloc : & Rc < RefCell < IsolatedAlloc > > ,
102- mut sv_guard : std:: sync:: MutexGuard < ' static , Option < Supervisor > > ,
103- raw_stack_ptr : Option < * mut [ u8 ; CALLBACK_STACK_SIZE ] > ,
120+ guard : SvFfiGuard < ' _ > ,
104121 ) -> Option < MemEvents > {
122+ let alloc = guard. alloc ;
123+ let mut sv_guard = guard. sv_guard ;
124+ let cb_stack = guard. cb_stack ;
125+
105126 // We can't use IPC channels here to signal that FFI mode has ended,
106127 // since they might allocate memory which could get us stuck in a SIGTRAP
107128 // with no easy way out! While this could be worked around, it is much
@@ -121,7 +142,7 @@ impl Supervisor {
121142 // SAFETY: Caller upholds that this pointer was allocated as a box with
122143 // this type.
123144 unsafe {
124- drop ( Box :: from_raw ( raw_stack_ptr . unwrap ( ) ) ) ;
145+ drop ( Box :: from_raw ( cb_stack . unwrap ( ) ) ) ;
125146 }
126147 // On the off-chance something really weird happens, don't block forever.
127148 sv. event_rx
@@ -130,7 +151,7 @@ impl Supervisor {
130151 match e {
131152 ipc:: TryRecvError :: IpcError ( _) => ( ) ,
132153 ipc:: TryRecvError :: Empty =>
133- eprintln ! ( "Waiting for accesses from supervisor timed out!" ) ,
154+ panic ! ( "Waiting for accesses from supervisor timed out!" ) ,
134155 }
135156 } )
136157 . ok ( )
@@ -141,6 +162,10 @@ impl Supervisor {
141162/// supervisor process could not be created successfully; else, the caller
142163/// is now the child process and can communicate via `start_ffi`/`end_ffi`,
143164/// receiving back events through `get_events`.
165+ ///
166+ /// When forking to initialise the supervisor, the child raises a `SIGSTOP`; if
167+ /// the parent successfully ptraces the child, it will allow it to resume. Else,
168+ /// the child will be killed by the parent.
144169///
145170/// # Safety
146171/// The invariants for `fork()` must be upheld by the caller, namely either:
@@ -151,11 +176,6 @@ pub unsafe fn init_sv() -> Result<(), SvInitError> {
151176 // FIXME: Much of this could be reimplemented via the mitosis crate if we upstream the
152177 // relevant missing bits.
153178
154- // Not on a properly supported architecture!
155- if cfg ! ( not( any( target_arch = "x86" , target_arch = "x86_64" , target_arch = "aarch64" ) ) ) {
156- return Err ( SvInitError ) ;
157- }
158-
159179 // On Linux, this will check whether ptrace is fully disabled by the Yama module.
160180 // If Yama isn't running or we're not on Linux, we'll still error later, but
161181 // this saves a very expensive fork call.
0 commit comments