Skip to content

Commit 60837c7

Browse files
committed
review 4: a new hope
1 parent f2eb0e3 commit 60837c7

File tree

8 files changed

+110
-126
lines changed

8 files changed

+110
-126
lines changed

src/bin/miri.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
335335
fn exit(exit_code: i32) -> ! {
336336
// Drop the tracing guard before exiting, so tracing calls are flushed correctly.
337337
deinit_loggers();
338+
#[cfg(target_os = "linux")]
338339
miri::native_lib::register_retcode_sv(exit_code);
339340
std::process::exit(exit_code);
340341
}
@@ -752,6 +753,7 @@ fn main() {
752753
if !miri_config.native_lib.is_empty() && miri_config.native_lib_enable_tracing {
753754
// FIXME(ptrace): This should display a diagnostic / warning on error
754755
// SAFETY: No other threads are running
756+
#[cfg(target_os = "linux")]
755757
let _ = unsafe { miri::native_lib::init_sv() };
756758
}
757759
run_compiler_and_exit(

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub use rustc_const_eval::interpret::{self, AllocMap, Provenance as _};
9797
use rustc_middle::{bug, span_bug};
9898
use tracing::{info, trace};
9999

100+
#[cfg(target_os = "linux")]
100101
pub mod native_lib {
101102
pub use crate::shims::{init_sv, register_retcode_sv};
102103
}

src/shims/native_lib/mod.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,40 @@ pub mod trace;
2121

2222
use crate::*;
2323

24+
/// The final results of an FFI trace, containing every relevant event detected
25+
/// by the tracer. Sent by the supervisor after receiving a `SIGUSR1` signal.
26+
///
27+
/// The sender for this channel should live on the parent process.
28+
#[derive(serde::Serialize, serde::Deserialize, Debug)]
29+
pub struct MemEvents {
30+
/// An ordered list of memory accesses that occurred. These should be assumed
31+
/// to be overcautious; that is, if the size of an access is uncertain it is
32+
/// pessimistically rounded up, and if the type (read/write/both) is uncertain
33+
/// it is reported as whatever would be safest to assume; i.e. a read + maybe-write
34+
/// becomes a read + write, etc.
35+
pub acc_events: Vec<AccessEvent>,
36+
}
37+
38+
/// A single memory access.
39+
#[derive(serde::Serialize, serde::Deserialize, Debug)]
40+
pub enum AccessEvent {
41+
/// A read may have occurred on no more than the specified address range.
42+
Read(AccessRange),
43+
/// A write may have occurred on no more than the specified address range.
44+
Write(AccessRange),
45+
}
46+
47+
/// The actual size of a performed access, bounded by size.
48+
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
49+
pub struct AccessRange {
50+
/// The base address in memory where an access occurred.
51+
pub addr: usize,
52+
/// The smallest size this access could have been.
53+
pub min_size: usize,
54+
/// The largest size this access could have been.
55+
pub max_size: usize,
56+
}
57+
2458
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
2559
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
2660
/// Call native host function and return the output as an immediate.
@@ -30,7 +64,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
3064
dest: &MPlaceTy<'tcx>,
3165
ptr: CodePtr,
3266
libffi_args: Vec<libffi::high::Arg<'a>>,
33-
) -> trace::CallResult<'tcx> {
67+
) -> InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<MemEvents>)> {
3468
let this = self.eval_context_mut();
3569
#[cfg(target_os = "linux")]
3670
let alloc = this.machine.allocator.as_ref().unwrap();
@@ -108,7 +142,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
108142
interp_ok(ImmTy::from_scalar(scalar, dest.layout))
109143
};
110144

111-
trace::do_ffi(alloc, ffi_fn)
145+
trace::Supervisor::do_ffi(alloc, ffi_fn)
112146
}
113147

114148
/// Get the pointer to the function of the specified name in the shared object file,

src/shims/native_lib/trace/child.rs

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ use std::rc::Rc;
44
use ipc_channel::ipc;
55
use nix::sys::{ptrace, signal};
66
use nix::unistd;
7+
use rustc_const_eval::interpret::InterpResult;
78

89
use super::CALLBACK_STACK_SIZE;
9-
use super::messages::{Confirmation, MemEvents, StartFfiInfo, TraceRequest};
10+
use super::messages::{Confirmation, StartFfiInfo, TraceRequest};
1011
use super::parent::{ChildListener, sv_loop};
1112
use crate::alloc::isolated_alloc::IsolatedAlloc;
13+
use crate::shims::native_lib::MemEvents;
1214

1315
/// A handle to the single, shared supervisor process across all `MiriMachine`s.
1416
/// Since it would be very difficult to trace multiple FFI calls in parallel, we
@@ -32,12 +34,6 @@ pub struct Supervisor {
3234
event_rx: ipc::IpcReceiver<MemEvents>,
3335
}
3436

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-
4137
/// Marker representing that an error occurred during creation of the supervisor.
4238
#[derive(Debug)]
4339
pub struct SvInitError;
@@ -48,24 +44,20 @@ impl Supervisor {
4844
SUPERVISOR.lock().unwrap().is_some()
4945
}
5046

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.
5548
/// 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>)> {
6254
let mut sv_guard = SUPERVISOR.lock().unwrap();
6355
// If the supervisor is not initialised for whatever reason, fast-return.
6456
// This might be desired behaviour, as even on platforms where ptracing
6557
// is not implemented it enables us to enforce that only one FFI call
6658
// happens at a time.
6759
let Some(sv) = sv_guard.as_mut() else {
68-
return SvFfiGuard { alloc, sv_guard, cb_stack: None };
60+
return f().map(|v| (v, None))
6961
};
7062

