Skip to content

Commit 2fcbadc

Browse files
authored
Merge branch 'master' into flock
2 parents 5979937 + 43ab65e commit 2fcbadc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+615
-202
lines changed

rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
9057c3ffec44926d5e149dc13ff3ce1613b69cce
1+
72d73cec61aa8f85901358cd5d386d5dd066fe52

src/bin/miri.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,14 @@ fn main() {
620620
"-Zmiri-unique-is-unique only has an effect when -Zmiri-tree-borrows is also used"
621621
);
622622
}
623+
// Tree Borrows + permissive provenance does not work.
624+
if miri_config.provenance_mode == ProvenanceMode::Permissive
625+
&& matches!(miri_config.borrow_tracker, Some(BorrowTrackerMethod::TreeBorrows))
626+
{
627+
show_error!(
628+
"Tree Borrows does not support integer-to-pointer casts, and is hence not compatible with permissive provenance"
629+
);
630+
}
623631

624632
debug!("rustc arguments: {:?}", rustc_args);
625633
debug!("crate arguments: {:?}", miri_config.args);

src/borrow_tracker/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ impl GlobalStateInner {
232232
pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_>) {
233233
self.root_ptr_tags.retain(|id, _| allocs.is_live(*id));
234234
}
235+
236+
pub fn borrow_tracker_method(&self) -> BorrowTrackerMethod {
237+
self.borrow_tracker_method
238+
}
235239
}
236240

237241
/// Which borrow tracking method to use

src/borrow_tracker/stacked_borrows/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod diagnostics;
55
mod item;
66
mod stack;
77

8+
use std::cell::RefCell;
89
use std::cmp;
910
use std::fmt::Write;
1011
use std::mem;
@@ -820,7 +821,19 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
820821
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/276.
821822
let size = match size {
822823
Some(size) => size,
823-
None => return Ok(place.clone()),
824+
None => {
825+
// The first time this happens, show a warning.
826+
thread_local! { static WARNING_SHOWN: RefCell<bool> = const { RefCell::new(false) }; }
827+
WARNING_SHOWN.with_borrow_mut(|shown| {
828+
if *shown {
829+
return;
830+
}
831+
// Not yet shown. Show it!
832+
*shown = true;
833+
this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow);
834+
});
835+
return Ok(place.clone());
836+
}
824837
};
825838

826839
// Compute new borrow.

src/diagnostics.rs

Lines changed: 79 additions & 62 deletions
Large diffs are not rendered by default.

src/machine.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ impl<'tcx> MiriMachine<'tcx> {
660660
tls: TlsData::default(),
661661
isolated_op: config.isolated_op,
662662
validate: config.validate,
663-
fds: shims::FdTable::new(config.mute_stdout_stderr),
663+
fds: shims::FdTable::init(config.mute_stdout_stderr),
664664
dirs: Default::default(),
665665
layouts,
666666
threads,

src/shims/env.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
108108
EnvVars::Windows(vars) => vars.get(name),
109109
}
110110
}
111+
112+
fn get_pid(&self) -> u32 {
113+
let this = self.eval_context_ref();
114+
if this.machine.communicate() { std::process::id() } else { 1000 }
115+
}
111116
}

src/shims/unix/env.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
274274
let this = self.eval_context_mut();
275275
this.assert_target_os_is_unix("getpid");
276276

277-
this.check_no_isolation("`getpid`")?;
278-
279277
// The reason we need to do this wacky of a conversion is because
280278
// `libc::getpid` returns an i32, however, `std::process::id()` return an u32.
281279
// So we un-do the conversion that stdlib does and turn it back into an i32.
282280
#[allow(clippy::cast_possible_wrap)]
283-
Ok(std::process::id() as i32)
281+
Ok(this.get_pid() as i32)
282+
}
283+
284+
fn linux_gettid(&mut self) -> InterpResult<'tcx, i32> {
285+
let this = self.eval_context_ref();
286+
this.assert_target_os("linux", "gettid");
287+
288+
let index = this.machine.threads.active_thread().to_u32();
289+
290+
// Compute a TID for this thread, ensuring that the main thread has PID == TID.
291+
let tid = this.get_pid().strict_add(index);
292+
293+
#[allow(clippy::cast_possible_wrap)]
294+
Ok(tid as i32)
284295
}
285296
}

