Skip to content

Commit e7650fd

Browse files
authored
Merge pull request #4714 from hulxv/feat/support-fstat-in-linux
feat: Support `fstat` in linux
2 parents c9d097d + 8b9d1d7 commit e7650fd

File tree

7 files changed

+63
-29
lines changed

7 files changed

+63
-29
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
345345
let result = this.symlink(target, linkpath)?;
346346
this.write_scalar(result, dest)?;
347347
}
348+
"fstat" => {
349+
let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
350+
let result = this.fstat(fd, buf)?;
351+
this.write_scalar(result, dest)?;
352+
}
348353
"rename" => {
349354
let [oldpath, newpath] = this.check_shim_sig(
350355
shim_sig!(extern "C" fn(*const _, *const _) -> i32),

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
148148
let result = this.macos_fbsd_solarish_lstat(path, buf)?;
149149
this.write_scalar(result, dest)?;
150150
}
151-
"fstat" | "fstat@FBSD_1.0" => {
151+
"fstat@FBSD_1.0" => {
152152
let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
153-
let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
153+
let result = this.fstat(fd, buf)?;
154154
this.write_scalar(result, dest)?;
155155
}
156156
"readdir_r" | "readdir_r@FBSD_1.0" => {

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

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ impl UnixFileDescription for FileHandle {
118118

119119
impl<'tcx> EvalContextExtPrivate<'tcx> for crate::MiriInterpCx<'tcx> {}
120120
trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
121-
fn macos_fbsd_solarish_write_stat_buf(
121+
fn write_stat_buf(
122122
&mut self,
123123
metadata: FileMetadata,
124124
buf_op: &OpTy<'tcx>,
@@ -130,7 +130,11 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
130130
let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0));
131131
let mode = metadata.mode.to_uint(this.libc_ty_layout("mode_t").size)?;
132132

133-
let buf = this.deref_pointer_as(buf_op, this.libc_ty_layout("stat"))?;
133+
// We do *not* use `deref_pointer_as` here since determining the right pointee type
134+
// is highly non-trivial: it depends on which exact alias of the function was invoked
135+
// (e.g. `fstat` vs `fstat64`), and then on FreeBSD it also depends on the ABI level
136+
// which can be different between the libc used by std and the libc used by everyone else.
137+
let buf = this.deref_pointer(buf_op)?;
134138
this.write_int_fields_named(
135139
&[
136140
("st_dev", metadata.dev.into()),
@@ -141,8 +145,11 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
141145
("st_gid", metadata.gid.into()),
142146
("st_rdev", 0),
143147
("st_atime", access_sec.into()),
148+
("st_atime_nsec", access_nsec.into()),
144149
("st_mtime", modified_sec.into()),
150+
("st_mtime_nsec", modified_nsec.into()),
145151
("st_ctime", 0),
152+
("st_ctime_nsec", 0),
146153
("st_size", metadata.size.into()),
147154
("st_blocks", 0),
148155
("st_blksize", 0),
@@ -153,9 +160,6 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
153160
if matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd) {
154161
this.write_int_fields_named(
155162
&[
156-
("st_atime_nsec", access_nsec.into()),
157-
("st_mtime_nsec", modified_nsec.into()),
158-
("st_ctime_nsec", 0),
159163
("st_birthtime", created_sec.into()),
160164
("st_birthtime_nsec", created_nsec.into()),
161165
("st_flags", 0),
@@ -550,7 +554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
550554
Err(err) => return this.set_last_error_and_return_i32(err),
551555
};
552556

553-
interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?))
557+
interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?))
554558
}
555559

556560
// `lstat` is used to get symlink metadata.
@@ -583,22 +587,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
583587
Err(err) => return this.set_last_error_and_return_i32(err),
584588
};
585589

586-
interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?))
590+
interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?))
587591
}
588592

