Auto merge of #120451 - RalfJung:miri, r=RalfJung

Miri subtree update

r? `@ghost`
This commit is contained in:
bors 2024-01-29 06:50:18 +00:00
commit 0ea334ab73
17 changed files with 235 additions and 172 deletions

View file

@ -1,8 +1,8 @@
name: Tier 2 sysroots
on: push
# schedule:
# - cron: '44 4 * * *' # At 4:44 UTC every day.
on:
schedule:
- cron: '44 4 * * *' # At 4:44 UTC every day.
defaults:
run:

View file

@ -486,9 +486,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "measureme"
version = "10.1.2"
version = "11.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e381dcdad44c3c435f8052b08c5c4a1449c48ab56f312345eae12d7a693dbe"
checksum = "dfa4a40f09af7aa6faef38285402a78847d0d72bf8827006cd2a332e1e6e4a8d"
dependencies = [
"log",
"memmap2",

View file

@ -590,6 +590,7 @@ Definite bugs found:
* [Incorrect use of `compare_exchange_weak` in `once_cell`](https://github.com/matklad/once_cell/issues/186)
* [Dropping with unaligned pointers in `vec::IntoIter`](https://github.com/rust-lang/rust/pull/106084)
* [Deallocating with the wrong layout in new specializations for in-place `Iterator::collect`](https://github.com/rust-lang/rust/pull/118460)
* [Incorrect offset computation for highly-aligned types in `portable-atomic-util`](https://github.com/taiki-e/portable-atomic/pull/138)
Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment):

View file

@ -501,11 +501,10 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
// Set missing env vars. We prefer build-time env vars over run-time ones; see
// <https://github.com/rust-lang/miri/issues/1661> for the kind of issue that fixes.
for (name, val) in info.env {
// `CARGO_MAKEFLAGS` contains information about how to reach the
// jobserver, but by the time the program is being run, that jobserver
// no longer exists. Hence we shouldn't forward this.
// FIXME: Miri builds the final crate without a jobserver.
// This may be fixed with github.com/rust-lang/cargo/issues/12597.
// `CARGO_MAKEFLAGS` contains information about how to reach the jobserver, but by the time
// the program is being run, that jobserver no longer exists (cargo only runs the jobserver
// for the build portion of `cargo run`/`cargo test`). Hence we shouldn't forward this.
// Also see <https://github.com/rust-lang/rust/pull/113730>.
if name == "CARGO_MAKEFLAGS" {
continue;
}

View file

@ -1 +1 @@
5bcd86d89b2b7b6a490f7e075dd4eb346deb5f98
dd2559e08e1530806740931037d6bb83ef956161

View file

@ -217,7 +217,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
/// Helper function to get a `windows` constant as a `Scalar`.
fn eval_windows(&self, module: &str, name: &str) -> Scalar<Provenance> {
self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal","windows", module, name])
self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
}
/// Helper function to get a `windows` constant as a `u32`.
@ -249,7 +249,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
let this = self.eval_context_ref();
let ty = this
.resolve_path(&["std", "sys", "pal","windows", "c", name], Namespace::TypeNS)
.resolve_path(&["std", "sys", "pal", "windows", "c", name], Namespace::TypeNS)
.ty(*this.tcx, ty::ParamEnv::reveal_all());
this.layout_of(ty).unwrap()
}
@ -270,6 +270,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
bug!("No field named {} in type {}", name, base.layout().ty);
}
/// Search if `base` (which must be a struct or union type) contains the `name` field.
fn projectable_has_field<P: Projectable<'tcx, Provenance>>(
&self,
base: &P,
name: &str,
) -> bool {
let adt = base.layout().ty.ty_adt_def().unwrap();
for field in adt.non_enum_variant().fields.iter() {
if field.name.as_str() == name {
return true;
}
}
false
}
/// Write an int of the appropriate size to `dest`. The target type may be signed or unsigned,
/// we try to do the right thing anyway. `i128` can fit all integer types except for `u128` so
/// this method is fine for almost all integer types.

View file

@ -94,7 +94,6 @@ pub use crate::shims::os_str::EvalContextExt as _;
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
pub use crate::shims::time::EvalContextExt as _;
pub use crate::shims::tls::TlsData;
pub use crate::shims::EvalContextExt as _;
pub use crate::borrow_tracker::stacked_borrows::{
EvalContextExt as _, Item, Permission, Stack, Stacks,

View file

@ -10,8 +10,8 @@ use std::process;
use either::Either;
use rand::rngs::StdRng;
use rand::SeedableRng;
use rand::Rng;
use rand::SeedableRng;
use rustc_ast::ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@ -83,7 +83,8 @@ pub struct FrameExtra<'tcx> {
impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Omitting `timing`, it does not support `Debug`.
let FrameExtra { borrow_tracker, catch_unwind, timing: _, is_user_relevant: _, salt: _ } = self;
let FrameExtra { borrow_tracker, catch_unwind, timing: _, is_user_relevant: _, salt: _ } =
self;
f.debug_struct("FrameData")
.field("borrow_tracker", borrow_tracker)
.field("catch_unwind", catch_unwind)
@ -93,7 +94,8 @@ impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
impl VisitProvenance for FrameExtra<'_> {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
let FrameExtra { catch_unwind, borrow_tracker, timing: _, is_user_relevant: _, salt: _ } = self;
let FrameExtra { catch_unwind, borrow_tracker, timing: _, is_user_relevant: _, salt: _ } =
self;
catch_unwind.visit_provenance(visit);
borrow_tracker.visit_provenance(visit);
@ -710,7 +712,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
Ok(())
}
fn add_extern_static(
pub(crate) fn add_extern_static(
this: &mut MiriInterpCx<'mir, 'tcx>,
name: &str,
ptr: Pointer<Option<Provenance>>,
@ -720,75 +722,6 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
this.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
}
fn alloc_extern_static(
this: &mut MiriInterpCx<'mir, 'tcx>,
name: &str,
val: ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx> {
let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
this.write_immediate(*val, &place)?;
Self::add_extern_static(this, name, place.ptr());
Ok(())
}
/// Sets up the "extern statics" for this machine.
fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
// "__rust_no_alloc_shim_is_unstable"
let val = ImmTy::from_int(0, this.machine.layouts.u8);
Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?;
match this.tcx.sess.target.os.as_ref() {
"linux" => {
// "environ"
Self::add_extern_static(
this,
"environ",
this.machine.env_vars.environ.as_ref().unwrap().ptr(),
);
// A couple zero-initialized pointer-sized extern statics.
// Most of them are for weak symbols, which we all set to null (indicating that the
// symbol is not supported, and triggering fallback code which ends up calling a
// syscall that we do support).
for name in &["__cxa_thread_atexit_impl", "getrandom", "statx", "__clock_gettime64"]
{
let val = ImmTy::from_int(0, this.machine.layouts.usize);
Self::alloc_extern_static(this, name, val)?;
}
}
"freebsd" => {
// "environ"
Self::add_extern_static(
this,
"environ",
this.machine.env_vars.environ.as_ref().unwrap().ptr(),
);
}
"android" => {
// "signal" -- just needs a non-zero pointer value (function does not even get called),
// but we arrange for this to be callable anyway (it will then do nothing).
let layout = this.machine.layouts.const_raw_ptr;
let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str("signal")));
let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout);
Self::alloc_extern_static(this, "signal", val)?;
// A couple zero-initialized pointer-sized extern statics.
// Most of them are for weak symbols, which we all set to null (indicating that the
// symbol is not supported, and triggering fallback code.)
for name in &["bsd_signal"] {
let val = ImmTy::from_int(0, this.machine.layouts.usize);
Self::alloc_extern_static(this, name, val)?;
}
}
"windows" => {
// "_tls_used"
// This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
let val = ImmTy::from_int(0, this.machine.layouts.u8);
Self::alloc_extern_static(this, "_tls_used", val)?;
}
_ => {} // No "extern statics" supported on this target
}
Ok(())
}
pub(crate) fn communicate(&self) -> bool {
self.isolated_op == IsolatedOp::Allow
}
@ -1009,7 +942,21 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
ret: Option<mir::BasicBlock>,
unwind: mir::UnwindAction,
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
ecx.find_mir_or_eval_fn(instance, abi, args, dest, ret, unwind)
// For foreign items, try to see if we can emulate them.
if ecx.tcx.is_foreign_item(instance.def_id()) {
// An external function call that does not have a MIR body. We either find MIR elsewhere
// or emulate its effect.
// This will be Ok(None) if we're emulating the intrinsic entirely within Miri (no need
// to run extra MIR), and Ok(Some(body)) if we found MIR to run for the
// foreign function
// Any needed call to `goto_block` will be performed by `emulate_foreign_item`.
let args = ecx.copy_fn_args(args)?; // FIXME: Should `InPlace` arguments be reset to uninit?
let link_name = ecx.item_link_name(instance.def_id());
return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind);
}
// Otherwise, load the MIR.
Ok(Some((ecx.load_mir(instance.def, None)?, instance)))
}
#[inline(always)]

