From a04ac722195e711800b6347712192eafe1de1979 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 20 Nov 2023 20:01:10 +0000 Subject: [PATCH] freebsd add *stat calls interception support --- ci.sh | 3 +- src/shims/unix/foreign_items.rs | 11 +++ src/shims/unix/freebsd/foreign_items.rs | 26 +++++++ src/shims/unix/fs.rs | 74 ++++++++++++------- src/shims/unix/macos/foreign_items.rs | 15 +--- .../pass-dep/shims/libc-fs-with-isolation.rs | 1 - tests/pass/shims/fs.rs | 13 +++- 7 files changed, 98 insertions(+), 45 deletions(-) diff --git a/ci.sh b/ci.sh index 378d7744cf..3e87b267fa 100755 --- a/ci.sh +++ b/ci.sh @@ -108,7 +108,8 @@ case $HOST_TARGET in MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests MIRI_TEST_TARGET=aarch64-apple-darwin run_tests MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests - MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align + MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs fs atomic env align + MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs fs atomic env align MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal hello integer vec panic/panic MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal no_std integer strings wasm MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std integer strings wasm diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 7bc26788b2..67e06b3621 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -157,6 +157,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, 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_target_isize(offset)?; + let whence = this.read_scalar(whence)?.to_i32()?; let result = this.lseek64(fd, offset, whence)?; this.write_scalar(result, dest)?; } diff --git a/src/shims/unix/freebsd/foreign_items.rs b/src/shims/unix/freebsd/foreign_items.rs index 7c843e106e..92f4e6d1bd 100644 --- a/src/shims/unix/freebsd/foreign_items.rs +++ b/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,31 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(Scalar::from_target_usize(len, this), dest)?; } + // File related shims + "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/shims/unix/fs.rs b/src/shims/unix/fs.rs index 721fa16123..14a95439f1 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -827,18 +827,14 @@ 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: i64, + whence: i32, ) -> InterpResult<'tcx, Scalar> { 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") { @@ -856,7 +852,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let Some(file_descriptor) = this.machine.file_handler.handles.get_mut(&fd) { let result = file_descriptor .seek(communicate, seek_from)? - .map(|offset| i64::try_from(offset).unwrap()); + .map(|offset| offset.try_into().unwrap()); this.try_unwrap_io_result(result)? } else { this.handle_not_found()? @@ -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> { 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> { 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> { 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> { 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,28 @@ 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, - )?; + if this.tcx.sess.target.os == "macos" { + 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 { + 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/shims/unix/macos/foreign_items.rs b/src/shims/unix/macos/foreign_items.rs index 07e19cadd6..ecc07e28a2 100644 --- a/src/shims/unix/macos/foreign_items.rs +++ b/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/tests/pass-dep/shims/libc-fs-with-isolation.rs b/tests/pass-dep/shims/libc-fs-with-isolation.rs index adfece5866..5185db0b0e 100644 --- a/tests/pass-dep/shims/libc-fs-with-isolation.rs +++ b/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" diff --git a/tests/pass/shims/fs.rs b/tests/pass/shims/fs.rs index 6ba39c1f56..35299ca226 100644 --- a/tests/pass/shims/fs.rs +++ b/tests/pass/shims/fs.rs @@ -4,11 +4,8 @@ #![feature(io_error_more)] #![feature(io_error_uncategorized)] -use std::collections::HashMap; -use std::ffi::OsString; use std::fs::{ - canonicalize, create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename, - File, OpenOptions, + canonicalize, create_dir, read_link, remove_dir_all, remove_file, rename, File, OpenOptions, }; use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; @@ -18,6 +15,7 @@ mod utils; fn main() { test_path_conversion(); + #[cfg(not(all(target_os = "freebsd", target_pointer_width = "32")))] test_file(); test_file_clone(); test_file_create_new(); @@ -28,6 +26,7 @@ fn main() { test_symlink(); test_errors(); test_rename(); + #[cfg(not(target_os = "freebsd"))] test_directory(); test_canonicalize(); test_from_raw_os_error(); @@ -63,6 +62,8 @@ fn test_path_conversion() { assert!(tmp.is_dir(), "{:?} is not a directory", tmp); } +// FIXME: lseek64 does not work yet on freebsd 32 bits +#[cfg(not(all(target_os = "freebsd", target_pointer_width = "32")))] fn test_file() { let bytes = b"Hello, World!\n"; let path = prepare("miri_test_fs_file.txt"); @@ -296,7 +297,11 @@ fn test_canonicalize() { remove_dir_all(&dir_path).unwrap(); } +#[cfg(not(target_os = "freebsd"))] // FIXME: freebsd does not support readdir yet fn test_directory() { + use std::collections::HashMap; + use std::ffi::OsString; + use std::fs::{read_dir, remove_dir}; let dir_path = prepare_dir("miri_test_fs_dir"); // Creating a directory should succeed. create_dir(&dir_path).unwrap();