Skip to content

Miri subtree update #139630

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

Merged
merged 14 commits into from
Apr 10, 2025
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
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2312,6 +2312,7 @@ name = "miri"
version = "0.1.0"
dependencies = [
"aes",
"bitflags",
"chrono",
"chrono-tz",
"colored",
Expand Down
1 change: 1 addition & 0 deletions src/tools/miri/Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ name = "miri"
version = "0.1.0"
dependencies = [
"aes",
"bitflags",
"chrono",
"chrono-tz",
"colored",
Expand Down
1 change: 1 addition & 0 deletions src/tools/miri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ measureme = "12"
chrono = { version = "0.4.38", default-features = false }
chrono-tz = "0.10"
directories = "6"
bitflags = "2.6"

# Copied from `compiler/rustc/Cargo.toml`.
# But only for some targets, it fails for others. Rustc configures this in its CI, but we can't
Expand Down
2 changes: 1 addition & 1 deletion src/tools/miri/ci/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ case $HOST_TARGET in
# Partially supported targets (tier 2)
BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator
UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe concurrency sync
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random sync concurrency thread epoll eventfd
TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm
Expand Down
2 changes: 1 addition & 1 deletion src/tools/miri/rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
25a615bf829b9f6d6f22da537e3851043f92e5f2
7d7de5bf3c3cbf9c2c5bbc5cbfb9197a8a427d35
115 changes: 105 additions & 10 deletions src/tools/miri/src/shims/files.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::any::Any;
use std::collections::BTreeMap;
use std::io::{IsTerminal, SeekFrom, Write};
use std::fs::{File, Metadata};
use std::io::{IsTerminal, Seek, SeekFrom, Write};
use std::marker::CoercePointee;
use std::ops::Deref;
use std::rc::{Rc, Weak};
Expand Down Expand Up @@ -192,7 +193,7 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
false
}

fn as_unix(&self) -> &dyn UnixFileDescription {
fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {
panic!("Not a unix file descriptor: {}", self.name());
}
}
Expand Down Expand Up @@ -278,6 +279,97 @@ impl FileDescription for io::Stderr {
}
}

#[derive(Debug)]
pub struct FileHandle {
pub(crate) file: File,
pub(crate) writable: bool,
}

impl FileDescription for FileHandle {
fn name(&self) -> &'static str {
"file"
}

fn read<'tcx>(
self: FileDescriptionRef<Self>,
communicate_allowed: bool,
ptr: Pointer,
len: usize,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");

let result = ecx.read_from_host(&self.file, len, ptr)?;
finish.call(ecx, result)
}

fn write<'tcx>(
self: FileDescriptionRef<Self>,
communicate_allowed: bool,
ptr: Pointer,
len: usize,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");

let result = ecx.write_to_host(&self.file, len, ptr)?;
finish.call(ecx, result)
}

fn seek<'tcx>(
&self,
communicate_allowed: bool,
offset: SeekFrom,
) -> InterpResult<'tcx, io::Result<u64>> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
interp_ok((&mut &self.file).seek(offset))
}

fn close<'tcx>(
self,
communicate_allowed: bool,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<()>> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
// We sync the file if it was opened in a mode different than read-only.
if self.writable {
// `File::sync_all` does the checks that are done when closing a file. We do this to
// to handle possible errors correctly.
let result = self.file.sync_all();
// Now we actually close the file and return the result.
drop(self.file);
interp_ok(result)
} else {
// We drop the file, this closes it but ignores any errors
// produced when closing it. This is done because
// `File::sync_all` cannot be done over files like
// `/dev/urandom` which are read-only. Check
// https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
// for a deeper discussion.
drop(self.file);
interp_ok(Ok(()))
}
}

fn metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result<Metadata>> {
interp_ok(self.file.metadata())
}

fn is_tty(&self, communicate_allowed: bool) -> bool {
communicate_allowed && self.file.is_terminal()
}

fn as_unix<'tcx>(&self, ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {
assert!(
ecx.target_os_is_unix(),
"unix file operations are only available for unix targets"
);
self
}
}

/// Like /dev/null
#[derive(Debug)]
pub struct NullOutput;
Expand All @@ -300,10 +392,13 @@ impl FileDescription for NullOutput {
}
}

/// Internal type of a file-descriptor - this is what [`FdTable`] expects
pub type FdNum = i32;

/// The file descriptor table
#[derive(Debug)]
pub struct FdTable {
pub fds: BTreeMap<i32, DynFileDescriptionRef>,
pub fds: BTreeMap<FdNum, DynFileDescriptionRef>,
/// Unique identifier for file description, used to differentiate between various file description.
next_file_description_id: FdId,
}
Expand Down Expand Up @@ -339,21 +434,21 @@ impl FdTable {
}

