@@ -19,7 +19,7 @@ pub struct Supervisor {
1919 /// Used for synchronisation, allowing us to receive confirmation that the
2020 /// parent process has handled the request from `message_tx`.
2121 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.
2323 event_rx : ipc:: IpcReceiver < MemEvents > ,
2424}
2525
@@ -144,67 +144,89 @@ impl Supervisor {
144144/// receiving back events through `get_events`.
145145///
146146/// # 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.
148148pub 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
150152 let ptrace_status = std:: fs:: read_to_string ( "/proc/sys/kernel/yama/ptrace_scope" ) ;
151153 if let Ok ( stat) = ptrace_status {
152154 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 ' {
155157 return Err ( SvInitError ) ;
156158 }
157159 }
158160 }
159161
160162 // Initialise the supervisor if it isn't already, placing it into SUPERVISOR
161163 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+ }
206222 }
207223 }
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+ }
208230 }
209231 }
210232 Ok ( ( ) )
0 commit comments