Skip to content

Commit 9911d8d

Browse files
committed
freebsd add *stat calls interception support
1 parent 535db18 commit 9911d8d

File tree

6 files changed

+118
-41
lines changed

6 files changed

+118
-41
lines changed

src/tools/miri/src/helpers.rs

+15
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
225225
bug!("No field named {} in type {}", name, base.layout().ty);
226226
}
227227

228+
/// Search if Project (which must be a struct or union type) contains the `name` field.
229+
fn projectable_has_field<P: Projectable<'tcx, Provenance>>(
230+
&self,
231+
base: &P,
232+
name: &str,
233+
) -> bool {
234+
let adt = base.layout().ty.ty_adt_def().unwrap();
235+
for field in adt.non_enum_variant().fields.iter() {
236+
if field.name.as_str() == name {
237+
return true;
238+
}
239+
}
240+
false
241+
}
242+
228243
/// Write an int of the appropriate size to `dest`. The target type may be signed or unsigned,
229244
/// we try to do the right thing anyway. `i128` can fit all integer types except for `u128` so
230245
/// this method is fine for almost all integer types.

src/tools/miri/src/shims/unix/foreign_items.rs

+11
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
155155
}
156156
"lseek64" => {
157157
let [fd, offset, whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
158+
let fd = this.read_scalar(fd)?.to_i32()?;
159+
let offset = this.read_scalar(offset)?.to_i64()?;
160+
let whence = this.read_scalar(whence)?.to_i32()?;
161+
let result = this.lseek64(fd, offset.into(), whence)?;
162+
this.write_scalar(result, dest)?;
163+
}
164+
"lseek" => {
165+
let [fd, offset, whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
166+
let fd = this.read_scalar(fd)?.to_i32()?;
167+
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
168+
let whence = this.read_scalar(whence)?.to_i32()?;
158169
let result = this.lseek64(fd, offset, whence)?;
159170
this.write_scalar(result, dest)?;
160171
}

src/tools/miri/src/shims/unix/freebsd/foreign_items.rs

+28
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use rustc_target::spec::abi::Abi;
33

44
use crate::*;
55
use shims::foreign_items::EmulateForeignItemResult;
6+
use shims::unix::fs::EvalContextExt as _;
67
use shims::unix::thread::EvalContextExt as _;
78

89
pub fn is_dyn_sym(_name: &str) -> bool {
@@ -63,6 +64,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
6364
this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
6465
}
6566

67+
// File related shims
68+
// For those, we both intercept `func` and `call@FBSD_1.0` symbols cases
69+
// since freebsd 12 the former form can be expected.
70+
"stat" | "stat@FBSD_1.0" => {
71+
let [path, buf] =
72+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
73+
let result = this.macos_fbsd_stat(path, buf)?;
74+
this.write_scalar(result, dest)?;
75+
}
76+
"lstat" | "lstat@FBSD_1.0" => {
77+
let [path, buf] =
78+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
79+
let result = this.macos_fbsd_lstat(path, buf)?;
80+
this.write_scalar(result, dest)?;
81+
}
82+
"fstat" | "fstat@FBSD_1.0" => {
83+
let [fd, buf] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
84+
let result = this.macos_fbsd_fstat(fd, buf)?;
85+
this.write_scalar(result, dest)?;
86+
}
87+
"readdir_r" | "readdir_r@FBSD_1.0" => {
88+
let [dirp, entry, result] =
89+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
90+
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
91+
this.write_scalar(result, dest)?;
92+
}
93+
6694
// errno
6795
"__error" => {
6896
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

src/tools/miri/src/shims/unix/fs.rs

+60-29
Original file line numberDiff line numberDiff line change
@@ -827,24 +827,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
827827

828828
fn lseek64(
829829
&mut self,
830-
fd_op: &OpTy<'tcx, Provenance>,
831-
offset_op: &OpTy<'tcx, Provenance>,
832-
whence_op: &OpTy<'tcx, Provenance>,
830+
fd: i32,
831+
offset: i128,
832+
whence: i32,
833833
) -> InterpResult<'tcx, Scalar<Provenance>> {
834834
let this = self.eval_context_mut();
835835

836836
// Isolation check is done via `FileDescriptor` trait.
837837

838-
let fd = this.read_scalar(fd_op)?.to_i32()?;
839-
let offset = this.read_scalar(offset_op)?.to_i64()?;
840-
let whence = this.read_scalar(whence_op)?.to_i32()?;
841-
842838
let seek_from = if whence == this.eval_libc_i32("SEEK_SET") {
843839
SeekFrom::Start(u64::try_from(offset).unwrap())
844840
} else if whence == this.eval_libc_i32("SEEK_CUR") {
845-
SeekFrom::Current(offset)
841+
SeekFrom::Current(i64::try_from(offset).unwrap())
846842
} else if whence == this.eval_libc_i32("SEEK_END") {
847-
SeekFrom::End(offset)
843+
SeekFrom::End(i64::try_from(offset).unwrap())
848844
} else {
849845
let einval = this.eval_libc("EINVAL");
850846
this.set_last_error(einval)?;
@@ -911,13 +907,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
911907
this.try_unwrap_io_result(result)
912908
}
913909

914-
fn macos_stat(
910+
fn macos_fbsd_stat(
915911
&mut self,
916912
path_op: &OpTy<'tcx, Provenance>,
917913
buf_op: &OpTy<'tcx, Provenance>,
918914
) -> InterpResult<'tcx, Scalar<Provenance>> {
919915
let this = self.eval_context_mut();
920-
this.assert_target_os("macos", "stat");
916+
917+
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
918+
panic!("`macos_fbsd_stat` should not be called on {}", this.tcx.sess.target.os);
919+
}
921920

922921
let path_scalar = this.read_pointer(path_op)?;
923922
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> {
940939
}
941940

942941
// `lstat` is used to get symlink metadata.
943-
fn macos_lstat(
942+
fn macos_fbsd_lstat(
944943
&mut self,
945944
path_op: &OpTy<'tcx, Provenance>,
946945
buf_op: &OpTy<'tcx, Provenance>,
947946
) -> InterpResult<'tcx, Scalar<Provenance>> {
948947
let this = self.eval_context_mut();
949-
this.assert_target_os("macos", "lstat");
948+
949+
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
950+
panic!("`macos_fbsd_lstat` should not be called on {}", this.tcx.sess.target.os);
951+
}
950952

951953
let path_scalar = this.read_pointer(path_op)?;
952954
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> {
967969
Ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
968970
}
969971

970-
fn macos_fstat(
972+
fn macos_fbsd_fstat(
971973
&mut self,
972974
fd_op: &OpTy<'tcx, Provenance>,
973975
buf_op: &OpTy<'tcx, Provenance>,
974976
) -> InterpResult<'tcx, Scalar<Provenance>> {
975977
let this = self.eval_context_mut();
976978

977-
this.assert_target_os("macos", "fstat");
979+
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
980+
panic!("`macos_fbsd_fstat` should not be called on {}", this.tcx.sess.target.os);
981+
}
978982

979983
let fd = this.read_scalar(fd_op)?.to_i32()?;
980984

@@ -1213,7 +1217,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
12131217
let this = self.eval_context_mut();
12141218

12151219
#[cfg_attr(not(unix), allow(unused_variables))]
1216-
let mode = if this.tcx.sess.target.os == "macos" {
1220+
let mode = if matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
12171221
u32::from(this.read_scalar(mode_op)?.to_u16()?)
12181222
} else {
12191223
this.read_scalar(mode_op)?.to_u32()?
@@ -1385,15 +1389,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
13851389
Ok(Scalar::from_maybe_pointer(entry, this))
13861390
}
13871391

1388-
fn macos_readdir_r(
1392+
fn macos_fbsd_readdir_r(
13891393
&mut self,
13901394
dirp_op: &OpTy<'tcx, Provenance>,
13911395
entry_op: &OpTy<'tcx, Provenance>,
13921396
result_op: &OpTy<'tcx, Provenance>,
13931397
) -> InterpResult<'tcx, Scalar<Provenance>> {
13941398
let this = self.eval_context_mut();
13951399

1396-
this.assert_target_os("macos", "readdir_r");
1400+
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
1401+
panic!("`macos_fbsd_readdir_r` should not be called on {}", this.tcx.sess.target.os);
1402+
}
13971403

13981404
let dirp = this.read_target_usize(dirp_op)?;
13991405

@@ -1424,7 +1430,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
14241430
// }
14251431

14261432
let entry_place = this.deref_pointer_as(entry_op, this.libc_ty_layout("dirent"))?;
1427-
let name_place = this.project_field(&entry_place, 5)?;
1433+
let name_place = this.project_field_named(&entry_place, "d_name")?;
14281434

14291435
let file_name = dir_entry.file_name(); // not a Path as there are no separators!
14301436
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> {
14481454

14491455
let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
14501456

1451-
this.write_int_fields_named(
1452-
&[
1453-
("d_ino", ino.into()),
1454-
("d_seekoff", 0),
1455-
("d_reclen", 0),
1456-
("d_namlen", file_name_len.into()),
1457-
("d_type", file_type.into()),
1458-
],
1459-
&entry_place,
1460-
)?;
1457+
// macOS offset field is d_seekoff
1458+
if this.projectable_has_field(&entry_place, "d_seekoff") {
1459+
this.write_int_fields_named(
1460+
&[
1461+
("d_ino", ino.into()),
1462+
("d_seekoff", 0),
1463+
("d_reclen", 0),
1464+
("d_namlen", file_name_len.into()),
1465+
("d_type", file_type.into()),
1466+
],
1467+
&entry_place,
1468+
)?;
1469+
// freebsd 12 and onwards had added the d_off field
1470+
} else if this.projectable_has_field(&entry_place, "d_off") {
1471+
this.write_int_fields_named(
1472+
&[
1473+
("d_fileno", ino.into()),
1474+
("d_off", 0),
1475+
("d_reclen", 0),
1476+
("d_type", file_type.into()),
1477+
("d_namlen", file_name_len.into()),
1478+
],
1479+
&entry_place,
1480+
)?;
1481+
} else {
1482+
this.write_int_fields_named(
1483+
&[
1484+
("d_fileno", ino.into()),
1485+
("d_reclen", 0),
1486+
("d_type", file_type.into()),
1487+
("d_namlen", file_name_len.into()),
1488+
],
1489+
&entry_place,
1490+
)?;
1491+
}
14611492

14621493
let result_place = this.deref_pointer(result_op)?;
14631494
this.write_scalar(this.read_scalar(entry_op)?, &result_place)?;

src/tools/miri/src/shims/unix/macos/foreign_items.rs

+4-11
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
4040
"stat" | "stat64" | "stat$INODE64" => {
4141
let [path, buf] =
4242
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
43-
let result = this.macos_stat(path, buf)?;
43+
let result = this.macos_fbsd_stat(path, buf)?;
4444
this.write_scalar(result, dest)?;
4545
}
4646
"lstat" | "lstat64" | "lstat$INODE64" => {
4747
let [path, buf] =
4848
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
49-
let result = this.macos_lstat(path, buf)?;
49+
let result = this.macos_fbsd_lstat(path, buf)?;
5050
this.write_scalar(result, dest)?;
5151
}
5252
"fstat" | "fstat64" | "fstat$INODE64" => {
5353
let [fd, buf] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
54-
let result = this.macos_fstat(fd, buf)?;
54+
let result = this.macos_fbsd_fstat(fd, buf)?;
5555
this.write_scalar(result, dest)?;
5656
}
5757
"opendir$INODE64" => {
@@ -62,14 +62,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
6262
"readdir_r" | "readdir_r$INODE64" => {
6363
let [dirp, entry, result] =
6464
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
65-
let result = this.macos_readdir_r(dirp, entry, result)?;
66-
this.write_scalar(result, dest)?;
67-
}
68-
"lseek" => {
69-
let [fd, offset, whence] =
70-
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
71-
// macOS is 64bit-only, so this is lseek64
72-
let result = this.lseek64(fd, offset, whence)?;
65+
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
7366
this.write_scalar(result, dest)?;
7467
}
7568
"realpath$DARWIN_EXTSN" => {

src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs

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

0 commit comments

Comments
 (0)