Skip to content

Commit 180efe5

Browse files
committed
wip: hook up bits
1 parent 0913b24 commit 180efe5

File tree

3 files changed

+98
-19
lines changed

3 files changed

+98
-19
lines changed

src/shims/native_lib/mod.rs

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,27 @@ pub struct MemEvents {
4040
/// A single memory access.
4141
#[allow(dead_code)]
4242
#[cfg_attr(target_os = "linux", derive(serde::Serialize, serde::Deserialize))]
43-
#[derive(Debug)]
43+
#[derive(Clone, Debug)]
4444
pub enum AccessEvent {
4545
/// A read occurred on this memory range.
4646
Read(AccessRange),
47-
/// A read occurred on this memory range.
47+
/// A read occurred on this memory range.
4848
Write(AccessRange),
4949
}
5050

51+
impl AccessEvent {
52+
fn get_range(&self) -> AccessRange {
53+
match self {
54+
AccessEvent::Read(access_range) => access_range.clone(),
55+
AccessEvent::Write(access_range) => access_range.clone(),
56+
}
57+
}
58+
59+
fn is_read(&self) -> bool {
60+
matches!(self, AccessEvent::Read(_))
61+
}
62+
}
63+
5164
/// The memory touched by a given access.
5265
#[allow(dead_code)]
5366
#[cfg_attr(target_os = "linux", derive(serde::Serialize, serde::Deserialize))]
@@ -198,6 +211,52 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
198211
}
199212
None
200213
}
214+
215+
/// Applies the `events` to Miri's internal state. The event vector must be
216+
/// ordered sequentially by when the accesses happened, and the sizes are
217+
/// assumed to be exact.
218+
fn tracing_apply_accesses(&mut self, events: MemEvents) -> InterpResult<'tcx> {
219+
let this = self.eval_context_mut();
220+
221+
for evt in events.acc_events {
222+
let evt_rg = evt.get_range();
223+
// We're assuming an access only touches 1 allocation.
224+
let alloc_id = this
225+
.alloc_id_from_addr(evt_rg.addr.to_u64(), evt_rg.size.try_into().unwrap(), true)
226+
.expect("Foreign code did an out-of-bounds access!");
227+
228+
// Get the (size, len) pair for the current allocation.
229+
let (alloc_addr, alloc_len) = {
230+
let alloc = this.get_alloc_raw(alloc_id)?;
231+
(alloc.get_bytes_unchecked_raw().addr(), alloc.len())
232+
};
233+
234+
let unshifted_overlap = std::cmp::max(evt_rg.addr, alloc_addr)
235+
..std::cmp::min(
236+
evt_rg.addr.strict_add(evt_rg.size),
237+
alloc_addr.strict_add(alloc_len),
238+
);
239+
// Shift the overlap range to be an offset from the allocation base addr.
240+
let overlap = unshifted_overlap.start.strict_sub(alloc_addr)
241+
..unshifted_overlap.end.strict_sub(alloc_addr);
242+
243+
if evt.is_read() {
244+
let alloc = this.get_alloc_raw(alloc_id)?;
245+
let p_map = alloc.provenance();
246+
for idx in overlap {
247+
// If a provenance was read by the foreign code, expose it.
248+
if let Some(prov) = p_map.get(Size::from_bytes(idx), this) {
249+
this.expose_provenance(prov)?;
250+
}
251+
}
252+
} else {
253+
let (_alloc_mut, _m) = this.get_alloc_raw_mut(alloc_id)?;
254+
// TODO: expose a way to write wildcards on a given range and mark it as init
255+
}
256+
}
257+
258+
interp_ok(())
259+
}
201260
}
202261

203262
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -223,6 +282,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
223282
}
224283
};
225284

285+
// Do we have ptrace?
286+
let tracing = trace::Supervisor::is_enabled();
287+
226288
// Get the function arguments, and convert them to `libffi`-compatible form.
227289
let mut libffi_args = Vec::<CArg>::with_capacity(args.len());
228290
for arg in args.iter() {
@@ -242,9 +304,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
242304
// The first time this happens, print a warning.
243305
if !this.machine.native_call_mem_warned.replace(true) {
244306
// Newly set, so first time we get here.
245-
this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem {
246-
tracing: self::trace::Supervisor::is_enabled(),
247-
});
307+
this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem { tracing });
248308
}
249309

