@@ -19,7 +19,7 @@ pub struct Supervisor {
19
19
/// Used for synchronisation, allowing us to receive confirmation that the
20
20
/// parent process has handled the request from `message_tx`.
21
21
confirm_rx : ipc:: IpcReceiver < ( ) > ,
22
- /// Receiver for memory acceses that ocurred at the end of the FFI call.
22
+ /// Receiver for memory acceses that ocurred during the FFI call.
23
23
event_rx : ipc:: IpcReceiver < MemEvents > ,
24
24
}
25
25
@@ -144,67 +144,89 @@ impl Supervisor {
144
144
/// receiving back events through `get_events`.
145
145
///
146
146
/// # Safety
147
- /// Only a single thread must exist when calling this.
147
+ /// Only a single OS thread must exist in the process when calling this.
148
148
pub unsafe fn init_sv ( ) -> Result < ( ) , SvInitError > {
149
- // TODO: Check for `CAP_SYS_PTRACE` if this fails
149
+ // On Linux, this will check whether ptrace is fully disabled by the Yama module.
150
+ // If Yama isn't running or we're not on Linux, we'll still error later, but
151
+ // this saves a very expensive fork call
150
152
let ptrace_status = std:: fs:: read_to_string ( "/proc/sys/kernel/yama/ptrace_scope" ) ;
151
153
if let Ok ( stat) = ptrace_status {
152
154
if let Some ( stat) = stat. chars ( ) . next ( ) {
153
- // Fast-error if ptrace is disabled on the system
154
- if stat != '0' && stat ! = '1 ' {
155
+ // Fast-error if ptrace is fully disabled on the system
156
+ if stat = = '3 ' {
155
157
return Err ( SvInitError ) ;
156
158
}
157
159
}
158
160
}
159
161
160
162
// Initialise the supervisor if it isn't already, placing it into SUPERVISOR
161
163
let mut lock = SUPERVISOR . lock ( ) . unwrap ( ) ;
162
- if lock. is_none ( ) {
163
- // TODO: Do we want to compress the confirm and event channels into one?
164
- let ( message_tx, message_rx) = ipc:: channel ( ) . unwrap ( ) ;
165
- let ( confirm_tx, confirm_rx) = ipc:: channel ( ) . unwrap ( ) ;
166
- let ( event_tx, event_rx) = ipc:: channel ( ) . unwrap ( ) ;
167
- // SAFETY: Calling sysconf(_SC_PAGESIZE) is always safe and cannot error
168
- let page_size = unsafe { libc:: sysconf ( libc:: _SC_PAGESIZE) } . try_into ( ) . unwrap ( ) ;
169
-
170
- super :: parent:: PAGE_SIZE . store ( page_size, std:: sync:: atomic:: Ordering :: Relaxed ) ;
171
- unsafe {
172
- // SAFETY: Caller upholds that only one thread exists.
173
- match unistd:: fork ( ) . unwrap ( ) {
174
- unistd:: ForkResult :: Parent { child } => {
175
- // If somehow another thread does exist, prevent it from accessing the lock
176
- // and thus breaking our safety invariants
177
- std:: mem:: forget ( lock) ;
178
- // The child process is free to unwind, so we won't to avoid doubly freeing
179
- // system resources
180
- let p = std:: panic:: catch_unwind ( || {
181
- let listener = ChildListener {
182
- message_rx,
183
- pid : child,
184
- attached : false ,
185
- override_retcode : None ,
186
- } ;
187
- // Trace as many things as possible, to be able to handle them as needed
188
- let options = ptrace:: Options :: PTRACE_O_TRACESYSGOOD
189
- | ptrace:: Options :: PTRACE_O_TRACECLONE
190
- | ptrace:: Options :: PTRACE_O_TRACEFORK ;
191
- // Attach to the child process without stopping it
192
- ptrace:: seize ( child, options) . unwrap ( ) ;
193
-
194
- let code = sv_loop ( listener, event_tx, confirm_tx, page_size) . unwrap_err ( ) ;
195
- // If a return code of 0 is not explicitly given, assume something went
196
- // wrong and return 1
197
- std:: process:: exit ( code. unwrap_or ( 1 ) ) ;
198
- } )
199
- . unwrap_err ( ) ; // The Ok variant of this is !
200
- eprintln ! ( "Supervisor process panicked!\n {p:?}" ) ;
201
- std:: process:: exit ( 1 ) ;
202
- }
203
- unistd:: ForkResult :: Child => {
204
- // If we're the child process, save the supervisor info
205
- * lock = Some ( Supervisor { message_tx, confirm_rx, event_rx } ) ;
164
+ if lock. is_some ( ) {
165
+ return Ok ( ( ) ) ;
166
+ }
167
+
168
+ // Prepare the IPC channels we need
169
+ let ( message_tx, message_rx) = ipc:: channel ( ) . unwrap ( ) ;
170
+ let ( confirm_tx, confirm_rx) = ipc:: channel ( ) . unwrap ( ) ;
171
+ let ( event_tx, event_rx) = ipc:: channel ( ) . unwrap ( ) ;
172
+ // SAFETY: Calling sysconf(_SC_PAGESIZE) is always safe and cannot error
173
+ let page_size = unsafe { libc:: sysconf ( libc:: _SC_PAGESIZE) } . try_into ( ) . unwrap ( ) ;
174
+
175
+ super :: parent:: PAGE_SIZE . store ( page_size, std:: sync:: atomic:: Ordering :: Relaxed ) ;
176
+ unsafe {
177
+ // TODO: Maybe use clone3() instead for better signalling of when the child exits?
178
+ // SAFETY: Caller upholds that only one thread exists.
179
+ match unistd:: fork ( ) . unwrap ( ) {
180
+ unistd:: ForkResult :: Parent { child } => {
181
+ // If somehow another thread does exist, prevent it from accessing the lock
182
+ // and thus breaking our safety invariants
183
+ std:: mem:: forget ( lock) ;
184
+ // The child process is free to unwind, so we won't to avoid doubly freeing
185
+ // system resources
186
+ let init = std:: panic:: catch_unwind ( || {
187
+ let listener = ChildListener {
188
+ message_rx,
189
+ pid : child,
190
+ attached : false ,
191
+ override_retcode : None ,
192
+ } ;
193
+ // Trace as many things as possible, to be able to handle them as needed
194
+ let options = ptrace:: Options :: PTRACE_O_TRACESYSGOOD
195
+ | ptrace:: Options :: PTRACE_O_TRACECLONE
196
+ | ptrace:: Options :: PTRACE_O_TRACEFORK ;
197
+ // Attach to the child process without stopping it
198
+ match ptrace:: seize ( child, options) {
199
+ // Ptrace works :D
200
+ Ok ( _) => {
201
+ let code =
202
+ sv_loop ( listener, event_tx, confirm_tx, page_size) . unwrap_err ( ) ;
203
+ // If a return code of 0 is not explicitly given, assume something went
204
+ // wrong and return 1
205
+ std:: process:: exit ( code. unwrap_or ( 1 ) )
206
+ }
207
+ // Ptrace does not work and we failed to catch that
208
+ Err ( _) => {
209
+ // If we can't ptrace, Miri continues being the parent
210
+ signal:: kill ( child, signal:: SIGKILL ) . unwrap ( ) ;
211
+ SvInitError
212
+ }
213
+ }
214
+ } ) ;
215
+ match init {
216
+ // The "Ok" case means that we couldn't ptrace
217
+ Ok ( e) => return Err ( e) ,
218
+ Err ( p) => {
219
+ eprintln ! ( "Supervisor process panicked!\n {p:?}" ) ;
220
+ std:: process:: exit ( 1 ) ;
221
+ }
206
222
}
207
223
}
224
+ unistd:: ForkResult :: Child => {
225
+ // First make sure the parent succeeded with ptracing us!
226
+ signal:: raise ( signal:: SIGSTOP ) . unwrap ( ) ;
227
+ // If we're the child process, save the supervisor info
228
+ * lock = Some ( Supervisor { message_tx, confirm_rx, event_rx } ) ;
229
+ }
208
230
}
209
231
}
210
232
Ok ( ( ) )
0 commit comments