@@ -52,30 +52,40 @@ impl Supervisor {
52
52
// If the supervisor is not initialised for whatever reason, fast-fail.
53
53
// This might be desired behaviour, as even on platforms where ptracing
54
54
// is not implemented it enables us to enforce that only one FFI call
55
- // happens at a time
55
+ // happens at a time.
56
56
let Some ( sv) = sv_guard. take ( ) else {
57
57
return ( sv_guard, None ) ;
58
58
} ;
59
59
60
60
// Get pointers to all the pages the supervisor must allow accesses in
61
- // and prepare the fake stack
61
+ // and prepare the fake stack.
62
62
let page_ptrs = alloc. borrow ( ) . pages ( ) ;
63
63
let raw_stack_ptr: * mut [ u8 ; FAKE_STACK_SIZE ] =
64
64
Box :: leak ( Box :: new ( [ 0u8 ; FAKE_STACK_SIZE ] ) ) . as_mut_ptr ( ) . cast ( ) ;
65
65
let stack_ptr = raw_stack_ptr. expose_provenance ( ) ;
66
66
let start_info = StartFfiInfo { page_ptrs, stack_ptr } ;
67
67
68
+ // SAFETY: We do not access machine memory past this point until the
69
+ // supervisor is ready to allow it.
70
+ unsafe {
71
+ if alloc. borrow_mut ( ) . prepare_ffi ( ) . is_err ( ) {
72
+ // Don't mess up unwinding by maybe leaving the memory partly protected
73
+ alloc. borrow_mut ( ) . unprep_ffi ( ) ;
74
+ panic ! ( "Cannot protect memory for FFI call!" ) ;
75
+ }
76
+ }
77
+
68
78
// Send over the info.
69
79
// NB: if we do not wait to receive a blank confirmation response, it is
70
80
// possible that the supervisor is alerted of the SIGSTOP *before* it has
71
81
// actually received the start_info, thus deadlocking! This way, we can
72
- // enforce an ordering for these events
82
+ // enforce an ordering for these events.
73
83
sv. message_tx . send ( TraceRequest :: StartFfi ( start_info) ) . unwrap ( ) ;
74
84
sv. confirm_rx . recv ( ) . unwrap ( ) ;
75
85
* sv_guard = Some ( sv) ;
76
86
// We need to be stopped for the supervisor to be able to make certain
77
87
// modifications to our memory - simply waiting on the recv() doesn't
78
- // count
88
+ // count.
79
89
signal:: raise ( signal:: SIGSTOP ) . unwrap ( ) ;
80
90
( sv_guard, Some ( raw_stack_ptr) )
81
91
}
@@ -90,7 +100,7 @@ impl Supervisor {
90
100
/// one passed to it also.
91
101
pub unsafe fn end_ffi (
92
102
mut sv_guard : std:: sync:: MutexGuard < ' static , Option < Supervisor > > ,
93
- _alloc : Rc < RefCell < IsolatedAlloc > > ,
103
+ alloc : Rc < RefCell < IsolatedAlloc > > ,
94
104
raw_stack_ptr : Option < * mut [ u8 ; FAKE_STACK_SIZE ] > ,
95
105
) -> Option < MemEvents > {
96
106
// We can't use IPC channels here to signal that FFI mode has ended,
@@ -99,19 +109,22 @@ impl Supervisor {
99
109
// simpler and more robust to simply use the signals which are left for
100
110
// arbitrary usage. Since this will block until we are continued by the
101
111
// supervisor, we can assume past this point that everything is back to
102
- // normal
112
+ // normal.
103
113
signal:: raise ( signal:: SIGUSR1 ) . unwrap ( ) ;
104
114
115
+ // This is safe! It just sets memory to normal expected permissions.
116
+ alloc. borrow_mut ( ) . unprep_ffi ( ) ;
117
+
105
118
// If this is `None`, then `raw_stack_ptr` is None and does not need to
106
119
// be deallocated (and there's no need to worry about the guard, since
107
- // it contains nothing)
120
+ // it contains nothing).
108
121
let sv = sv_guard. take ( ) ?;
109
122
// SAFETY: Caller upholds that this pointer was allocated as a box with
110
- // this type
123
+ // this type.
111
124
unsafe {
112
125
drop ( Box :: from_raw ( raw_stack_ptr. unwrap ( ) ) ) ;
113
126
}
114
- // On the off-chance something really weird happens, don't block forever
127
+ // On the off-chance something really weird happens, don't block forever.
115
128
let ret = sv
116
129
. event_rx
117
130
. try_recv_timeout ( std:: time:: Duration :: from_secs ( 5 ) )
@@ -138,71 +151,71 @@ impl Supervisor {
138
151
/// The invariants for `fork()` must be upheld by the caller.
139
152
pub unsafe fn init_sv ( ) -> Result < ( ) , SvInitError > {
140
153
// FIXME: Much of this could be reimplemented via the mitosis crate if we upstream the
141
- // relevant missing bits
154
+ // relevant missing bits.
142
155
143
156
// On Linux, this will check whether ptrace is fully disabled by the Yama module.
144
157
// If Yama isn't running or we're not on Linux, we'll still error later, but
145
- // this saves a very expensive fork call
158
+ // this saves a very expensive fork call.
146
159
let ptrace_status = std:: fs:: read_to_string ( "/proc/sys/kernel/yama/ptrace_scope" ) ;
147
160
if let Ok ( stat) = ptrace_status {
148
161
if let Some ( stat) = stat. chars ( ) . next ( ) {
149
- // Fast-error if ptrace is fully disabled on the system
162
+ // Fast-error if ptrace is fully disabled on the system.
150
163
if stat == '3' {
151
164
return Err ( SvInitError ) ;
152
165
}
153
166
}
154
167
}
155
168
156
- // Initialise the supervisor if it isn't already, placing it into SUPERVISOR
169
+ // Initialise the supervisor if it isn't already, placing it into SUPERVISOR.
157
170
let mut lock = SUPERVISOR . lock ( ) . unwrap ( ) ;
158
171
if lock. is_some ( ) {
159
172
return Ok ( ( ) ) ;
160
173
}
161
174
162
- // Prepare the IPC channels we need
175
+ // Prepare the IPC channels we need.
163
176
let ( message_tx, message_rx) = ipc:: channel ( ) . unwrap ( ) ;
164
177
let ( confirm_tx, confirm_rx) = ipc:: channel ( ) . unwrap ( ) ;
165
178
let ( event_tx, event_rx) = ipc:: channel ( ) . unwrap ( ) ;
166
- // SAFETY: Calling sysconf(_SC_PAGESIZE) is always safe and cannot error
179
+ // SAFETY: Calling sysconf(_SC_PAGESIZE) is always safe and cannot error.
167
180
let page_size = unsafe { libc:: sysconf ( libc:: _SC_PAGESIZE) } . try_into ( ) . unwrap ( ) ;
181
+ super :: parent:: PAGE_SIZE . store ( page_size, std:: sync:: atomic:: Ordering :: Relaxed ) ;
168
182
169
183
unsafe {
170
184
// TODO: Maybe use clone3() instead for better signalling of when the child exits?
171
185
// SAFETY: Caller upholds that only one thread exists.
172
186
match unistd:: fork ( ) . unwrap ( ) {
173
187
unistd:: ForkResult :: Parent { child } => {
174
188
// If somehow another thread does exist, prevent it from accessing the lock
175
- // and thus breaking our safety invariants
189
+ // and thus breaking our safety invariants.
176
190
std:: mem:: forget ( lock) ;
177
191
// The child process is free to unwind, so we won't to avoid doubly freeing
178
- // system resources
192
+ // system resources.
179
193
let init = std:: panic:: catch_unwind ( || {
180
194
let listener =
181
195
ChildListener { message_rx, attached : false , override_retcode : None } ;
182
- // Trace as many things as possible, to be able to handle them as needed
196
+ // Trace as many things as possible, to be able to handle them as needed.
183
197
let options = ptrace:: Options :: PTRACE_O_TRACESYSGOOD
184
198
| ptrace:: Options :: PTRACE_O_TRACECLONE
185
199
| ptrace:: Options :: PTRACE_O_TRACEFORK ;
186
- // Attach to the child process without stopping it
200
+ // Attach to the child process without stopping it.
187
201
match ptrace:: seize ( child, options) {
188
202
// Ptrace works :D
189
203
Ok ( _) => {
190
- let code = sv_loop ( listener, child, event_tx, confirm_tx, page_size)
191
- . unwrap_err ( ) ;
204
+ let code = sv_loop ( listener, child, event_tx, confirm_tx) . unwrap_err ( ) ;
192
205
// If a return code of 0 is not explicitly given, assume something went
193
- // wrong and return 1
206
+ // wrong and return 1.
194
207
std:: process:: exit ( code. unwrap_or ( 1 ) )
195
208
}
196
- // Ptrace does not work and we failed to catch that
209
+ // Ptrace does not work and we failed to catch that.
197
210
Err ( _) => {
198
- // If we can't ptrace, Miri continues being the parent
211
+ // If we can't ptrace, Miri continues being the parent.
199
212
signal:: kill ( child, signal:: SIGKILL ) . unwrap ( ) ;
200
213
SvInitError
201
214
}
202
215
}
203
216
} ) ;
204
217
match init {
205
- // The "Ok" case means that we couldn't ptrace
218
+ // The "Ok" case means that we couldn't ptrace.
206
219
Ok ( e) => return Err ( e) ,
207
220
Err ( p) => {
208
221
eprintln ! ( "Supervisor process panicked!\n {p:?}" ) ;
@@ -212,12 +225,12 @@ pub unsafe fn init_sv() -> Result<(), SvInitError> {
212
225
}
213
226
unistd:: ForkResult :: Child => {
214
227
// Make sure we never get orphaned and stuck in SIGSTOP or similar
215
- // SAFETY: prctl PR_SET_PDEATHSIG is always safe to call
228
+ // SAFETY: prctl PR_SET_PDEATHSIG is always safe to call.
216
229
let ret = libc:: prctl ( libc:: PR_SET_PDEATHSIG , libc:: SIGTERM ) ;
217
230
assert_eq ! ( ret, 0 ) ;
218
231
// First make sure the parent succeeded with ptracing us!
219
232
signal:: raise ( signal:: SIGSTOP ) . unwrap ( ) ;
220
- // If we're the child process, save the supervisor info
233
+ // If we're the child process, save the supervisor info.
221
234
* lock = Some ( Supervisor { message_tx, confirm_rx, event_rx } ) ;
222
235
}
223
236
}
0 commit comments