Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_ref();

fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>(
this: &MiriInterpCx<'tcx>,
ecx: &MiriInterpCx<'tcx>,
src: F,
cast_to: TyAndLayout<'tcx>,
round: rustc_apfloat::Round,
Expand All @@ -1029,7 +1029,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Nothing else
_ =>
span_bug!(
this.cur_span(),
ecx.cur_span(),
"attempted float-to-int conversion with non-int output type {}",
cast_to.ty,
),
Expand Down
12 changes: 6 additions & 6 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,20 +730,20 @@ impl<'tcx> MiriMachine<'tcx> {
}

pub(crate) fn late_init(
this: &mut MiriInterpCx<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
config: &MiriConfig,
on_main_stack_empty: StackEmptyCallback<'tcx>,
) -> InterpResult<'tcx> {
EnvVars::init(this, config)?;
MiriMachine::init_extern_statics(this)?;
ThreadManager::init(this, on_main_stack_empty);
EnvVars::init(ecx, config)?;
MiriMachine::init_extern_statics(ecx)?;
ThreadManager::init(ecx, on_main_stack_empty);
interp_ok(())
}

pub(crate) fn add_extern_static(this: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) {
pub(crate) fn add_extern_static(ecx: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) {
// This got just allocated, so there definitely is a pointer here.
let ptr = ptr.into_pointer_or_addr().unwrap();
this.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
ecx.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
}

pub(crate) fn communicate(&self) -> bool {
Expand Down
20 changes: 10 additions & 10 deletions src/provenance_gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,27 +195,27 @@ impl LiveAllocs<'_, '_> {
}
}

fn remove_unreachable_tags<'tcx>(this: &mut MiriInterpCx<'tcx>, tags: FxHashSet<BorTag>) {
fn remove_unreachable_tags<'tcx>(ecx: &mut MiriInterpCx<'tcx>, tags: FxHashSet<BorTag>) {
// Avoid iterating all allocations if there's no borrow tracker anyway.
if this.machine.borrow_tracker.is_some() {
this.memory.alloc_map().iter(|it| {
if ecx.machine.borrow_tracker.is_some() {
ecx.memory.alloc_map().iter(|it| {
for (_id, (_kind, alloc)) in it {
alloc.extra.borrow_tracker.as_ref().unwrap().remove_unreachable_tags(&tags);
}
});
}
}

fn remove_unreachable_allocs<'tcx>(this: &mut MiriInterpCx<'tcx>, allocs: FxHashSet<AllocId>) {
let allocs = LiveAllocs { ecx: this, collected: allocs };
this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
this.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
this.machine.alloc_addresses.borrow_mut().remove_unreachable_allocs(&allocs);
if let Some(borrow_tracker) = &this.machine.borrow_tracker {
fn remove_unreachable_allocs<'tcx>(ecx: &mut MiriInterpCx<'tcx>, allocs: FxHashSet<AllocId>) {
let allocs = LiveAllocs { ecx, collected: allocs };
ecx.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
ecx.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
ecx.machine.alloc_addresses.borrow_mut().remove_unreachable_allocs(&allocs);
if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);
}
// Clean up core (non-Miri-specific) state.
this.remove_unreachable_allocs(&allocs.collected);
ecx.remove_unreachable_allocs(&allocs.collected);
}

impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
Expand Down
26 changes: 13 additions & 13 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,28 +496,28 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {

// Rust allocation
"__rust_alloc" | "miri_alloc" => {
let default = |this: &mut MiriInterpCx<'tcx>| {
let default = |ecx: &mut MiriInterpCx<'tcx>| {
// Only call `check_shim` when `#[global_allocator]` isn't used. When that
// macro is used, we act like no shim exists, so that the exported function can run.
let [size, align] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
let size = this.read_target_usize(size)?;
let align = this.read_target_usize(align)?;
let [size, align] = ecx.check_shim(abi, ExternAbi::Rust, link_name, args)?;
let size = ecx.read_target_usize(size)?;
let align = ecx.read_target_usize(align)?;

this.check_rustc_alloc_request(size, align)?;
ecx.check_rustc_alloc_request(size, align)?;

let memory_kind = match link_name.as_str() {
"__rust_alloc" => MiriMemoryKind::Rust,
"miri_alloc" => MiriMemoryKind::Miri,
_ => unreachable!(),
};

let ptr = this.allocate_ptr(
let ptr = ecx.allocate_ptr(
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
memory_kind.into(),
)?;

this.write_pointer(ptr, dest)
ecx.write_pointer(ptr, dest)
};

match link_name.as_str() {
Expand Down Expand Up @@ -555,14 +555,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
});
}
"__rust_dealloc" | "miri_dealloc" => {
let default = |this: &mut MiriInterpCx<'tcx>| {
let default = |ecx: &mut MiriInterpCx<'tcx>| {
// See the comment for `__rust_alloc` why `check_shim` is only called in the
// default case.
let [ptr, old_size, align] =
this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let old_size = this.read_target_usize(old_size)?;
let align = this.read_target_usize(align)?;
ecx.check_shim(abi, ExternAbi::Rust, link_name, args)?;
let ptr = ecx.read_pointer(ptr)?;
let old_size = ecx.read_target_usize(old_size)?;
let align = ecx.read_target_usize(align)?;

let memory_kind = match link_name.as_str() {
"__rust_dealloc" => MiriMemoryKind::Rust,
Expand All @@ -571,7 +571,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
};

// No need to check old_size/align; we anyway check that they match the allocation.
this.deallocate_ptr(
ecx.deallocate_ptr(
ptr,
Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
memory_kind.into(),
Expand Down
26 changes: 13 additions & 13 deletions src/shims/unix/android/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,49 @@ use crate::*;
const TASK_COMM_LEN: usize = 16;

pub fn prctl<'tcx>(
this: &mut MiriInterpCx<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
link_name: Symbol,
abi: ExternAbi,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
// We do not use `check_shim` here because `prctl` is variadic. The argument
// count is checked bellow.
this.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;
ecx.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;

// FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch.
let pr_set_name = 15;
let pr_get_name = 16;

let [op] = check_min_arg_count("prctl", args)?;
let res = match this.read_scalar(op)?.to_i32()? {
let res = match ecx.read_scalar(op)?.to_i32()? {
op if op == pr_set_name => {
let [_, name] = check_min_arg_count("prctl(PR_SET_NAME, ...)", args)?;
let name = this.read_scalar(name)?;
let thread = this.pthread_self()?;
let name = ecx.read_scalar(name)?;
let thread = ecx.pthread_self()?;
// The Linux kernel silently truncates long names.
// https://www.man7.org/linux/man-pages/man2/PR_SET_NAME.2const.html
let res =
this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
ecx.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
assert_eq!(res, ThreadNameResult::Ok);
Scalar::from_u32(0)
}
op if op == pr_get_name => {
let [_, name] = check_min_arg_count("prctl(PR_GET_NAME, ...)", args)?;
let name = this.read_scalar(name)?;
let thread = this.pthread_self()?;
let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, this);
this.check_ptr_access(
name.to_pointer(this)?,
let name = ecx.read_scalar(name)?;
let thread = ecx.pthread_self()?;
let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, ecx);
ecx.check_ptr_access(
name.to_pointer(ecx)?,
Size::from_bytes(TASK_COMM_LEN),
CheckInAllocMsg::MemoryAccessTest,
)?;
let res = this.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
let res = ecx.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
assert_eq!(res, ThreadNameResult::Ok);
Scalar::from_u32(0)
}
op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op),
};
this.write_scalar(res, dest)?;
ecx.write_scalar(res, dest)?;
interp_ok(())
}
72 changes: 36 additions & 36 deletions src/shims/unix/linux/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ struct LinuxFutex {
/// Implementation of the SYS_futex syscall.
/// `args` is the arguments *including* the syscall number.
pub fn futex<'tcx>(
this: &mut MiriInterpCx<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
Expand All @@ -26,19 +26,19 @@ pub fn futex<'tcx>(
// The first three arguments (after the syscall number itself) are the same to all futex operations:
// (int *addr, int op, int val).
// We checked above that these definitely exist.
let addr = this.read_pointer(addr)?;
let op = this.read_scalar(op)?.to_i32()?;
let val = this.read_scalar(val)?.to_i32()?;
let addr = ecx.read_pointer(addr)?;
let op = ecx.read_scalar(op)?.to_i32()?;
let val = ecx.read_scalar(val)?.to_i32()?;

// This is a vararg function so we have to bring our own type for this pointer.
let addr = this.ptr_to_mplace(addr, this.machine.layouts.i32);
let addr = ecx.ptr_to_mplace(addr, ecx.machine.layouts.i32);

let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG");
let futex_wait = this.eval_libc_i32("FUTEX_WAIT");
let futex_wait_bitset = this.eval_libc_i32("FUTEX_WAIT_BITSET");
let futex_wake = this.eval_libc_i32("FUTEX_WAKE");
let futex_wake_bitset = this.eval_libc_i32("FUTEX_WAKE_BITSET");
let futex_realtime = this.eval_libc_i32("FUTEX_CLOCK_REALTIME");
let futex_private = ecx.eval_libc_i32("FUTEX_PRIVATE_FLAG");
let futex_wait = ecx.eval_libc_i32("FUTEX_WAIT");
let futex_wait_bitset = ecx.eval_libc_i32("FUTEX_WAIT_BITSET");
let futex_wake = ecx.eval_libc_i32("FUTEX_WAKE");
let futex_wake_bitset = ecx.eval_libc_i32("FUTEX_WAKE_BITSET");
let futex_realtime = ecx.eval_libc_i32("FUTEX_CLOCK_REALTIME");

// FUTEX_PRIVATE enables an optimization that stops it from working across processes.
// Miri doesn't support that anyway, so we ignore that flag.
Expand All @@ -57,31 +57,31 @@ pub fn futex<'tcx>(
let (timeout, bitset) = if wait_bitset {
let [_, _, _, _, timeout, uaddr2, bitset] =
check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT_BITSET, ...)`", args)?;
let _timeout = this.read_pointer(timeout)?;
let _uaddr2 = this.read_pointer(uaddr2)?;
(timeout, this.read_scalar(bitset)?.to_u32()?)
let _timeout = ecx.read_pointer(timeout)?;
let _uaddr2 = ecx.read_pointer(uaddr2)?;
(timeout, ecx.read_scalar(bitset)?.to_u32()?)
} else {
let [_, _, _, _, timeout] =
check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT, ...)`", args)?;
(timeout, u32::MAX)
};

if bitset == 0 {
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
return ecx.set_last_error_and_return(LibcError("EINVAL"), dest);
}

let timeout = this.deref_pointer_as(timeout, this.libc_ty_layout("timespec"))?;
let timeout = if this.ptr_is_null(timeout.ptr())? {
let timeout = ecx.deref_pointer_as(timeout, ecx.libc_ty_layout("timespec"))?;
let timeout = if ecx.ptr_is_null(timeout.ptr())? {
None
} else {
let duration = match this.read_timespec(&timeout)? {
let duration = match ecx.read_timespec(&timeout)? {
Some(duration) => duration,
None => {
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
return ecx.set_last_error_and_return(LibcError("EINVAL"), dest);
}
};
let timeout_clock = if op & futex_realtime == futex_realtime {
this.check_no_isolation(
ecx.check_no_isolation(
"`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`",
)?;
TimeoutClock::RealTime
Expand Down Expand Up @@ -139,36 +139,36 @@ pub fn futex<'tcx>(
//
// Thankfully, preemptions cannot happen inside a Miri shim, so we do not need to
// do anything special to guarantee fence-load-comparison atomicity.
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
ecx.atomic_fence(AtomicFenceOrd::SeqCst)?;
// Read an `i32` through the pointer, regardless of any wrapper types.
// It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`.
// We do an acquire read -- it only seems reasonable that if we observe a value here, we
// actually establish an ordering with that value.
let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Acquire)?.to_i32()?;
let futex_val = ecx.read_scalar_atomic(&addr, AtomicReadOrd::Acquire)?.to_i32()?;
if val == futex_val {
// The value still matches, so we block the thread and make it wait for FUTEX_WAKE.

// This cannot fail since we already did an atomic acquire read on that pointer.
// Acquire reads are only allowed on mutable memory.
let futex_ref = this
let futex_ref = ecx
.get_sync_or_init(addr.ptr(), |_| LinuxFutex { futex: Default::default() })
.unwrap()
.futex
.clone();

this.futex_wait(
ecx.futex_wait(
futex_ref,
bitset,
timeout,
Scalar::from_target_isize(0, this), // retval_succ
Scalar::from_target_isize(-1, this), // retval_timeout
Scalar::from_target_isize(0, ecx), // retval_succ
Scalar::from_target_isize(-1, ecx), // retval_timeout
dest.clone(),
LibcError("ETIMEDOUT"), // errno_timeout
);
} else {
// The futex value doesn't match the expected value, so we return failure
// right away without sleeping: -1 and errno set to EAGAIN.
return this.set_last_error_and_return(LibcError("EAGAIN"), dest);
return ecx.set_last_error_and_return(LibcError("EAGAIN"), dest);
}
}
// FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val)
Expand All @@ -179,42 +179,42 @@ pub fn futex<'tcx>(
// Same as FUTEX_WAKE, but allows you to specify a bitset to select which threads to wake up.
op if op == futex_wake || op == futex_wake_bitset => {
let Some(futex_ref) =
this.get_sync_or_init(addr.ptr(), |_| LinuxFutex { futex: Default::default() })
ecx.get_sync_or_init(addr.ptr(), |_| LinuxFutex { futex: Default::default() })
else {
// No AllocId, or no live allocation at that AllocId.
// Return an error code. (That seems nicer than silently doing something non-intuitive.)
// This means that if an address gets reused by a new allocation,
// we'll use an independent futex queue for this... that seems acceptable.
return this.set_last_error_and_return(LibcError("EFAULT"), dest);
return ecx.set_last_error_and_return(LibcError("EFAULT"), dest);
};
let futex_ref = futex_ref.futex.clone();

let bitset = if op == futex_wake_bitset {
let [_, _, _, _, timeout, uaddr2, bitset] =
check_min_arg_count("`syscall(SYS_futex, FUTEX_WAKE_BITSET, ...)`", args)?;
let _timeout = this.read_pointer(timeout)?;
let _uaddr2 = this.read_pointer(uaddr2)?;
this.read_scalar(bitset)?.to_u32()?
let _timeout = ecx.read_pointer(timeout)?;
let _uaddr2 = ecx.read_pointer(uaddr2)?;
ecx.read_scalar(bitset)?.to_u32()?
} else {
u32::MAX
};
if bitset == 0 {
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
return ecx.set_last_error_and_return(LibcError("EINVAL"), dest);
}
// Together with the SeqCst fence in futex_wait, this makes sure that futex_wait
// will see the latest value on addr which could be changed by our caller
// before doing the syscall.
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
ecx.atomic_fence(AtomicFenceOrd::SeqCst)?;
let mut n = 0;
#[expect(clippy::arithmetic_side_effects)]
for _ in 0..val {
if this.futex_wake(&futex_ref, bitset)? {
if ecx.futex_wake(&futex_ref, bitset)? {
n += 1;
} else {
break;
}
}
this.write_scalar(Scalar::from_target_isize(n, this), dest)?;
ecx.write_scalar(Scalar::from_target_isize(n, ecx), dest)?;
}
op => throw_unsup_format!("Miri does not support `futex` syscall with op={}", op),
}
Expand Down
Loading