Skip to content

Commit a9872e5

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 9e6be84 commit a9872e5

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
@@ -851,8 +851,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
851851
// Reject if isolation is enabled.
852852
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
853853
this.reject_in_isolation("`stat`", reject_with)?;
854-
// macos stat never sets "EPERM". Set error code "ENOENT".
855-
this.set_last_error_from_io_error(ErrorKind::NotFound)?;
854+
let eacc = this.eval_libc("EACCES")?;
855+
this.set_last_error(eacc)?;
856856
return Ok(-1);
857857
}
858858

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

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

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

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

@@ -973,6 +965,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
973965
)
974966
}
975967

968+
// Reject if isolation is enabled.
969+
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
970+
this.reject_in_isolation("`statx`", reject_with)?;
971+
let ecode = if path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD")? {
972+
// since `path` is provided, either absolute or
973+
// relative to CWD, `EACCES` is the most relevant.
974+
this.eval_libc("EACCES")?
975+
} else {
976+
// `dirfd` is set to target file, and `path` is
977+
// empty. `EACCES` would violate the spec.
978+
this.eval_libc("EBADF")?
979+
};
980+
this.set_last_error(ecode)?;
981+
return Ok(-1);
982+
}
983+
976984
// the `_mask_op` paramter specifies the file information that the caller requested.
977985
// However `statx` is allowed to return information that was not requested or to not
978986
// return information that was requested. This `mask` represents the information we can
@@ -1167,8 +1175,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11671175
// Reject if isolation is enabled.
11681176
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
11691177
this.reject_in_isolation("`opendir`", reject_with)?;
1170-
// opendir function never sets "EPERM". Set "ENOENT".
1171-
this.set_last_error_from_io_error(ErrorKind::NotFound)?;
1178+
let eacc = this.eval_libc("EACCES")?;
1179+
this.set_last_error(eacc)?;
11721180
return Ok(Scalar::null_ptr(this));
11731181
}
11741182

@@ -1422,8 +1430,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
14221430
// Reject if isolation is enabled.
14231431
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
14241432
this.reject_in_isolation("`ftruncate64`", reject_with)?;
1425-
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
1426-
return Ok(-1);
1433+
// Set error code as "EBADF" (bad fd)
1434+
return this.handle_not_found();
14271435
}
14281436

14291437
let fd = this.read_scalar(fd_op)?.to_i32()?;
@@ -1554,8 +1562,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
15541562
// Reject if isolation is enabled.
15551563
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
15561564
this.reject_in_isolation("`readlink`", reject_with)?;
1557-
// readlink never sets "EPERM". Set "ENOENT".
1558-
this.set_last_error_from_io_error(ErrorKind::NotFound)?;
1565+
let eacc = this.eval_libc("EACCES")?;
1566+
this.set_last_error(eacc)?;
15591567
return Ok(-1);
15601568
}
15611569

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(x)?)" -> "$$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)