Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

freebsd add *stat calls interception support #3181

Merged
merged 2 commits into from
Jan 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
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.
Expand Down
11 changes: 11 additions & 0 deletions src/shims/unix/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?;
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
let result = this.lseek64(fd, offset, whence)?;
this.write_scalar(result, dest)?;
}
Expand Down
28 changes: 28 additions & 0 deletions src/shims/unix/freebsd/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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" => {
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
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" => {
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
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)?;
Expand Down
89 changes: 60 additions & 29 deletions src/shims/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down Expand Up @@ -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(
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
&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();
Expand All @@ -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();
Expand All @@ -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()?;

Expand Down Expand Up @@ -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()?
Expand Down Expand Up @@ -1385,15 +1389,17 @@ 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>,
result_op: &OpTy<'tcx, Provenance>,
) -> 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)?;

Expand Down Expand Up @@ -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(
Expand All @@ -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(
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
&[
("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)?;
Expand Down
15 changes: 4 additions & 11 deletions src/shims/unix/macos/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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" => {
Expand All @@ -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" => {
Expand Down
1 change: 0 additions & 1 deletion tests/pass-dep/shims/libc-fs-with-isolation.rs
Original file line number Diff line number Diff line change
@@ -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"

Expand Down