@@ -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,25 +44,19 @@ 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.
67- let Some ( sv) = sv_guard. as_mut ( ) else {
68- return SvFfiGuard { alloc, sv_guard, cb_stack : None } ;
69- } ;
59+ let Some ( sv) = sv_guard. as_mut ( ) else { return f ( ) . map ( |v| ( v, None ) ) } ;
7060
7161 // Get pointers to all the pages the supervisor must allow accesses in
7262 // and prepare the callback stack.
@@ -97,21 +87,8 @@ impl Supervisor {
9787 // modifications to our memory - simply waiting on the recv() doesn't
9888 // count.
9989 signal:: raise ( signal:: SIGSTOP ) . unwrap ( ) ;
100- SvFfiGuard { alloc, sv_guard, cb_stack : Some ( raw_stack_ptr) }
101- }
10290
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 ;
91+ let res = f ( ) ;
11592
11693 // We can't use IPC channels here to signal that FFI mode has ended,
11794 // since they might allocate memory which could get us stuck in a SIGTRAP
@@ -125,17 +102,14 @@ impl Supervisor {
125102 // This is safe! It just sets memory to normal expected permissions.
126103 alloc. borrow_mut ( ) . end_ffi ( ) ;
127104
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 ( ) ?;
132105 // SAFETY: Caller upholds that this pointer was allocated as a box with
133106 // this type.
134107 unsafe {
135- drop ( Box :: from_raw ( cb_stack . unwrap ( ) ) ) ;
108+ drop ( Box :: from_raw ( raw_stack_ptr ) ) ;
136109 }
137110 // On the off-chance something really weird happens, don't block forever.
138- sv. event_rx
111+ let events = sv
112+ . event_rx
139113 . try_recv_timeout ( std:: time:: Duration :: from_secs ( 5 ) )
140114 . map_err ( |e| {
141115 match e {
@@ -144,14 +118,16 @@ impl Supervisor {
144118 panic ! ( "Waiting for accesses from supervisor timed out!" ) ,
145119 }
146120 } )
147- . ok ( )
121+ . ok ( ) ;
122+
123+ res. map ( |v| ( v, events) )
148124 }
149125}
150126
151127/// Initialises the supervisor process. If this function errors, then the
152128/// 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` .
129+ /// is now the child process and can communicate via `do_ffi`, receiving back
130+ /// events at the end .
155131///
156132/// # Safety
157133/// The invariants for `fork()` must be upheld by the caller, namely either:
0 commit comments