/// Insert a new file description to the FdTable.
pub fn insert_new(&mut self, fd: impl FileDescription) -> i32 {
pub fn insert_new(&mut self, fd: impl FileDescription) -> FdNum {
let fd_ref = self.new_ref(fd);
self.insert(fd_ref)
}

pub fn insert(&mut self, fd_ref: DynFileDescriptionRef) -> i32 {
pub fn insert(&mut self, fd_ref: DynFileDescriptionRef) -> FdNum {
self.insert_with_min_num(fd_ref, 0)
}

/// Insert a file description, giving it a file descriptor that is at least `min_fd_num`.
pub fn insert_with_min_num(
&mut self,
file_handle: DynFileDescriptionRef,
min_fd_num: i32,
) -> i32 {
min_fd_num: FdNum,
) -> FdNum {
// Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
// between used FDs, the find_map combinator will return it. If the first such unused FD
// is after all other used FDs, the find_map combinator will return None, and we will use
Expand All @@ -379,16 +474,16 @@ impl FdTable {
new_fd_num
}

pub fn get(&self, fd_num: i32) -> Option<DynFileDescriptionRef> {
pub fn get(&self, fd_num: FdNum) -> Option<DynFileDescriptionRef> {
let fd = self.fds.get(&fd_num)?;
Some(fd.clone())
}

pub fn remove(&mut self, fd_num: i32) -> Option<DynFileDescriptionRef> {
pub fn remove(&mut self, fd_num: FdNum) -> Option<DynFileDescriptionRef> {
self.fds.remove(&fd_num)
}

pub fn is_fd_num(&self, fd_num: i32) -> bool {
pub fn is_fd_num(&self, fd_num: FdNum) -> bool {
self.fds.contains_key(&fd_num)
}
}
Expand Down
36 changes: 26 additions & 10 deletions src/tools/miri/src/shims/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,16 +219,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

let filetime = this.deref_pointer_as(LPFILETIME_op, this.windows_ty_layout("FILETIME"))?;

let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC");
let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC");
let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH");
let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC;
let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC;

let duration = system_time_to_duration(&SystemTime::now())?
+ Duration::from_secs(SECONDS_TO_UNIX_EPOCH);
let duration_ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL))
.map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?;
let duration = this.system_time_since_windows_epoch(&SystemTime::now())?;
let duration_ticks = this.windows_ticks_for(duration)?;

let dwLowDateTime = u32::try_from(duration_ticks & 0x00000000FFFFFFFF).unwrap();
let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap();
Expand Down Expand Up @@ -281,6 +273,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(Scalar::from_i32(-1)) // Return non-zero on success
}

#[allow(non_snake_case, clippy::arithmetic_side_effects)]
fn system_time_since_windows_epoch(&self, time: &SystemTime) -> InterpResult<'tcx, Duration> {
let this = self.eval_context_ref();

let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC");
let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH");
let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC;

interp_ok(system_time_to_duration(time)? + Duration::from_secs(SECONDS_TO_UNIX_EPOCH))
}

#[allow(non_snake_case, clippy::arithmetic_side_effects)]
fn windows_ticks_for(&self, duration: Duration) -> InterpResult<'tcx, u64> {
let this = self.eval_context_ref();

let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC");
let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC");
let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC;

let ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL))
.map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?;
interp_ok(ticks)
}

fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_ref();

Expand Down
6 changes: 3 additions & 3 deletions src/tools/miri/src/shims/unix/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
throw_unsup_format!("unsupported flags {:#x}", op);
};

let result = fd.as_unix().flock(this.machine.communicate(), parsed_op)?;
let result = fd.as_unix(this).flock(this.machine.communicate(), parsed_op)?;
// return `0` if flock is successful
let result = result.map(|()| 0i32);
interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
Expand Down Expand Up @@ -273,7 +273,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let Ok(offset) = u64::try_from(offset) else {
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
};
fd.as_unix().pread(communicate, offset, buf, count, this, finish)?
fd.as_unix(this).pread(communicate, offset, buf, count, this, finish)?
}
};
interp_ok(())
Expand Down Expand Up @@ -333,7 +333,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let Ok(offset) = u64::try_from(offset) else {
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
};
fd.as_unix().pwrite(communicate, buf, count, offset, this, finish)?
fd.as_unix(this).pwrite(communicate, buf, count, offset, this, finish)?
}
};
interp_ok(())
Expand Down
8 changes: 8 additions & 0 deletions src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::{Conv, FnAbi};

use super::sync::EvalContextExt as _;
use crate::shims::unix::*;
use crate::*;

Expand Down Expand Up @@ -55,6 +56,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}

// Synchronization primitives
"_umtx_op" => {
let [obj, op, val, uaddr, uaddr2] =
this.check_shim(abi, Conv::C, link_name, args)?;
this._umtx_op(obj, op, val, uaddr, uaddr2, dest)?;
}

// File related shims
// For those, we both intercept `func` and `call@FBSD_1.0` symbols cases
// since freebsd 12 the former form can be expected.
Expand Down
1 change: 1 addition & 0 deletions src/tools/miri/src/shims/unix/freebsd/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod foreign_items;
pub mod sync;
Loading
Loading