Skip to content

Commit

Permalink
Merge branch 'master' into flock
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov authored Jul 26, 2024
2 parents 5979937 + 43ab65e commit 2fcbadc
Show file tree
Hide file tree
Showing 44 changed files with 615 additions and 202 deletions.
2 changes: 1 addition & 1 deletion rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
9057c3ffec44926d5e149dc13ff3ce1613b69cce
72d73cec61aa8f85901358cd5d386d5dd066fe52
8 changes: 8 additions & 0 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,14 @@ fn main() {
"-Zmiri-unique-is-unique only has an effect when -Zmiri-tree-borrows is also used"
);
}
// Tree Borrows + permissive provenance does not work.
if miri_config.provenance_mode == ProvenanceMode::Permissive
&& matches!(miri_config.borrow_tracker, Some(BorrowTrackerMethod::TreeBorrows))
{
show_error!(
"Tree Borrows does not support integer-to-pointer casts, and is hence not compatible with permissive provenance"
);
}

debug!("rustc arguments: {:?}", rustc_args);
debug!("crate arguments: {:?}", miri_config.args);
Expand Down
4 changes: 4 additions & 0 deletions src/borrow_tracker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ impl GlobalStateInner {
pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_>) {
self.root_ptr_tags.retain(|id, _| allocs.is_live(*id));
}

pub fn borrow_tracker_method(&self) -> BorrowTrackerMethod {
self.borrow_tracker_method
}
}

/// Which borrow tracking method to use
Expand Down
15 changes: 14 additions & 1 deletion src/borrow_tracker/stacked_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod diagnostics;
mod item;
mod stack;

use std::cell::RefCell;
use std::cmp;
use std::fmt::Write;
use std::mem;
Expand Down Expand Up @@ -820,7 +821,19 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/276.
let size = match size {
Some(size) => size,
None => return Ok(place.clone()),
None => {
// The first time this happens, show a warning.
thread_local! { static WARNING_SHOWN: RefCell<bool> = const { RefCell::new(false) }; }
WARNING_SHOWN.with_borrow_mut(|shown| {
if *shown {
return;
}
// Not yet shown. Show it!
*shown = true;
this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow);
});
return Ok(place.clone());
}
};

// Compute new borrow.
Expand Down
141 changes: 79 additions & 62 deletions src/diagnostics.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ impl<'tcx> MiriMachine<'tcx> {
tls: TlsData::default(),
isolated_op: config.isolated_op,
validate: config.validate,
fds: shims::FdTable::new(config.mute_stdout_stderr),
fds: shims::FdTable::init(config.mute_stdout_stderr),
dirs: Default::default(),
layouts,
threads,
Expand Down
5 changes: 5 additions & 0 deletions src/shims/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
EnvVars::Windows(vars) => vars.get(name),
}
}

fn get_pid(&self) -> u32 {
let this = self.eval_context_ref();
if this.machine.communicate() { std::process::id() } else { 1000 }
}
}
17 changes: 14 additions & 3 deletions src/shims/unix/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,12 +274,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("getpid");

this.check_no_isolation("`getpid`")?;

// The reason we need to do this wacky of a conversion is because
// `libc::getpid` returns an i32, however, `std::process::id()` return an u32.
// So we un-do the conversion that stdlib does and turn it back into an i32.
#[allow(clippy::cast_possible_wrap)]
Ok(std::process::id() as i32)
Ok(this.get_pid() as i32)
}

fn linux_gettid(&mut self) -> InterpResult<'tcx, i32> {
let this = self.eval_context_ref();
this.assert_target_os("linux", "gettid");

let index = this.machine.threads.active_thread().to_u32();

// Compute a TID for this thread, ensuring that the main thread has PID == TID.
let tid = this.get_pid().strict_add(index);

#[allow(clippy::cast_possible_wrap)]
Ok(tid as i32)
}
}
118 changes: 87 additions & 31 deletions src/shims/unix/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,30 @@ pub trait FileDescription: std::fmt::Debug + Any {
throw_unsup_format!("cannot write to {}", self.name());
}

/// Reads as much as possible into the given buffer from a given offset,
/// and returns the number of bytes read.
fn pread<'tcx>(
&mut self,
_communicate_allowed: bool,
_bytes: &mut [u8],
_offset: u64,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
throw_unsup_format!("cannot pread from {}", self.name());
}

/// Writes as much as possible from the given buffer starting at a given offset,
/// and returns the number of bytes written.
fn pwrite<'tcx>(
&mut self,
_communicate_allowed: bool,
_bytes: &[u8],
_offset: u64,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
throw_unsup_format!("cannot pwrite to {}", self.name());
}

