Skip to content

Commit b65f449

Browse files
committed
ffi ptracing
fix review comments ?? docs move init to arg parsing docs again
1 parent a87c442 commit b65f449

File tree

7 files changed

+613
-56
lines changed

7 files changed

+613
-56
lines changed

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ libloading = "0.8"
4444
nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"] }
4545
ipc-channel = "0.19.0"
4646
serde = { version = "1.0.219", features = ["derive"] }
47+
capstone = "0.13"
4748

4849
[dev-dependencies]
4950
ui_test = "0.29.1"

src/alloc/isolated_alloc.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::alloc::{self, Layout};
22

3+
use nix::sys::mman;
34
use rustc_index::bit_set::DenseBitSet;
45

56
/// How many bytes of memory each bit in the bitset represents.
@@ -271,13 +272,54 @@ impl IsolatedAlloc {
271272
pub fn pages(&self) -> Vec<usize> {
272273
let mut pages: Vec<_> =
273274
self.page_ptrs.clone().into_iter().map(|p| p.expose_provenance()).collect();
274-
for (ptr, size) in &self.huge_ptrs {
275+
self.huge_ptrs.iter().for_each(|(ptr, size)| {
275276
for i in 0..size / self.page_size {
276277
pages.push(ptr.expose_provenance().strict_add(i * self.page_size));
277278
}
278-
}
279+
});
279280
pages
280281
}
282+
283+
/// Protects all owned memory as `PROT_NONE`, preventing accesses.
284+
///
285+
/// SAFETY: Accessing memory after this point will result in a segfault
286+
/// unless it is first unprotected.
287+
pub unsafe fn prepare_ffi(&mut self) -> Result<(), nix::errno::Errno> {
288+
let prot = mman::ProtFlags::PROT_NONE;
289+
unsafe { self.mprotect(prot) }
290+
}
291+
292+
/// Deprotects all owned memory by setting it to RW. Erroring here is very
293+
/// likely unrecoverable, so it may panic if applying those permissions
294+
/// fails.
295+
pub fn unprep_ffi(&mut self) {
296+
let prot = mman::ProtFlags::PROT_READ | mman::ProtFlags::PROT_WRITE;
297+
unsafe {
298+
self.mprotect(prot).unwrap();
299+
}
300+
}
301+
302+
/// Applies `prot` to every page managed by the allocator.
303+
///
304+
/// SAFETY: Accessing memory in violation of the protection flags will
305+
/// trigger a segfault.
306+
unsafe fn mprotect(&mut self, prot: mman::ProtFlags) -> Result<(), nix::errno::Errno> {
307+
for &pg in &self.page_ptrs {
308+
unsafe {
309+
// We already know only non-null ptrs are pushed to self.pages
310+
let addr: std::ptr::NonNull<std::ffi::c_void> =
311+
std::ptr::NonNull::new_unchecked(pg.cast());
312+
mman::mprotect(addr, self.page_size, prot)?;
313+
}
314+
}
315+
for &(hpg, size) in &self.huge_ptrs {
316+
unsafe {
317+
let addr = std::ptr::NonNull::new_unchecked(hpg.cast());
318+
mman::mprotect(addr, size.next_multiple_of(self.page_size), prot)?;
319+
}
320+
}
321+
Ok(())
322+
}
281323
}
282324

283325
#[cfg(test)]

