Skip to content

Commit 18d25d0

Browse files
committed
Update error code for fs ops in isolation
Change the code to either `EACCES` (if the op is performed on the path), or `EBADF` (if the op is performed the fd) Updated ops: `stat`, `opendir`, `ftruncate64`, and `readlink` Add a new test for fs ops in isolation.
1 parent e3b7b82 commit 18d25d0

File tree

3 files changed

+100
-18
lines changed

3 files changed

+100
-18
lines changed

src/shims/posix/fs.rs

+26-18
Original file line numberDiff line numberDiff line change
@@ -852,8 +852,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
852852
// Reject if isolation is enabled.
853853
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
854854
this.reject_in_isolation("`stat`", reject_with)?;
855-
// macos stat never sets "EPERM". Set error code "ENOENT".
856-
this.set_last_error_from_io_error(ErrorKind::NotFound)?;
855+
let eacc = this.eval_libc("EACCES")?;
856+
this.set_last_error(eacc)?;
857857
return Ok(-1);
858858
}
859859

@@ -873,8 +873,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
873873
// Reject if isolation is enabled.
874874
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
875875
this.reject_in_isolation("`lstat`", reject_with)?;
876-
// macos lstat never sets "EPERM". Set error code "ENOENT".
877-
this.set_last_error_from_io_error(ErrorKind::NotFound)?;
876+
let eacc = this.eval_libc("EACCES")?;
877+
this.set_last_error(eacc)?;
878878
return Ok(-1);
879879
}
880880

@@ -918,14 +918,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
918918

919919
this.assert_target_os("linux", "statx");
920920

921-
// Reject if isolation is enabled.
922-
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
923-
this.reject_in_isolation("`statx`", reject_with)?;
924-
// statx never sets "EPERM". Set error code "ENOENT".
925-
this.set_last_error_from_io_error(ErrorKind::NotFound)?;
926-
return Ok(-1);
927-
}
928-
929921
let statxbuf_scalar = this.read_scalar(statxbuf_op)?.check_init()?;
930922
let pathname_scalar = this.read_scalar(pathname_op)?.check_init()?;
931923

@@ -976,6 +968,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
976968
)
977969
}
978970

971+
// Reject if isolation is enabled.
972+
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
973+
this.reject_in_isolation("`statx`", reject_with)?;
974+
let ecode = if path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD")? {
975+
// since `path` is provided, either absolute or
976+
// relative to CWD, `EACCES` is the most relevant.
977+
this.eval_libc("EACCES")?
978+
} else {
979+
// `dirfd` is set to target file, and `path` is
980+
// empty. `EACCES` would violate the spec.
981+
this.eval_libc("EBADF")?
982+
};
983+
this.set_last_error(ecode)?;
984+
return Ok(-1);
985+
}
986+
979987
// the `_mask_op` paramter specifies the file information that the caller requested.
980988
// However `statx` is allowed to return information that was not requested or to not
981989
// return information that was requested. This `mask` represents the information we can
@@ -1170,8 +1178,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11701178
// Reject if isolation is enabled.
11711179
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
11721180
this.reject_in_isolation("`opendir`", reject_with)?;
1173-
// opendir function never sets "EPERM". Set "ENOENT".
1174-
this.set_last_error_from_io_error(ErrorKind::NotFound)?;
1181+
let eacc = this.eval_libc("EACCES")?;
1182+
this.set_last_error(eacc)?;
11751183
return Ok(Scalar::null_ptr(this));
11761184
}
11771185

@@ -1425,8 +1433,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
14251433
// Reject if isolation is enabled.
14261434
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
14271435
this.reject_in_isolation("`ftruncate64`", reject_with)?;
1428-
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
1429-
return Ok(-1);
1436+
// Set error code as "EBADF" (bad fd)
1437+
return this.handle_not_found();
14301438
}
14311439

14321440
let fd = this.read_scalar(fd_op)?.to_i32()?;
@@ -1557,8 +1565,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
15571565
// Reject if isolation is enabled.
15581566
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
15591567
this.reject_in_isolation("`readlink`", reject_with)?;
1560-
// readlink never sets "EPERM". Set "ENOENT".
1561-
this.set_last_error_from_io_error(ErrorKind::NotFound)?;
1568+
let eacc = this.eval_libc("EACCES")?;
1569+
this.set_last_error(eacc)?;
15621570
return Ok(-1);
15631571
}
15641572

tests/run-pass/fs_with_isolation.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// ignore-windows: File handling is not implemented yet
2+
// compile-flags: -Zmiri-isolation-error=warn-nobacktrace
3+
// normalize-stderr-test "(stat|statx)" -> "$$STAT"
4+
5+
#![feature(rustc_private)]
6+
7+
extern crate libc;
8+
9+
use std::ffi::CString;
10+
use std::os::unix;
11+
use std::fs::{self, File};
12+
use std::io::{Error, ErrorKind};
13+
14+
fn main() {
15+
// test `open`
16+
assert_eq!(File::create("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
17+
18+
// test `fcntl`
19+
unsafe {
20+
assert_eq!(libc::fcntl(1, libc::F_DUPFD, 0), -1);
21+
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EPERM));
22+
}
23+
24+
// test `unlink`
25+
assert_eq!(fs::remove_file("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
26+
27+
// test `symlink`
28+
assert_eq!(unix::fs::symlink("foo.txt", "foo_link.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
29+
30+
// test `readlink`
31+
let symlink_c_str = CString::new("foo.txt").unwrap();
32+
let mut buf = vec![0; "foo_link.txt".len() + 1];
33+
unsafe {
34+
assert_eq!(libc::readlink(symlink_c_str.as_ptr(), buf.as_mut_ptr(), buf.len()), -1);
35+
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
36+
}
37+
38+
// test `stat`
39+
assert_eq!(fs::metadata("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
40+
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
41+
42+
// test `rename`
43+
assert_eq!(fs::rename("a.txt", "b.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
44+
45+
// test `mkdir`
46+
assert_eq!(fs::create_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied);
47+
48+
// test `rmdir`
49+
assert_eq!(fs::remove_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied);
50+
51+
// test `opendir`
52+
assert_eq!(fs::read_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied);
53+
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
54+
}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
warning: `open` was made to return an error due to isolation
2+
3+
warning: `fcntl` was made to return an error due to isolation
4+
5+
warning: `unlink` was made to return an error due to isolation
6+
7+
warning: `symlink` was made to return an error due to isolation
8+
9+
warning: `readlink` was made to return an error due to isolation
10+
11+
warning: `$STAT` was made to return an error due to isolation
12+
13+
warning: `rename` was made to return an error due to isolation
14+
15+
warning: `mkdir` was made to return an error due to isolation
16+
17+
warning: `rmdir` was made to return an error due to isolation
18+
19+
warning: `opendir` was made to return an error due to isolation
20+

0 commit comments

Comments
 (0)