@@ -4,11 +4,13 @@ use std::rc::Rc;
44use ipc_channel:: ipc;
55use nix:: sys:: { ptrace, signal} ;
66use nix:: unistd;
7+ use rustc_const_eval:: interpret:: InterpResult ;
78
89use super :: CALLBACK_STACK_SIZE ;
9- use super :: messages:: { Confirmation , MemEvents , StartFfiInfo , TraceRequest } ;
10+ use super :: messages:: { Confirmation , StartFfiInfo , TraceRequest } ;
1011use super :: parent:: { ChildListener , sv_loop} ;
1112use crate :: alloc:: isolated_alloc:: IsolatedAlloc ;
13+ use crate :: shims:: native_lib:: MemEvents ;
1214
1315/// A handle to the single, shared supervisor process across all `MiriMachine`s.
1416/// Since it would be very difficult to trace multiple FFI calls in parallel, we
@@ -32,12 +34,6 @@ pub struct Supervisor {
3234 event_rx : ipc:: IpcReceiver < MemEvents > ,
3335}
3436
35- pub struct SvFfiGuard < ' a > {
36- alloc : & ' a Rc < RefCell < IsolatedAlloc > > ,
37- sv_guard : std:: sync:: MutexGuard < ' static , Option < Supervisor > > ,
38- cb_stack : Option < * mut [ u8 ; CALLBACK_STACK_SIZE ] > ,
39- }
40-
4137/// Marker representing that an error occurred during creation of the supervisor.
4238#[ derive( Debug ) ]
4339pub struct SvInitError ;
@@ -48,24 +44,20 @@ impl Supervisor {
4844 SUPERVISOR . lock ( ) . unwrap ( ) . is_some ( )
4945 }
5046
51- /// Begins preparations for doing an FFI call. This should be called at
52- /// the last possible moment before entering said call. `alloc` points to
53- /// the allocator which handed out the memory used for this machine.
54- ///
47+ /// Performs an arbitrary FFI call, enabling tracing from the supervisor.
5548 /// As this locks the supervisor via a mutex, no other threads may enter FFI
56- /// until this one returns and its guard is dropped via `end_ffi`. The
57- /// pointer returned should be passed to `end_ffi` to avoid a memory leak.
58- ///
59- /// SAFETY: The resulting guard must be dropped *via `end_ffi`* immediately
60- /// after the desired call has concluded.
61- pub unsafe fn start_ffi ( alloc : & Rc < RefCell < IsolatedAlloc > > ) -> SvFfiGuard < ' _ > {
49+ /// until this function returns.
50+ pub fn do_ffi < ' tcx > (
51+ alloc : & Rc < RefCell < IsolatedAlloc > > ,
52+ f : impl FnOnce ( ) -> InterpResult < ' tcx , crate :: ImmTy < ' tcx > > ,
53+ ) -> InterpResult < ' tcx , ( crate :: ImmTy < ' tcx > , Option < MemEvents > ) > {
6254 let mut sv_guard = SUPERVISOR . lock ( ) . unwrap ( ) ;
6355 // If the supervisor is not initialised for whatever reason, fast-return.
6456 // This might be desired behaviour, as even on platforms where ptracing
6557 // is not implemented it enables us to enforce that only one FFI call
6658 // happens at a time.
6759 let Some ( sv) = sv_guard. as_mut ( ) else {
68- return SvFfiGuard { alloc , sv_guard , cb_stack : None } ;
60+ return f ( ) . map ( |v| ( v , None ) )
6961 } ;
7062
7163 // Get pointers to all the pages the supervisor must allow accesses in
@@ -97,21 +89,8 @@ impl Supervisor {
9789 // modifications to our memory - simply waiting on the recv() doesn't
9890 // count.
9991 signal:: raise ( signal:: SIGSTOP ) . unwrap ( ) ;
100- SvFfiGuard { alloc, sv_guard, cb_stack : Some ( raw_stack_ptr) }
101- }
10292
103- /// Undoes FFI-related preparations, allowing Miri to continue as normal, then
104- /// gets the memory accesses and changes performed during the FFI call. Note
105- /// that this may include some spurious accesses done by `libffi` itself in
106- /// the process of executing the function call.
107- ///
108- /// SAFETY: The `sv_guard` and `raw_stack_ptr` passed must be the same ones
109- /// received by a prior call to `start_ffi`, and the allocator must be the
110- /// one passed to it also.
111- pub unsafe fn end_ffi ( guard : SvFfiGuard < ' _ > ) -> Option < MemEvents > {
112- let alloc = guard. alloc ;
113- let mut sv_guard = guard. sv_guard ;
114- let cb_stack = guard. cb_stack ;
93+ let res = f ( ) ;
11594
11695 // We can't use IPC channels here to signal that FFI mode has ended,
11796 // since they might allocate memory which could get us stuck in a SIGTRAP
@@ -125,17 +104,13 @@ impl Supervisor {
125104 // This is safe! It just sets memory to normal expected permissions.
126105 alloc. borrow_mut ( ) . end_ffi ( ) ;
127106
128- // If this is `None`, then `raw_stack_ptr` is None and does not need to
129- // be deallocated (and there's no need to worry about the guard, since
130- // it contains nothing).
131- let sv = sv_guard. as_mut ( ) ?;
132107 // SAFETY: Caller upholds that this pointer was allocated as a box with
133108 // this type.
134109 unsafe {
135- drop ( Box :: from_raw ( cb_stack . unwrap ( ) ) ) ;
110+ drop ( Box :: from_raw ( raw_stack_ptr ) ) ;
136111 }
137112 // On the off-chance something really weird happens, don't block forever.
138- sv. event_rx
113+ let events = sv. event_rx
139114 . try_recv_timeout ( std:: time:: Duration :: from_secs ( 5 ) )
140115 . map_err ( |e| {
141116 match e {
@@ -144,14 +119,16 @@ impl Supervisor {
144119 panic ! ( "Waiting for accesses from supervisor timed out!" ) ,
145120 }
146121 } )
147- . ok ( )
122+ . ok ( ) ;
123+
124+ res. map ( |v| ( v, events) )
148125 }
149126}
150127
151128/// Initialises the supervisor process. If this function errors, then the
152129/// supervisor process could not be created successfully; else, the caller
153- /// is now the child process and can communicate via `start_ffi`/`end_ffi`,
154- /// receiving back events through `get_events` .
130+ /// is now the child process and can communicate via `do_ffi`, receiving back
131+ /// events at the end .
155132///
156133/// # Safety
157134/// The invariants for `fork()` must be upheld by the caller, namely either:
0 commit comments