View file

@ -24,7 +24,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
Ok(match bin_op {
Eq | Ne | Lt | Le | Gt | Ge => {
assert_eq!(left.layout.abi, right.layout.abi); // types an differ, e.g. fn ptrs with different `for`
assert_eq!(left.layout.abi, right.layout.abi); // types can differ, e.g. fn ptrs with different `for`
let size = this.pointer_size();
// Just compare the bits. ScalarPairs are compared lexicographically.
// We thus always compare pairs and simply fill scalars up with 0.

View file

@ -0,0 +1,79 @@
//! Provides the `extern static` that this platform expects.
use crate::*;
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
fn alloc_extern_static(
this: &mut MiriInterpCx<'mir, 'tcx>,
name: &str,
val: ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx> {
let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
this.write_immediate(*val, &place)?;
Self::add_extern_static(this, name, place.ptr());
Ok(())
}
/// Zero-initialized pointer-sized extern statics are pretty common.
/// Most of them are for weak symbols, which we all set to null (indicating that the
/// symbol is not supported, and triggering fallback code which ends up calling a
/// syscall that we do support).
fn null_ptr_extern_statics(
this: &mut MiriInterpCx<'mir, 'tcx>,
names: &[&str],
) -> InterpResult<'tcx> {
for name in names {
let val = ImmTy::from_int(0, this.machine.layouts.usize);
Self::alloc_extern_static(this, name, val)?;
}
Ok(())
}
/// Sets up the "extern statics" for this machine.
pub fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
// "__rust_no_alloc_shim_is_unstable"
let val = ImmTy::from_int(0, this.machine.layouts.u8);
Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?;
match this.tcx.sess.target.os.as_ref() {
"linux" => {
Self::null_ptr_extern_statics(
this,
&["__cxa_thread_atexit_impl", "getrandom", "statx", "__clock_gettime64"],
)?;
// "environ"
Self::add_extern_static(
this,
"environ",
this.machine.env_vars.environ.as_ref().unwrap().ptr(),
);
}
"freebsd" => {
Self::null_ptr_extern_statics(this, &["__cxa_thread_atexit_impl"])?;
// "environ"
Self::add_extern_static(
this,
"environ",
this.machine.env_vars.environ.as_ref().unwrap().ptr(),
);
}
"android" => {
Self::null_ptr_extern_statics(this, &["bsd_signal"])?;
// "signal" -- just needs a non-zero pointer value (function does not even get called),
// but we arrange for this to call the `signal` function anyway.
let layout = this.machine.layouts.const_raw_ptr;
let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str("signal")));
let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout);
Self::alloc_extern_static(this, "signal", val)?;
}
"windows" => {
// "_tls_used"
// This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
let val = ImmTy::from_int(0, this.machine.layouts.u8);
Self::alloc_extern_static(this, "_tls_used", val)?;
}
_ => {} // No "extern statics" supported on this target
}
Ok(())
}
}

