Skip to content

native_lib/trace: fix and reenable #4435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
21 changes: 9 additions & 12 deletions src/alloc/isolated_alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,31 +302,28 @@ impl IsolatedAlloc {
}
}

/// Returns a vector of page addresses managed by the allocator.
pub fn pages(&self) -> Vec<usize> {
let mut pages: Vec<usize> =
self.page_ptrs.iter().map(|p| p.expose_provenance().get()).collect();
for (ptr, size) in self.huge_ptrs.iter() {
for i in 0..size / self.page_size {
pages.push(ptr.expose_provenance().get().strict_add(i * self.page_size));
}
}
pages
/// Returns a list of page addresses managed by the allocator.
pub fn pages(&self) -> impl Iterator<Item = usize> {
let pages = self.page_ptrs.iter().map(|p| p.expose_provenance().get());
pages.chain(self.huge_ptrs.iter().flat_map(|(ptr, size)| {
(0..size / self.page_size)
.map(|i| ptr.expose_provenance().get().strict_add(i * self.page_size))
}))
}

/// Protects all owned memory as `PROT_NONE`, preventing accesses.
///
/// SAFETY: Accessing memory after this point will result in a segfault
/// unless it is first unprotected.
pub unsafe fn prepare_ffi(&mut self) -> Result<(), nix::errno::Errno> {
pub unsafe fn start_ffi(&mut self) -> Result<(), nix::errno::Errno> {
let prot = mman::ProtFlags::PROT_NONE;
unsafe { self.mprotect(prot) }
}

/// Deprotects all owned memory by setting it to RW. Erroring here is very
/// likely unrecoverable, so it may panic if applying those permissions
/// fails.
pub fn unprep_ffi(&mut self) {
pub fn end_ffi(&mut self) {
let prot = mman::ProtFlags::PROT_READ | mman::ProtFlags::PROT_WRITE;
unsafe {
self.mprotect(prot).unwrap();
Expand Down
30 changes: 16 additions & 14 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,6 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
} else {
let return_code = miri::eval_entry(tcx, entry_def_id, entry_type, &config, None)
.unwrap_or_else(|| {
//#[cfg(target_os = "linux")]
//miri::native_lib::register_retcode_sv(rustc_driver::EXIT_FAILURE);
tcx.dcx().abort_if_errors();
rustc_driver::EXIT_FAILURE
});
Expand Down Expand Up @@ -337,6 +335,9 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
fn exit(exit_code: i32) -> ! {
// Drop the tracing guard before exiting, so tracing calls are flushed correctly.
deinit_loggers();
// Make sure the supervisor knows about the code code.
#[cfg(target_os = "linux")]
miri::native_lib::register_retcode_sv(exit_code);
std::process::exit(exit_code);
}

Expand All @@ -355,6 +356,11 @@ fn run_compiler_and_exit(
args: &[String],
callbacks: &mut (dyn rustc_driver::Callbacks + Send),
) -> ! {
// Install the ctrlc handler that sets `rustc_const_eval::CTRL_C_RECEIVED`, even if
// MIRI_BE_RUSTC is set. We do this late so that when `native_lib::init_sv` is called,
// there are no other threads.
rustc_driver::install_ctrlc_handler();

// Invoke compiler, catch any unwinding panics and handle return code.
let exit_code =
rustc_driver::catch_with_exit_code(move || rustc_driver::run_compiler(args, callbacks));
Expand Down Expand Up @@ -439,10 +445,6 @@ fn main() {
let args = rustc_driver::catch_fatal_errors(|| rustc_driver::args::raw_args(&early_dcx))
.unwrap_or_else(|_| std::process::exit(rustc_driver::EXIT_FAILURE));

// Install the ctrlc handler that sets `rustc_const_eval::CTRL_C_RECEIVED`, even if
// MIRI_BE_RUSTC is set.
rustc_driver::install_ctrlc_handler();

// If the environment asks us to actually be rustc, then do that.
if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {
// Earliest rustc setup.
Expand Down Expand Up @@ -750,15 +752,15 @@ fn main() {

debug!("rustc arguments: {:?}", rustc_args);
debug!("crate arguments: {:?}", miri_config.args);
#[cfg(target_os = "linux")]
if !miri_config.native_lib.is_empty() && miri_config.native_lib_enable_tracing {
// FIXME: This should display a diagnostic / warning on error
// SAFETY: If any other threads exist at this point (namely for the ctrlc
// handler), they will not interact with anything on the main rustc/Miri
// thread in an async-signal-unsafe way such as by accessing shared
// semaphores, etc.; the handler only calls `sleep()` and `exit()`, which
// are async-signal-safe, as is accessing atomics
//let _ = unsafe { miri::native_lib::init_sv() };
// SAFETY: No other threads are running
#[cfg(target_os = "linux")]
if unsafe { miri::native_lib::init_sv() }.is_err() {
eprintln!(
"warning: The native-lib tracer could not be started. Is this an x86 Linux system, and does Miri have permissions to ptrace?\n\
Falling back to non-tracing native-lib mode."
);
}
}
run_compiler_and_exit(
&rustc_args,
Expand Down
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ pub use rustc_const_eval::interpret::{self, AllocMap, Provenance as _};
use rustc_middle::{bug, span_bug};
use tracing::{info, trace};

//#[cfg(target_os = "linux")]
//pub mod native_lib {
// pub use crate::shims::{init_sv, register_retcode_sv};
//}
#[cfg(target_os = "linux")]
pub mod native_lib {
pub use crate::shims::{init_sv, register_retcode_sv};
}

// Type aliases that set the provenance parameter.
pub type Pointer = interpret::Pointer<Option<machine::Provenance>>;
Expand Down
4 changes: 2 additions & 2 deletions src/shims/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ pub mod tls;
pub mod unwind;

pub use self::files::FdTable;
//#[cfg(target_os = "linux")]
//pub use self::native_lib::trace::{init_sv, register_retcode_sv};
#[cfg(target_os = "linux")]
pub use self::native_lib::trace::{init_sv, register_retcode_sv};
pub use self::unix::{DirTable, EpollInterestTable};

/// What needs to be done after emulating an item (a shim or an intrinsic) is done.
Expand Down
90 changes: 54 additions & 36 deletions src/shims/native_lib/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
//! Implements calling functions from a native library.

// FIXME: disabled since it fails to build on many targets.
//#[cfg(target_os = "linux")]
//pub mod trace;

use std::ops::Deref;

use libffi::high::call as ffi;
Expand All @@ -13,14 +9,49 @@ use rustc_middle::mir::interpret::Pointer;
use rustc_middle::ty::{self as ty, IntTy, UintTy};
use rustc_span::Symbol;

//#[cfg(target_os = "linux")]
//use self::trace::Supervisor;
#[cfg_attr(
not(all(
target_os = "linux",
target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")
)),
path = "trace/stub.rs"
)]
pub mod trace;

use crate::*;

//#[cfg(target_os = "linux")]
//type CallResult<'tcx> = InterpResult<'tcx, (ImmTy<'tcx>, Option<self::trace::messages::MemEvents>)>;
//#[cfg(not(target_os = "linux"))]
type CallResult<'tcx> = InterpResult<'tcx, (ImmTy<'tcx>, Option<!>)>;
/// The final results of an FFI trace, containing every relevant event detected
/// by the tracer.
#[allow(dead_code)]
#[cfg_attr(target_os = "linux", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug)]
pub struct MemEvents {
/// An list of memory accesses that occurred, in the order they occurred in.
pub acc_events: Vec<AccessEvent>,
}

/// A single memory access.
#[allow(dead_code)]
#[cfg_attr(target_os = "linux", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug)]
pub enum AccessEvent {
/// A read occurred on this memory range.
Read(AccessRange),
/// A read occurred on this memory range.
Write(AccessRange),
}

/// The memory touched by a given access.
#[allow(dead_code)]
#[cfg_attr(target_os = "linux", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug)]
pub struct AccessRange {
/// The base address in memory where an access occurred.
pub addr: usize,
/// The number of bytes affected from the base.
pub size: usize,
}

impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
Expand All @@ -31,18 +62,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
dest: &MPlaceTy<'tcx>,
ptr: CodePtr,
libffi_args: Vec<libffi::high::Arg<'a>>,
) -> CallResult<'tcx> {
) -> InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<MemEvents>)> {
let this = self.eval_context_mut();
//#[cfg(target_os = "linux")]
//let alloc = this.machine.allocator.as_ref().unwrap();

// SAFETY: We don't touch the machine memory past this point.
//#[cfg(target_os = "linux")]
//let (guard, stack_ptr) = unsafe { Supervisor::start_ffi(alloc) };
#[cfg(target_os = "linux")]
let alloc = this.machine.allocator.as_ref().unwrap();
#[cfg(not(target_os = "linux"))]
// Placeholder value.
let alloc = ();

// Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value
// as the specified primitive integer type
let res = 'res: {
trace::Supervisor::do_ffi(alloc, || {
// Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value
// as the specified primitive integer type
let scalar = match dest.layout.ty.kind() {
// ints
ty::Int(IntTy::I8) => {
Expand Down Expand Up @@ -93,31 +123,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// have the output_type `Tuple([])`.
ty::Tuple(t_list) if (*t_list).deref().is_empty() => {
unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) };
break 'res interp_ok(ImmTy::uninit(dest.layout));
return interp_ok(ImmTy::uninit(dest.layout));
}
ty::RawPtr(..) => {
let x = unsafe { ffi::call::<*const ()>(ptr, libffi_args.as_slice()) };
let ptr = Pointer::new(Provenance::Wildcard, Size::from_bytes(x.addr()));
Scalar::from_pointer(ptr, this)
}
_ =>
break 'res Err(err_unsup_format!(
return Err(err_unsup_format!(
"unsupported return type for native call: {:?}",
link_name
))
.into(),
};
interp_ok(ImmTy::from_scalar(scalar, dest.layout))
};

// SAFETY: We got the guard and stack pointer from start_ffi, and
// the allocator is the same
//#[cfg(target_os = "linux")]
//let events = unsafe { Supervisor::end_ffi(alloc, guard, stack_ptr) };
//#[cfg(not(target_os = "linux"))]
let events = None;

interp_ok((res?, events))
})
}

/// Get the pointer to the function of the specified name in the shared object file,
Expand Down Expand Up @@ -214,10 +235,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if !this.machine.native_call_mem_warned.replace(true) {
// Newly set, so first time we get here.
this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem {
//#[cfg(target_os = "linux")]
//tracing: self::trace::Supervisor::is_enabled(),
//#[cfg(not(target_os = "linux"))]
tracing: false,
tracing: self::trace::Supervisor::is_enabled(),
});
}

Expand Down
Loading