src/shims/unix/fd.rs

Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,30 @@ pub trait FileDescription: std::fmt::Debug + Any {
3636
throw_unsup_format!("cannot write to {}", self.name());
3737
}
3838

39+
/// Reads as much as possible into the given buffer from a given offset,
40+
/// and returns the number of bytes read.
41+
fn pread<'tcx>(
42+
&mut self,
43+
_communicate_allowed: bool,
44+
_bytes: &mut [u8],
45+
_offset: u64,
46+
_ecx: &mut MiriInterpCx<'tcx>,
47+
) -> InterpResult<'tcx, io::Result<usize>> {
48+
throw_unsup_format!("cannot pread from {}", self.name());
49+
}
50+
51+
/// Writes as much as possible from the given buffer starting at a given offset,
52+
/// and returns the number of bytes written.
53+
fn pwrite<'tcx>(
54+
&mut self,
55+
_communicate_allowed: bool,
56+
_bytes: &[u8],
57+
_offset: u64,
58+
_ecx: &mut MiriInterpCx<'tcx>,
59+
) -> InterpResult<'tcx, io::Result<usize>> {
60+
throw_unsup_format!("cannot pwrite to {}", self.name());
61+
}
62+
3963
/// Seeks to the given offset (which can be relative to the beginning, end, or current position).
4064
/// Returns the new position from the start of the stream.
4165
fn seek<'tcx>(
@@ -176,10 +200,6 @@ impl FileDescription for NullOutput {
176200
pub struct FileDescriptor(Rc<RefCell<Box<dyn FileDescription>>>);
177201

178202
impl FileDescriptor {
179-
pub fn new<T: FileDescription>(fd: T) -> Self {
180-
FileDescriptor(Rc::new(RefCell::new(Box::new(fd))))
181-
}
182-
183203
pub fn borrow(&self) -> Ref<'_, dyn FileDescription> {
184204
Ref::map(self.0.borrow(), |fd| fd.as_ref())
185205
}
@@ -211,20 +231,25 @@ impl VisitProvenance for FdTable {
211231
}
212232

213233
impl FdTable {
214-
pub(crate) fn new(mute_stdout_stderr: bool) -> FdTable {
215-
let mut fds: BTreeMap<_, FileDescriptor> = BTreeMap::new();
216-
fds.insert(0i32, FileDescriptor::new(io::stdin()));
234+
fn new() -> Self {
235+
FdTable { fds: BTreeMap::new() }
236+
}
237+
pub(crate) fn init(mute_stdout_stderr: bool) -> FdTable {
238+
let mut fds = FdTable::new();
239+
fds.insert_fd(io::stdin());
217240
if mute_stdout_stderr {
218-
fds.insert(1i32, FileDescriptor::new(NullOutput));
219-
fds.insert(2i32, FileDescriptor::new(NullOutput));
241+
assert_eq!(fds.insert_fd(NullOutput), 1);
242+
assert_eq!(fds.insert_fd(NullOutput), 2);
220243
} else {
221-
fds.insert(1i32, FileDescriptor::new(io::stdout()));
222-
fds.insert(2i32, FileDescriptor::new(io::stderr()));
244+
assert_eq!(fds.insert_fd(io::stdout()), 1);
245+
assert_eq!(fds.insert_fd(io::stderr()), 2);
223246
}
224-
FdTable { fds }
247+
fds
225248
}
226249

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

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

425-
fn read(&mut self, fd: i32, buf: Pointer, count: u64) -> InterpResult<'tcx, i64> {
450+
/// Read data from `fd` into buffer specified by `buf` and `count`.
451+
///
452+
/// If `offset` is `None`, reads data from current cursor position associated with `fd`
453+
/// and updates cursor position on completion. Otherwise, reads from the specified offset
454+
/// and keeps the cursor unchanged.
455+
fn read(
456+
&mut self,
457+
fd: i32,
458+
buf: Pointer,
459+
count: u64,
460+
offset: Option<i128>,
461+
) -> InterpResult<'tcx, i64> {
426462
let this = self.eval_context_mut();
427463

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

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

448-
trace!("read: FD mapped to {:?}", file_descriptor);
484+
trace!("read: FD mapped to {fd:?}");
449485
// We want to read at most `count` bytes. We are sure that `count` is not negative
450486
// because it was a target's `usize`. Also we are sure that its smaller than
451487
// `usize::MAX` because it is bounded by the host's `isize`.
452488
let mut bytes = vec![0; usize::try_from(count).unwrap()];
453-
// `File::read` never returns a value larger than `count`,
454-
// so this cannot fail.
455-
let result = file_descriptor
456-
.borrow_mut()
457-
.read(communicate, &mut bytes, this)?
458-
.map(|c| i64::try_from(c).unwrap());
459-
drop(file_descriptor);
489+
let result = match offset {
490+
None => fd.borrow_mut().read(communicate, &mut bytes, this),
491+
Some(offset) => {
492+
let Ok(offset) = u64::try_from(offset) else {
493+
let einval = this.eval_libc("EINVAL");
494+
this.set_last_error(einval)?;
495+
return Ok(-1);
496+
};
497+
fd.borrow_mut().pread(communicate, &mut bytes, offset, this)
498+
}
499+
};
500+
drop(fd);
460501

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

479-
fn write(&mut self, fd: i32, buf: Pointer, count: u64) -> InterpResult<'tcx, i64> {
521+
fn write(
522+
&mut self,
523+
fd: i32,
524+
buf: Pointer,
525+
count: u64,
526+
offset: Option<i128>,
527+
) -> InterpResult<'tcx, i64> {
480528
let this = self.eval_context_mut();
481529

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

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

500-
let result = file_descriptor
501-
.borrow_mut()
502-
.write(communicate, &bytes, this)?
503-
.map(|c| i64::try_from(c).unwrap());
504-
drop(file_descriptor);
548+
let result = match offset {
549+
None => fd.borrow_mut().write(communicate, &bytes, this),
550+
Some(offset) => {
551+
let Ok(offset) = u64::try_from(offset) else {
552+
let einval = this.eval_libc("EINVAL");
553+
this.set_last_error(einval)?;
554+
return Ok(-1);
555+
};
556+
fd.borrow_mut().pwrite(communicate, &bytes, offset, this)
557+
}
558+
};
559+
drop(fd);
505560

561+
let result = result?.map(|c| i64::try_from(c).unwrap());
506562
this.try_unwrap_io_result(result)
507563
}
508564
}

src/shims/unix/foreign_items.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
9292
let fd = this.read_scalar(fd)?.to_i32()?;
9393
let buf = this.read_pointer(buf)?;
9494
let count = this.read_target_usize(count)?;
95-
let result = this.read(fd, buf, count)?;
95+
let result = this.read(fd, buf, count, None)?;
9696
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
9797
}
9898
"write" => {
@@ -101,7 +101,47 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
101101
let buf = this.read_pointer(buf)?;
102102
let count = this.read_target_usize(n)?;
103103
trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
104-
let result = this.write(fd, buf, count)?;
104+
let result = this.write(fd, buf, count, None)?;
105+
// Now, `result` is the value we return back to the program.
106+
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
107+
}
108+
"pread" => {
109+
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
110+
let fd = this.read_scalar(fd)?.to_i32()?;
111+
let buf = this.read_pointer(buf)?;
112+
let count = this.read_target_usize(count)?;
113+
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
114+
let result = this.read(fd, buf, count, Some(offset))?;
115+
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
116+
}
117+
"pwrite" => {
118+
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
119+
let fd = this.read_scalar(fd)?.to_i32()?;
120+
let buf = this.read_pointer(buf)?;
121+
let count = this.read_target_usize(n)?;
122+
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
123+
trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
124+
let result = this.write(fd, buf, count, Some(offset))?;
125+
// Now, `result` is the value we return back to the program.
126+
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
127+
}
128+
"pread64" => {
129+
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
130+
let fd = this.read_scalar(fd)?.to_i32()?;
131+
let buf = this.read_pointer(buf)?;
132+
let count = this.read_target_usize(count)?;
133+
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
134+
let result = this.read(fd, buf, count, Some(offset))?;
135+
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
136+
}
137+
"pwrite64" => {
138+
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
139+
let fd = this.read_scalar(fd)?.to_i32()?;
140+
let buf = this.read_pointer(buf)?;
141+
let count = this.read_target_usize(n)?;
142+
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
143+
trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
144+
let result = this.write(fd, buf, count, Some(offset))?;
105145
// Now, `result` is the value we return back to the program.
106146
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
107147
}

0 commit comments

Comments
 (0)