@@ -4,11 +4,13 @@ use std::rc::Rc;
4
4
use ipc_channel:: ipc;
5
5
use nix:: sys:: { ptrace, signal} ;
6
6
use nix:: unistd;
7
+ use rustc_const_eval:: interpret:: InterpResult ;
7
8
8
9
use super :: CALLBACK_STACK_SIZE ;
9
- use super :: messages:: { Confirmation , MemEvents , StartFfiInfo , TraceRequest } ;
10
+ use super :: messages:: { Confirmation , StartFfiInfo , TraceRequest } ;
10
11
use super :: parent:: { ChildListener , sv_loop} ;
11
12
use crate :: alloc:: isolated_alloc:: IsolatedAlloc ;
13
+ use crate :: shims:: native_lib:: MemEvents ;
12
14
13
15
/// A handle to the single, shared supervisor process across all `MiriMachine`s.
14
16
/// Since it would be very difficult to trace multiple FFI calls in parallel, we
@@ -32,12 +34,6 @@ pub struct Supervisor {
32
34
event_rx : ipc:: IpcReceiver < MemEvents > ,
33
35
}
34
36
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
-
41
37
/// Marker representing that an error occurred during creation of the supervisor.
42
38
#[ derive( Debug ) ]
43
39
pub struct SvInitError ;
@@ -48,24 +44,20 @@ impl Supervisor {
48
44
SUPERVISOR . lock ( ) . unwrap ( ) . is_some ( )
49
45
}
50
46
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.
55
48
/// 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 > ) > {
62
54
let mut sv_guard = SUPERVISOR . lock ( ) . unwrap ( ) ;
63
55
// If the supervisor is not initialised for whatever reason, fast-return.
64
56
// This might be desired behaviour, as even on platforms where ptracing
65
57
// is not implemented it enables us to enforce that only one FFI call
66
58
// happens at a time.
67
59
let Some ( sv) = sv_guard. as_mut ( ) else {
68
- return SvFfiGuard { alloc , sv_guard , cb_stack : None } ;
60
+ return f ( ) . map ( |v| ( v , None ) )
69
61
} ;
70
62
71
63
// Get pointers to all the pages the supervisor must allow accesses in
@@ -97,21 +89,8 @@ impl Supervisor {
97
89
// modifications to our memory - simply waiting on the recv() doesn't
98
90
// count.
99
91
signal:: raise ( signal:: SIGSTOP ) . unwrap ( ) ;
100
- SvFfiGuard { alloc, sv_guard, cb_stack : Some ( raw_stack_ptr) }
101
- }
102
92
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 ( ) ;
115
94
116
95
// We can't use IPC channels here to signal that FFI mode has ended,
117
96
// since they might allocate memory which could get us stuck in a SIGTRAP
@@ -125,17 +104,13 @@ impl Supervisor {
125
104
// This is safe! It just sets memory to normal expected permissions.
126
105
alloc. borrow_mut ( ) . end_ffi ( ) ;
127
106
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 ( ) ?;
132
107
// SAFETY: Caller upholds that this pointer was allocated as a box with
133
108
// this type.
134
109
unsafe {
135
- drop ( Box :: from_raw ( cb_stack . unwrap ( ) ) ) ;
110
+ drop ( Box :: from_raw ( raw_stack_ptr ) ) ;
136
111
}
137
112
// On the off-chance something really weird happens, don't block forever.
138
- sv. event_rx
113
+ let events = sv. event_rx
139
114
. try_recv_timeout ( std:: time:: Duration :: from_secs ( 5 ) )
140
115
. map_err ( |e| {
141
116
match e {
@@ -144,14 +119,16 @@ impl Supervisor {
144
119
panic ! ( "Waiting for accesses from supervisor timed out!" ) ,
145
120
}
146
121
} )
147
- . ok ( )
122
+ . ok ( ) ;
123
+
124
+ res. map ( |v| ( v, events) )
148
125
}
149
126
}
150
127
151
128
/// Initialises the supervisor process. If this function errors, then the
152
129
/// 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 .
155
132
///
156
133
/// # Safety
157
134
/// The invariants for `fork()` must be upheld by the caller, namely either:
0 commit comments