View file

@ -148,7 +148,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// ```
// Would not be considered UB, or the other way around (`is_val_statically_known(0)`).
"is_val_statically_known" => {
let [_] = check_arg_count(args)?;
let [arg] = check_arg_count(args)?;
this.validate_operand(arg)?;
let branch: bool = this.machine.rng.get_mut().gen();
this.write_scalar(Scalar::from_bool(branch), dest)?;
}

View file

@ -10,48 +10,8 @@ pub mod windows;
mod x86;
pub mod env;
pub mod extern_static;
pub mod os_str;
pub mod panic;
pub mod time;
pub mod tls;
// End module management, begin local code
use log::trace;
use rustc_middle::{mir, ty};
use rustc_target::spec::abi::Abi;
use crate::*;
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn find_mir_or_eval_fn(
&mut self,
instance: ty::Instance<'tcx>,
abi: Abi,
args: &[FnArg<'tcx, Provenance>],
dest: &PlaceTy<'tcx, Provenance>,
ret: Option<mir::BasicBlock>,
unwind: mir::UnwindAction,
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
let this = self.eval_context_mut();
trace!("eval_fn_call: {:#?}, {:?}", instance, dest);
// For foreign items, try to see if we can emulate them.
if this.tcx.is_foreign_item(instance.def_id()) {
// An external function call that does not have a MIR body. We either find MIR elsewhere
// or emulate its effect.
// This will be Ok(None) if we're emulating the intrinsic entirely within Miri (no need
// to run extra MIR), and Ok(Some(body)) if we found MIR to run for the
// foreign function
// Any needed call to `goto_block` will be performed by `emulate_foreign_item`.
let args = this.copy_fn_args(args)?; // FIXME: Should `InPlace` arguments be reset to uninit?
let link_name = this.item_link_name(instance.def_id());
return this.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind);
}
// Otherwise, load the MIR.
Ok(Some((this.load_mir(instance.def, None)?, instance)))
}
}