250310
this.expose_provenance(prov)?;
@@ -270,15 +330,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
270330
// be read by FFI. The `black_box` is defensive programming as LLVM likes
271331
// to (incorrectly) optimize away ptr2int casts whose result is unused.
272332
std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance());
273-
// Expose all provenances in this allocation, since the native code can do $whatever.
274-
for prov in alloc.provenance().provenances() {
275-
this.expose_provenance(prov)?;
333+
334+
if !tracing {
335+
// Expose all provenances in this allocation, since the native code can do $whatever.
336+
for prov in alloc.provenance().provenances() {
337+
this.expose_provenance(prov)?;
338+
}
276339
}
277340

278341
// Prepare for possible write from native code if mutable.
279342
if info.mutbl.is_mut() {
280-
let alloc = &mut this.get_alloc_raw_mut(alloc_id)?.0;
281-
alloc.prepare_for_native_access();
343+
let alloc = this.get_alloc_raw_mut(alloc_id)?.0;
344+
if tracing {
345+
let full_range =
346+
AllocRange { start: Size::ZERO, size: Size::from_bytes(alloc.len()) };
347+
// Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be.
348+
for chunk in alloc.init_mask().clone().range_as_init_chunks(full_range) {
349+
if !chunk.is_init() {
350+
let uninit_bytes = unsafe {
351+
let start = chunk.range().start.bytes_usize();
352+
let len = chunk.range().end.bytes_usize().strict_sub(start);
353+
let ptr = alloc.get_bytes_unchecked_raw_mut().add(start);
354+
std::slice::from_raw_parts_mut(ptr, len)
355+
};
356+
uninit_bytes.fill(0);
357+
}
358+
}
359+
} else {
360+
// FIXME: Make this take an arg to determine whether it actually
361+
// writes wildcard prov & marks init, so we don't duplicate code above.
362+
alloc.prepare_for_native_access();
363+
}
282364
// Also expose *mutable* provenance for the interpreter-level allocation.
283365
std::hint::black_box(alloc.get_bytes_unchecked_raw_mut().expose_provenance());
284366
}
@@ -290,10 +372,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
290372
let (ret, maybe_memevents) =
291373
this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
292374

293-
if cfg!(target_os = "linux")
294-
&& let Some(events) = maybe_memevents
295-
{
296-
trace!("Registered FFI events:\n{events:#0x?}");
375+
if tracing {
376+
this.tracing_apply_accesses(maybe_memevents.unwrap())?;
297377
}
298378

299379
this.write_immediate(*ret, dest)?;

src/shims/native_lib/trace/child.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,9 @@ pub unsafe fn init_sv() -> Result<(), SvInitError> {
202202
// The "Ok" case means that we couldn't ptrace.
203203
Ok(e) => return Err(e),
204204
Err(p) => {
205-
eprintln!("Supervisor process panicked!\n{p:?}\n\nTry running again without using the native-lib tracer.");
205+
eprintln!(
206+
"Supervisor process panicked!\n{p:?}\n\nTry running again without using the native-lib tracer."
207+
);
206208
std::process::exit(1);
207209
}
208210
}

src/shims/native_lib/trace/parent.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -408,10 +408,7 @@ fn handle_segfault(
408408
match x86_operand.op_type {
409409
// We only care about memory accesses
410410
arch::x86::X86OperandType::Mem(_) => {
411-
let push = AccessRange {
412-
addr,
413-
size: x86_operand.size.into(),
414-
};
411+
let push = AccessRange { addr, size: x86_operand.size.into() };
415412
// It's called a "RegAccessType" but it also applies to memory
416413
let acc_ty = x86_operand.access.unwrap();
417414
if acc_ty.is_readable() {

0 commit comments

Comments
 (0)