@@ -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,25 +44,19 @@ 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
- 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 ) ) } ;
70
60
71
61
// Get pointers to all the pages the supervisor must allow accesses in
72
62
// and prepare the callback stack.
@@ -97,21 +87,8 @@ impl Supervisor {
97
87
// modifications to our memory - simply waiting on the recv() doesn't
98
88
// count.
99
89
signal:: raise ( signal:: SIGSTOP ) . unwrap ( ) ;
100
- SvFfiGuard { alloc, sv_guard, cb_stack : Some ( raw_stack_ptr) }
101
- }
102
90
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 ( ) ;
115
92
116
93
// We can't use IPC channels here to signal that FFI mode has ended,
117
94
// since they might allocate memory which could get us stuck in a SIGTRAP
@@ -125,17 +102,14 @@ impl Supervisor {
125
102
// This is safe! It just sets memory to normal expected permissions.
126
103
alloc. borrow_mut ( ) . end_ffi ( ) ;
127
104
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
105
// SAFETY: Caller upholds that this pointer was allocated as a box with
133
106
// this type.
134
107
unsafe {
135
- drop ( Box :: from_raw ( cb_stack . unwrap ( ) ) ) ;
108
+ drop ( Box :: from_raw ( raw_stack_ptr ) ) ;
136
109
}
137
110
// On the off-chance something really weird happens, don't block forever.
138
- sv. event_rx
111
+ let events = sv
112
+ . event_rx
139
113
. try_recv_timeout ( std:: time:: Duration :: from_secs ( 5 ) )
140
114
. map_err ( |e| {
141
115
match e {
@@ -144,14 +118,16 @@ impl Supervisor {
144
118
panic ! ( "Waiting for accesses from supervisor timed out!" ) ,
145
119
}
146
120
} )
147
- . ok ( )
121
+ . ok ( ) ;
122
+
123
+ res. map ( |v| ( v, events) )
148
124
}
149
125
}
150
126
151
127
/// Initialises the supervisor process. If this function errors, then the
152
128
/// 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 .
155
131
///
156
132
/// # Safety
157
133
/// The invariants for `fork()` must be upheld by the caller, namely either:
0 commit comments