/// Seeks to the given offset (which can be relative to the beginning, end, or current position).
/// Returns the new position from the start of the stream.
fn seek<'tcx>(
Expand Down Expand Up @@ -176,10 +200,6 @@ impl FileDescription for NullOutput {
pub struct FileDescriptor(Rc<RefCell<Box<dyn FileDescription>>>);

impl FileDescriptor {
pub fn new<T: FileDescription>(fd: T) -> Self {
FileDescriptor(Rc::new(RefCell::new(Box::new(fd))))
}

pub fn borrow(&self) -> Ref<'_, dyn FileDescription> {
Ref::map(self.0.borrow(), |fd| fd.as_ref())
}
Expand Down Expand Up @@ -211,20 +231,25 @@ impl VisitProvenance for FdTable {
}

impl FdTable {
pub(crate) fn new(mute_stdout_stderr: bool) -> FdTable {
let mut fds: BTreeMap<_, FileDescriptor> = BTreeMap::new();
fds.insert(0i32, FileDescriptor::new(io::stdin()));
fn new() -> Self {
FdTable { fds: BTreeMap::new() }
}
pub(crate) fn init(mute_stdout_stderr: bool) -> FdTable {
let mut fds = FdTable::new();
fds.insert_fd(io::stdin());
if mute_stdout_stderr {
fds.insert(1i32, FileDescriptor::new(NullOutput));
fds.insert(2i32, FileDescriptor::new(NullOutput));
assert_eq!(fds.insert_fd(NullOutput), 1);
assert_eq!(fds.insert_fd(NullOutput), 2);
} else {
fds.insert(1i32, FileDescriptor::new(io::stdout()));
fds.insert(2i32, FileDescriptor::new(io::stderr()));
assert_eq!(fds.insert_fd(io::stdout()), 1);
assert_eq!(fds.insert_fd(io::stderr()), 2);
}
FdTable { fds }
fds
}

pub fn insert_fd(&mut self, file_handle: FileDescriptor) -> i32 {
/// Insert a file descriptor to the FdTable.
pub fn insert_fd<T: FileDescription>(&mut self, fd: T) -> i32 {
let file_handle = FileDescriptor(Rc::new(RefCell::new(Box::new(fd))));
self.insert_fd_with_min_fd(file_handle, 0)
}

Expand Down Expand Up @@ -422,7 +447,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok((-1).into())
}

fn read(&mut self, fd: i32, buf: Pointer, count: u64) -> InterpResult<'tcx, i64> {
/// Read data from `fd` into buffer specified by `buf` and `count`.
///
/// If `offset` is `None`, reads data from current cursor position associated with `fd`
/// and updates cursor position on completion. Otherwise, reads from the specified offset
/// and keeps the cursor unchanged.
fn read(
&mut self,
fd: i32,
buf: Pointer,
count: u64,
offset: Option<i128>,
) -> InterpResult<'tcx, i64> {
let this = self.eval_context_mut();

// Isolation check is done via `FileDescriptor` trait.
Expand All @@ -440,25 +476,31 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let communicate = this.machine.communicate();

// We temporarily dup the FD to be able to retain mutable access to `this`.
let Some(file_descriptor) = this.machine.fds.dup(fd) else {
let Some(fd) = this.machine.fds.dup(fd) else {
trace!("read: FD not found");
return this.fd_not_found();
};

trace!("read: FD mapped to {:?}", file_descriptor);
trace!("read: FD mapped to {fd:?}");
// We want to read at most `count` bytes. We are sure that `count` is not negative
// because it was a target's `usize`. Also we are sure that its smaller than
// `usize::MAX` because it is bounded by the host's `isize`.
let mut bytes = vec![0; usize::try_from(count).unwrap()];
// `File::read` never returns a value larger than `count`,
// so this cannot fail.
let result = file_descriptor
.borrow_mut()
.read(communicate, &mut bytes, this)?
.map(|c| i64::try_from(c).unwrap());
drop(file_descriptor);
let result = match offset {
None => fd.borrow_mut().read(communicate, &mut bytes, this),
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
return Ok(-1);
};
fd.borrow_mut().pread(communicate, &mut bytes, offset, this)
}
};
drop(fd);

match result {
// `File::read` never returns a value larger than `count`, so this cannot fail.
match result?.map(|c| i64::try_from(c).unwrap()) {
Ok(read_bytes) => {
// If reading to `bytes` did not fail, we write those bytes to the buffer.
// Crucially, if fewer than `bytes.len()` bytes were read, only write
Expand All @@ -476,7 +518,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}

fn write(&mut self, fd: i32, buf: Pointer, count: u64) -> InterpResult<'tcx, i64> {
fn write(
&mut self,
fd: i32,
buf: Pointer,
count: u64,
offset: Option<i128>,
) -> InterpResult<'tcx, i64> {
let this = self.eval_context_mut();

// Isolation check is done via `FileDescriptor` trait.
Expand All @@ -493,16 +541,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
// We temporarily dup the FD to be able to retain mutable access to `this`.
let Some(file_descriptor) = this.machine.fds.dup(fd) else {
let Some(fd) = this.machine.fds.dup(fd) else {
return this.fd_not_found();
};

let result = file_descriptor
.borrow_mut()
.write(communicate, &bytes, this)?
.map(|c| i64::try_from(c).unwrap());
drop(file_descriptor);
let result = match offset {
None => fd.borrow_mut().write(communicate, &bytes, this),
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
return Ok(-1);
};
fd.borrow_mut().pwrite(communicate, &bytes, offset, this)
}
};
drop(fd);

let result = result?.map(|c| i64::try_from(c).unwrap());
this.try_unwrap_io_result(result)
}
}
Expand Down
44 changes: 42 additions & 2 deletions src/shims/unix/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let result = this.read(fd, buf, count)?;
let result = this.read(fd, buf, count, None)?;
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"write" => {
Expand All @@ -101,7 +101,47 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
let result = this.write(fd, buf, count)?;
let result = this.write(fd, buf, count, None)?;
// Now, `result` is the value we return back to the program.
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"pread" => {
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
let result = this.read(fd, buf, count, Some(offset))?;
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"pwrite" => {
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
let result = this.write(fd, buf, count, Some(offset))?;
// Now, `result` is the value we return back to the program.
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"pread64" => {
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
let result = this.read(fd, buf, count, Some(offset))?;
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"pwrite64" => {
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
let result = this.write(fd, buf, count, Some(offset))?;
// Now, `result` is the value we return back to the program.
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
Expand Down
Loading

0 comments on commit 2fcbadc

Please sign in to comment.