src/alloc_addresses/mod.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,13 +470,46 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
470470
/// This overapproximates the modifications which external code might make to memory:
471471
/// We set all reachable allocations as initialized, mark all reachable provenances as exposed
472472
/// and overwrite them with `Provenance::WILDCARD`.
473-
fn prepare_exposed_for_native_call(&mut self) -> InterpResult<'tcx> {
473+
fn prepare_exposed_for_native_call(&mut self, _paranoid: bool) -> InterpResult<'tcx> {
474474
let this = self.eval_context_mut();
475475
// We need to make a deep copy of this list, but it's fine; it also serves as scratch space
476476
// for the search within `prepare_for_native_call`.
477477
let exposed: Vec<AllocId> =
478478
this.machine.alloc_addresses.get_mut().exposed.iter().copied().collect();
479-
this.prepare_for_native_call(exposed)
479+
this.prepare_for_native_call(exposed /*, paranoid*/)
480+
}
481+
482+
/// Makes use of information obtained about memory accesses during FFI to determine which
483+
/// provenances should be exposed. Note that if `prepare_exposed_for_native_call` was not
484+
/// called before the FFI (with `paranoid` set to false) then some of the writes may be
485+
/// lost!
486+
#[cfg(target_os = "linux")]
487+
fn apply_events(
488+
&mut self,
489+
events: crate::shims::trace::messages::MemEvents,
490+
) -> InterpResult<'tcx> {
491+
let this = self.eval_context_mut();
492+
493+
let mut reads = vec![];
494+
let mut writes = vec![];
495+
for acc in events.acc_events {
496+
match acc {
497+
// Ideally, we'd skip reads that occur after certain bytes were
498+
// already written to. However, these are always just conservative
499+
// overestimates - Read(range) means "a read maybe happened
500+
// spanning at most range" - so we can't make use of this for
501+
// now. Maybe we could also skip over reads/writes that hit the
502+
// same bytes, but that's best added together with the stuff above.
503+
shims::trace::AccessEvent::Read(range) => reads.push(range),
504+
shims::trace::AccessEvent::Write(range) => {
505+
writes.push(range);
506+
}
507+
}
508+
}
509+
let _exposed: Vec<AllocId> =
510+
this.machine.alloc_addresses.get_mut().exposed.iter().copied().collect();
511+
interp_ok(())
512+
//this.apply_accesses(exposed, events.reads, events.writes)
480513
}
481514
}
482515

src/shims/native_lib.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
219219
}
220220
}
221221

222-
// Prepare all exposed memory.
223-
this.prepare_exposed_for_native_call()?;
222+
// Prepare all exposed memory, depending on whether we have a supervisor process.
223+
#[cfg(target_os = "linux")]
224+
if super::trace::Supervisor::poll() {
225+
this.prepare_exposed_for_native_call(false)?;
226+
} else {
227+
//this.prepare_exposed_for_native_call(true)?;
228+
//eprintln!("Oh noes!");
229+
panic!("No ptrace!");
230+
}
231+
#[cfg(not(target_os = "linux"))]
232+
this.prepare_exposed_for_native_call(true)?;
224233

225234
// Convert them to `libffi::high::Arg` type.
226235
let libffi_args = libffi_args
@@ -229,7 +238,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
229238
.collect::<Vec<libffi::high::Arg<'_>>>();
230239

231240
// Call the function and store output, depending on return type in the function signature.
232-
let (ret, _) = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
241+
let (ret, maybe_memevents) =
242+
this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
243+
244+
#[cfg(target_os = "linux")]
245+
if let Some(events) = maybe_memevents {
246+
this.apply_events(events)?;
247+
}
248+
#[cfg(not(target_os = "linux"))]
249+
let _ = maybe_memevents; // Suppress the unused warning
233250

234251
this.write_immediate(*ret, dest)?;
235252
interp_ok(true)

src/shims/trace/child.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ impl Supervisor {
6565
let stack_ptr = raw_stack_ptr.expose_provenance();
6666
let start_info = StartFfiInfo { page_ptrs, stack_ptr };
6767

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+
6878
// Send over the info.
6979
// NB: if we do not wait to receive a blank confirmation response, it is
7080
// possible that the supervisor is alerted of the SIGSTOP *before* it has
@@ -102,6 +112,9 @@ impl Supervisor {
102112
// normal
103113
signal::raise(signal::SIGUSR1).unwrap();
104114

115+
// This is safe! It just sets memory to normal expected permissions
116+
alloc.borrow_mut().unprep_ffi();
117+
105118
// If this is `None`, then `raw_stack_ptr` is None and does not need to
106119
// be deallocated (and there's no need to worry about the guard, since
107120
// it contains nothing)

0 commit comments

Comments
 (0)