View file

@ -155,6 +155,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
"lseek64" => {
let [fd, offset, whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let offset = this.read_scalar(offset)?.to_i64()?;
let whence = this.read_scalar(whence)?.to_i32()?;
let result = this.lseek64(fd, offset.into(), whence)?;
this.write_scalar(result, dest)?;
}
"lseek" => {
let [fd, offset, whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
let whence = this.read_scalar(whence)?.to_i32()?;
let result = this.lseek64(fd, offset, whence)?;
this.write_scalar(result, dest)?;
}

View file

@ -3,6 +3,7 @@ use rustc_target::spec::abi::Abi;
use crate::*;
use shims::foreign_items::EmulateForeignItemResult;
use shims::unix::fs::EvalContextExt as _;
use shims::unix::thread::EvalContextExt as _;
pub fn is_dyn_sym(_name: &str) -> bool {
@ -63,6 +64,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.write_scalar(Scalar::from_target_usize(len, this), 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.
"stat" | "stat@FBSD_1.0" => {
let [path, buf] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.macos_fbsd_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat@FBSD_1.0" => {
let [path, buf] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.macos_fbsd_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat@FBSD_1.0" => {
let [fd, buf] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.macos_fbsd_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"readdir_r" | "readdir_r@FBSD_1.0" => {
let [dirp, entry, result] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
this.write_scalar(result, dest)?;
}
// errno
"__error" => {
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

View file

@ -813,24 +813,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn lseek64(
&mut self,
fd_op: &OpTy<'tcx, Provenance>,
offset_op: &OpTy<'tcx, Provenance>,
whence_op: &OpTy<'tcx, Provenance>,
fd: i32,
offset: i128,
whence: i32,
) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescriptor` trait.
let fd = this.read_scalar(fd_op)?.to_i32()?;
let offset = this.read_scalar(offset_op)?.to_i64()?;
let whence = this.read_scalar(whence_op)?.to_i32()?;
let seek_from = if whence == this.eval_libc_i32("SEEK_SET") {
SeekFrom::Start(u64::try_from(offset).unwrap())
} else if whence == this.eval_libc_i32("SEEK_CUR") {
SeekFrom::Current(offset)
SeekFrom::Current(i64::try_from(offset).unwrap())
} else if whence == this.eval_libc_i32("SEEK_END") {
SeekFrom::End(offset)
SeekFrom::End(i64::try_from(offset).unwrap())
} else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
@ -897,13 +893,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.try_unwrap_io_result(result)
}
fn macos_stat(
fn macos_fbsd_stat(
&mut self,
path_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut();
this.assert_target_os("macos", "stat");
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
panic!("`macos_fbsd_stat` should not be called on {}", this.tcx.sess.target.os);
}
let path_scalar = this.read_pointer(path_op)?;
let path = this.read_path_from_c_str(path_scalar)?.into_owned();
@ -926,13 +925,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
// `lstat` is used to get symlink metadata.
fn macos_lstat(
fn macos_fbsd_lstat(
&mut self,
path_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut();
this.assert_target_os("macos", "lstat");
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
panic!("`macos_fbsd_lstat` should not be called on {}", this.tcx.sess.target.os);
}
let path_scalar = this.read_pointer(path_op)?;
let path = this.read_path_from_c_str(path_scalar)?.into_owned();
@ -953,14 +955,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
Ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
}
fn macos_fstat(
fn macos_fbsd_fstat(
&mut self,
fd_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut();
this.assert_target_os("macos", "fstat");
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
panic!("`macos_fbsd_fstat` should not be called on {}", this.tcx.sess.target.os);
}
let fd = this.read_scalar(fd_op)?.to_i32()?;
@ -1199,7 +1203,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut();
#[cfg_attr(not(unix), allow(unused_variables))]
let mode = if this.tcx.sess.target.os == "macos" {
let mode = if matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
u32::from(this.read_scalar(mode_op)?.to_u16()?)
} else {
this.read_scalar(mode_op)?.to_u32()?
@ -1371,7 +1375,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
Ok(Scalar::from_maybe_pointer(entry, this))
}
fn macos_readdir_r(
fn macos_fbsd_readdir_r(
&mut self,
dirp_op: &OpTy<'tcx, Provenance>,
entry_op: &OpTy<'tcx, Provenance>,
@ -1379,7 +1383,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut();
this.assert_target_os("macos", "readdir_r");
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
panic!("`macos_fbsd_readdir_r` should not be called on {}", this.tcx.sess.target.os);
}
let dirp = this.read_target_usize(dirp_op)?;
@ -1410,7 +1416,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// }
let entry_place = this.deref_pointer_as(entry_op, this.libc_ty_layout("dirent"))?;
let name_place = this.project_field(&entry_place, 5)?;
let name_place = this.project_field_named(&entry_place, "d_name")?;
let file_name = dir_entry.file_name(); // not a Path as there are no separators!
let (name_fits, file_name_buf_len) = this.write_os_str_to_c_str(
@ -1434,16 +1440,41 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
this.write_int_fields_named(
&[
("d_ino", ino.into()),
("d_seekoff", 0),
("d_reclen", 0),
("d_namlen", file_name_len.into()),
("d_type", file_type.into()),
],
&entry_place,
)?;
// macOS offset field is d_seekoff
if this.projectable_has_field(&entry_place, "d_seekoff") {
this.write_int_fields_named(
&[
("d_ino", ino.into()),
("d_seekoff", 0),
("d_reclen", 0),
("d_namlen", file_name_len.into()),
("d_type", file_type.into()),
],
&entry_place,
)?;
} else if this.projectable_has_field(&entry_place, "d_off") {
// freebsd 12 and onwards had added the d_off field
this.write_int_fields_named(
&[
("d_fileno", ino.into()),
("d_off", 0),
("d_reclen", 0),
("d_type", file_type.into()),
("d_namlen", file_name_len.into()),
],
&entry_place,
)?;
} else {
this.write_int_fields_named(
&[
("d_fileno", ino.into()),
("d_reclen", 0),
("d_type", file_type.into()),
("d_namlen", file_name_len.into()),
],
&entry_place,
)?;
}
let result_place = this.deref_pointer(result_op)?;
this.write_scalar(this.read_scalar(entry_op)?, &result_place)?;

View file

@ -40,18 +40,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
"stat" | "stat64" | "stat$INODE64" => {
let [path, buf] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.macos_stat(path, buf)?;
let result = this.macos_fbsd_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat64" | "lstat$INODE64" => {
let [path, buf] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.macos_lstat(path, buf)?;
let result = this.macos_fbsd_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat64" | "fstat$INODE64" => {
let [fd, buf] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.macos_fstat(fd, buf)?;
let result = this.macos_fbsd_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"opendir$INODE64" => {
@ -62,14 +62,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
"readdir_r" | "readdir_r$INODE64" => {
let [dirp, entry, result] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.macos_readdir_r(dirp, entry, result)?;
this.write_scalar(result, dest)?;
}
"lseek" => {
let [fd, offset, whence] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
// macOS is 64bit-only, so this is lseek64
let result = this.lseek64(fd, offset, whence)?;
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
this.write_scalar(result, dest)?;
}
"realpath$DARWIN_EXTSN" => {

View file

@ -1,5 +1,4 @@
//@ignore-target-windows: no libc on Windows
//@ignore-target-freebsd: FIXME needs foreign function `stat@FBSD_1.0`
//@compile-flags: -Zmiri-isolation-error=warn-nobacktrace
//@normalize-stderr-test: "(stat(x)?)" -> "$$STAT"