about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/miri/src/helpers.rs15
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs11
-rw-r--r--src/tools/miri/src/shims/unix/freebsd/foreign_items.rs28
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs89
-rw-r--r--src/tools/miri/src/shims/unix/macos/foreign_items.rs15
-rw-r--r--src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs1
6 files changed, 118 insertions, 41 deletions
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 21f1d684924..3651db982b3 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -225,6 +225,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         bug!("No field named {} in type {}", name, base.layout().ty);
     }
 
+    /// Search if Project (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.
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 23342c8045e..35036ce078d 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -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)?;
             }
diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
index 7c843e106ea..64094ac307d 100644
--- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
@@ -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)?;
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index ba40a1b3c32..b2170c8c87f 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -827,24 +827,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)?;
@@ -911,13 +907,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();
@@ -940,13 +939,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();
@@ -967,14 +969,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()?;
 
@@ -1213,7 +1217,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()?
@@ -1385,7 +1389,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>,
@@ -1393,7 +1397,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)?;
 
@@ -1424,7 +1430,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(
@@ -1448,16 +1454,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,
+                    )?;
+                    // freebsd 12 and onwards had added the d_off field
+                } else if this.projectable_has_field(&entry_place, "d_off") {
+                    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)?;
diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
index 07e19cadd6e..ecc07e28a29 100644
--- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
@@ -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" => {
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
index adfece58661..5185db0b0e2 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
+++ b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
@@ -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"