Skip to content

Commit a3aed40

Browse files
committed
docs, etc
1 parent 4fa6a57 commit a3aed40

File tree

3 files changed

+81
-49
lines changed

3 files changed

+81
-49
lines changed

src/bin/miri.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
194194

195195
#[cfg(target_os = "linux")]
196196
if config.native_lib.is_some() {
197+
// FIXME: This should display a diagnostic / warning on error
197198
// SAFETY: No other threads have spawned yet
198199
let _ = unsafe { miri::init_sv() };
199200
}

src/shims/trace/child.rs

Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
148148
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
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(())

src/shims/trace/parent.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,15 @@ pub fn sv_loop(
234234
// have a reason to use another one.
235235
let main_pid = listener.pid;
236236

237+
// There's an initial sigstop we need to deal with
238+
wait_for_signal(main_pid, signal::SIGSTOP, false).map_err(|e| {
239+
match e {
240+
ExecError::Died(code) => code,
241+
ExecError::Shrug => None,
242+
}
243+
})?;
244+
ptrace::cont(main_pid, None).unwrap();
245+
237246
for evt in listener {
238247
match evt {
239248
// start_ffi was called by the child, so prep memory

0 commit comments

Comments
 (0)