7163
// Get pointers to all the pages the supervisor must allow accesses in
@@ -97,21 +89,8 @@ impl Supervisor {
9789
// modifications to our memory - simply waiting on the recv() doesn't
9890
// count.
9991
signal::raise(signal::SIGSTOP).unwrap();
100-
SvFfiGuard { alloc, sv_guard, cb_stack: Some(raw_stack_ptr) }
101-
}
10292

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;
93+
let res = f();
11594

11695
// We can't use IPC channels here to signal that FFI mode has ended,
11796
// since they might allocate memory which could get us stuck in a SIGTRAP
@@ -125,17 +104,13 @@ impl Supervisor {
125104
// This is safe! It just sets memory to normal expected permissions.
126105
alloc.borrow_mut().end_ffi();
127106

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()?;
132107
// SAFETY: Caller upholds that this pointer was allocated as a box with
133108
// this type.
134109
unsafe {
135-
drop(Box::from_raw(cb_stack.unwrap()));
110+
drop(Box::from_raw(raw_stack_ptr));
136111
}
137112
// On the off-chance something really weird happens, don't block forever.
138-
sv.event_rx
113+
let events = sv.event_rx
139114
.try_recv_timeout(std::time::Duration::from_secs(5))
140115
.map_err(|e| {
141116
match e {
@@ -144,14 +119,16 @@ impl Supervisor {
144119
panic!("Waiting for accesses from supervisor timed out!"),
145120
}
146121
})
147-
.ok()
122+
.ok();
123+
124+
res.map(|v| (v, events))
148125
}
149126
}
150127

151128
/// Initialises the supervisor process. If this function errors, then the
152129
/// 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`.
130+
/// is now the child process and can communicate via `do_ffi`, receiving back
131+
/// events at the end.
155132
///
156133
/// # Safety
157134
/// The invariants for `fork()` must be upheld by the caller, namely either:

src/shims/native_lib/trace/messages.rs

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,9 @@
1919
//!
2020
//! NB: sending these events out of order, skipping steps, etc. will result in
2121
//! unspecified behaviour from the supervisor process, so use the abstractions
22-
//! in `super::child` (namely `start_ffi()` and `end_ffi()`) to handle this. It is
22+
//! in `super::child` (namely `do_ffi()`) to handle this. It is
2323
//! trivially easy to cause a deadlock or crash by messing this up!
2424
25-
use std::ops::Range;
26-
2725
/// An IPC request sent by the child process to the parent.
2826
///
2927
/// The sender for this channel should live on the child process.
@@ -58,27 +56,3 @@ pub struct StartFfiInfo {
5856
/// The sender for this channel should live on the parent process.
5957
#[derive(serde::Serialize, serde::Deserialize, Debug)]
6058
pub struct Confirmation;
61-
62-
/// The final results of an FFI trace, containing every relevant event detected
63-
/// by the tracer. Sent by the supervisor after receiving a `SIGUSR1` signal.
64-
///
65-
/// The sender for this channel should live on the parent process.
66-
#[derive(serde::Serialize, serde::Deserialize, Debug)]
67-
pub struct MemEvents {
68-
/// An ordered list of memory accesses that occurred. These should be assumed
69-
/// to be overcautious; that is, if the size of an access is uncertain it is
70-
/// pessimistically rounded up, and if the type (read/write/both) is uncertain
71-
/// it is reported as whatever would be safest to assume; i.e. a read + maybe-write
72-
/// becomes a read + write, etc.
73-
pub acc_events: Vec<AccessEvent>,
74-
}
75-
76-
/// A single memory access, conservatively overestimated
77-
/// in case of ambiguity.
78-
#[derive(serde::Serialize, serde::Deserialize, Debug)]
79-
pub enum AccessEvent {
80-
/// A read may have occurred on no more than the specified address range.
81-
Read(Range<usize>),
82-
/// A write may have occurred on no more than the specified address range.
83-
Write(Range<usize>),
84-
}

src/shims/native_lib/trace/mod.rs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,9 @@ mod child;
22
pub mod messages;
33
mod parent;
44

5-
use std::cell::RefCell;
6-
use std::rc::Rc;
7-
8-
use rustc_const_eval::interpret::InterpResult;
9-
105
pub use self::child::{Supervisor, init_sv, register_retcode_sv};
11-
use crate::alloc::isolated_alloc::IsolatedAlloc;
126

137
/// The size of the temporary stack we use for callbacks that the server executes in the client.
148
/// This should be big enough that `mempr_on` and `mempr_off` can safely be jumped into with the
159
/// stack pointer pointing to a "stack" of this size without overflowing it.
1610
const CALLBACK_STACK_SIZE: usize = 1024;
17-
18-
/// Wrapper type for what we get back from an FFI call; the former is its actual
19-
/// return value, and the latter is the list of memory accesses that occurred during
20-
/// this call.
21-
pub type CallResult<'tcx> = InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<messages::MemEvents>)>;
22-
23-
/// Performs an arbitrary FFI call, enabling tracing from the supervisor.
24-
pub fn do_ffi<'tcx>(
25-
alloc: &Rc<RefCell<IsolatedAlloc>>,
26-
f: impl FnOnce() -> InterpResult<'tcx, crate::ImmTy<'tcx>>,
27-
) -> CallResult<'tcx> {
28-
// SAFETY: We don't touch the machine memory past this point.
29-
let guard = unsafe { Supervisor::start_ffi(alloc) };
30-
31-
f().map(|v| {
32-
let memevents = unsafe { Supervisor::end_ffi(guard) };
33-
(v, memevents)
34-
})
35-
}

0 commit comments

Comments
 (0)