freebsd add *stat calls interception support

This commit is contained in:
David Carlier 2023-11-20 20:01:10 +00:00
parent 535db18002
commit 9911d8d9bf
6 changed files with 118 additions and 41 deletions

View File

@ -225,6 +225,21 @@ fn project_field_named<P: Projectable<'tcx, Provenance>>(
bug!("No field named {} in type {}", name, base.layout().ty); 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, /// 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 /// 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. /// this method is fine for almost all integer types.

View File

@ -155,6 +155,17 @@ fn emulate_foreign_item_inner(
} }
"lseek64" => { "lseek64" => {
let [fd, offset, whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 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)?; let result = this.lseek64(fd, offset, whence)?;
this.write_scalar(result, dest)?; this.write_scalar(result, dest)?;
} }

View File

@ -3,6 +3,7 @@
use crate::*; use crate::*;
use shims::foreign_items::EmulateForeignItemResult; use shims::foreign_items::EmulateForeignItemResult;
use shims::unix::fs::EvalContextExt as _;
use shims::unix::thread::EvalContextExt as _; use shims::unix::thread::EvalContextExt as _;
pub fn is_dyn_sym(_name: &str) -> bool { pub fn is_dyn_sym(_name: &str) -> bool {
@ -63,6 +64,33 @@ fn emulate_foreign_item_inner(
this.write_scalar(Scalar::from_target_usize(len, this), dest)?; 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 // errno
"__error" => { "__error" => {
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

View File

@ -827,24 +827,20 @@ fn write(
fn lseek64( fn lseek64(
&mut self, &mut self,
fd_op: &OpTy<'tcx, Provenance>, fd: i32,
offset_op: &OpTy<'tcx, Provenance>, offset: i128,
whence_op: &OpTy<'tcx, Provenance>, whence: i32,
) -> InterpResult<'tcx, Scalar<Provenance>> { ) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
// Isolation check is done via `FileDescriptor` trait. // 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") { let seek_from = if whence == this.eval_libc_i32("SEEK_SET") {
SeekFrom::Start(u64::try_from(offset).unwrap()) SeekFrom::Start(u64::try_from(offset).unwrap())
} else if whence == this.eval_libc_i32("SEEK_CUR") { } 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") { } else if whence == this.eval_libc_i32("SEEK_END") {
SeekFrom::End(offset) SeekFrom::End(i64::try_from(offset).unwrap())
} else { } else {
let einval = this.eval_libc("EINVAL"); let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?; this.set_last_error(einval)?;
@ -911,13 +907,16 @@ fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> {
this.try_unwrap_io_result(result) this.try_unwrap_io_result(result)
} }
fn macos_stat( fn macos_fbsd_stat(
&mut self, &mut self,
path_op: &OpTy<'tcx, Provenance>, path_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx, Provenance>, buf_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> { ) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut(); 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_scalar = this.read_pointer(path_op)?;
let path = this.read_path_from_c_str(path_scalar)?.into_owned(); let path = this.read_path_from_c_str(path_scalar)?.into_owned();
@ -940,13 +939,16 @@ fn macos_stat(
} }
// `lstat` is used to get symlink metadata. // `lstat` is used to get symlink metadata.
fn macos_lstat( fn macos_fbsd_lstat(
&mut self, &mut self,
path_op: &OpTy<'tcx, Provenance>, path_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx, Provenance>, buf_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> { ) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut(); 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_scalar = this.read_pointer(path_op)?;
let path = this.read_path_from_c_str(path_scalar)?.into_owned(); let path = this.read_path_from_c_str(path_scalar)?.into_owned();
@ -967,14 +969,16 @@ fn macos_lstat(
Ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) Ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
} }
fn macos_fstat( fn macos_fbsd_fstat(
&mut self, &mut self,
fd_op: &OpTy<'tcx, Provenance>, fd_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx, Provenance>, buf_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> { ) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut(); 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()?; let fd = this.read_scalar(fd_op)?.to_i32()?;
@ -1213,7 +1217,7 @@ fn mkdir(
let this = self.eval_context_mut(); let this = self.eval_context_mut();
#[cfg_attr(not(unix), allow(unused_variables))] #[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()?) u32::from(this.read_scalar(mode_op)?.to_u16()?)
} else { } else {
this.read_scalar(mode_op)?.to_u32()? this.read_scalar(mode_op)?.to_u32()?
@ -1385,7 +1389,7 @@ fn linux_readdir64(
Ok(Scalar::from_maybe_pointer(entry, this)) Ok(Scalar::from_maybe_pointer(entry, this))
} }
fn macos_readdir_r( fn macos_fbsd_readdir_r(
&mut self, &mut self,
dirp_op: &OpTy<'tcx, Provenance>, dirp_op: &OpTy<'tcx, Provenance>,
entry_op: &OpTy<'tcx, Provenance>, entry_op: &OpTy<'tcx, Provenance>,
@ -1393,7 +1397,9 @@ fn macos_readdir_r(
) -> InterpResult<'tcx, Scalar<Provenance>> { ) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut(); 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)?; let dirp = this.read_target_usize(dirp_op)?;
@ -1424,7 +1430,7 @@ fn macos_readdir_r(
// } // }
let entry_place = this.deref_pointer_as(entry_op, this.libc_ty_layout("dirent"))?; 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 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( let (name_fits, file_name_buf_len) = this.write_os_str_to_c_str(
@ -1448,16 +1454,41 @@ fn macos_readdir_r(
let file_type = this.file_type_to_d_type(dir_entry.file_type())?; let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
this.write_int_fields_named( // macOS offset field is d_seekoff
&[ if this.projectable_has_field(&entry_place, "d_seekoff") {
("d_ino", ino.into()), this.write_int_fields_named(
("d_seekoff", 0), &[
("d_reclen", 0), ("d_ino", ino.into()),
("d_namlen", file_name_len.into()), ("d_seekoff", 0),
("d_type", file_type.into()), ("d_reclen", 0),
], ("d_namlen", file_name_len.into()),
&entry_place, ("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)?; let result_place = this.deref_pointer(result_op)?;
this.write_scalar(this.read_scalar(entry_op)?, &result_place)?; this.write_scalar(this.read_scalar(entry_op)?, &result_place)?;

View File

@ -40,18 +40,18 @@ fn emulate_foreign_item_inner(
"stat" | "stat64" | "stat$INODE64" => { "stat" | "stat64" | "stat$INODE64" => {
let [path, buf] = let [path, buf] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 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)?; this.write_scalar(result, dest)?;
} }
"lstat" | "lstat64" | "lstat$INODE64" => { "lstat" | "lstat64" | "lstat$INODE64" => {
let [path, buf] = let [path, buf] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 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)?; this.write_scalar(result, dest)?;
} }
"fstat" | "fstat64" | "fstat$INODE64" => { "fstat" | "fstat64" | "fstat$INODE64" => {
let [fd, buf] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 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)?; this.write_scalar(result, dest)?;
} }
"opendir$INODE64" => { "opendir$INODE64" => {
@ -62,14 +62,7 @@ fn emulate_foreign_item_inner(
"readdir_r" | "readdir_r$INODE64" => { "readdir_r" | "readdir_r$INODE64" => {
let [dirp, entry, result] = let [dirp, entry, result] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.macos_readdir_r(dirp, entry, result)?; let result = this.macos_fbsd_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)?;
this.write_scalar(result, dest)?; this.write_scalar(result, dest)?;
} }
"realpath$DARWIN_EXTSN" => { "realpath$DARWIN_EXTSN" => {

View File

@ -1,5 +1,4 @@
//@ignore-target-windows: no libc on Windows //@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 //@compile-flags: -Zmiri-isolation-error=warn-nobacktrace
//@normalize-stderr-test: "(stat(x)?)" -> "$$STAT" //@normalize-stderr-test: "(stat(x)?)" -> "$$STAT"