589-
fn macos_fbsd_solarish_fstat(
590-
&mut self,
591-
fd_op: &OpTy<'tcx>,
592-
buf_op: &OpTy<'tcx>,
593-
) -> InterpResult<'tcx, Scalar> {
593+
fn fstat(&mut self, fd_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
594594
let this = self.eval_context_mut();
595595

596-
if !matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos)
597-
{
598-
panic!(
599-
"`macos_fbsd_solaris_fstat` should not be called on {}",
600-
this.tcx.sess.target.os
601-
);
596+
if !matches!(
597+
&this.tcx.sess.target.os,
598+
Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Linux
599+
) {
600+
panic!("`fstat` should not be called on {}", this.tcx.sess.target.os);
602601
}
603602

604603
let fd = this.read_scalar(fd_op)?.to_i32()?;
@@ -614,7 +613,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
614613
Ok(metadata) => metadata,
615614
Err(err) => return this.set_last_error_and_return_i32(err),
616615
};
617-
interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?))
616+
interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?))
618617
}
619618

620619
fn linux_statx(

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
5353
let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
5454
this.write_scalar(result, dest)?;
5555
}
56-
5756
// epoll, eventfd
5857
"epoll_create1" => {
5958
let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
5656
let result = this.macos_fbsd_solarish_lstat(path, buf)?;
5757
this.write_scalar(result, dest)?;
5858
}
59-
"fstat" | "fstat64" | "fstat$INODE64" => {
59+
"fstat$INODE64" => {
6060
let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
61-
let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
61+
let result = this.fstat(fd, buf)?;
6262
this.write_scalar(result, dest)?;
6363
}
6464
"opendir$INODE64" => {

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
100100
let result = this.macos_fbsd_solarish_lstat(path, buf)?;
101101
this.write_scalar(result, dest)?;
102102
}
103-
"fstat" | "fstat64" => {
104-
let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
105-
let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
106-
this.write_scalar(result, dest)?;
107-
}
108103
"readdir" => {
109104
let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
110105
let result = this.readdir64("dirent", dirp)?;

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ fn main() {
4242
test_posix_fallocate::<libc::off64_t>(libc::posix_fallocate64);
4343
#[cfg(target_os = "linux")]
4444
test_sync_file_range();
45+
test_fstat();
4546
test_isatty();
4647
test_read_and_uninit();
4748
test_nofollow_not_symlink();
@@ -452,6 +453,41 @@ fn test_sync_file_range() {
452453
assert_eq!(result_2, 0);
453454
}
454455

456+
fn test_fstat() {
457+
use std::mem::MaybeUninit;
458+
use std::os::unix::io::AsRawFd;
459+
460+
let path = utils::prepare_with_content("miri_test_libc_fstat.txt", b"hello");
461+
let file = File::open(&path).unwrap();
462+
let fd = file.as_raw_fd();
463+
464+
let mut stat = MaybeUninit::<libc::stat>::uninit();
465+
let res = unsafe { libc::fstat(fd, stat.as_mut_ptr()) };
466+
assert_eq!(res, 0);
467+
let stat = unsafe { stat.assume_init_ref() };
468+
469+
assert_eq!(stat.st_size, 5);
470+
assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFREG);
471+
472+
// Check that all fields are initialized.
473+
let _st_nlink = stat.st_nlink;
474+
let _st_blksize = stat.st_blksize;
475+
let _st_blocks = stat.st_blocks;
476+
let _st_ino = stat.st_ino;
477+
let _st_dev = stat.st_dev;
478+
let _st_uid = stat.st_uid;
479+
let _st_gid = stat.st_gid;
480+
let _st_rdev = stat.st_rdev;
481+
let _st_atime = stat.st_atime;
482+
let _st_mtime = stat.st_mtime;
483+
let _st_ctime = stat.st_ctime;
484+
let _st_atime_nsec = stat.st_atime_nsec;
485+
let _st_mtime_nsec = stat.st_mtime_nsec;
486+
let _st_ctime_nsec = stat.st_ctime_nsec;
487+
488+
remove_file(&path).unwrap();
489+
}
490+
455491
fn test_isatty() {
456492
// Testing whether our isatty shim returns the right value would require controlling whether
457493
// these streams are actually TTYs, which is hard.

0 commit comments

Comments
 (0)