From a1cabac72707020e30a787f44740b855583fc341 Mon Sep 17 00:00:00 2001 From: Smit Soni Date: Tue, 15 Jun 2021 17:33:18 -0700 Subject: [PATCH 1/3] Fix use of deprecated `check_no_isolation` in posix fs ops Update posix fs shims to use new API `reject_in_isolation`, which allows rejection with error code instead of always forcing abort. Error code chosen for each op is the most appropriate one from the list in corresponding syscall's manual. Updated helper APIs to not use quotes (`) around input name while preparing the message. This allows callers to pass multi-word string like -- "`read` from stdin". --- src/diagnostics.rs | 2 +- src/helpers.rs | 2 +- src/shims/env.rs | 8 +-- src/shims/posix/fs.rs | 155 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 140 insertions(+), 27 deletions(-) diff --git a/src/diagnostics.rs b/src/diagnostics.rs index b5b75a7fc31..f66f66e0088 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -328,7 +328,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx CreatedAlloc(AllocId(id)) => format!("created allocation with id {}", id), FreedAlloc(AllocId(id)) => format!("freed allocation with id {}", id), RejectedIsolatedOp(ref op) => - format!("`{}` was made to return an error due to isolation", op), + format!("{} was made to return an error due to isolation", op), }; let (title, diag_level) = match e { diff --git a/src/helpers.rs b/src/helpers.rs index 363aefa62d2..e09ef2865fb 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -409,7 +409,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx RejectOpWith::WarningWithoutBacktrace => { this.tcx .sess - .warn(&format!("`{}` was made to return an error due to isolation", op_name)); + .warn(&format!("{} was made to return an error due to isolation", op_name)); Ok(()) } RejectOpWith::Warning => { diff --git a/src/shims/env.rs b/src/shims/env.rs index ddd2b615889..9290ec022b5 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -322,7 +322,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let size = this.read_scalar(&size_op)?.to_machine_usize(&*this.tcx)?; if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("getcwd", reject_with)?; + this.reject_in_isolation("`getcwd`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; return Ok(Pointer::null()); } @@ -355,7 +355,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let buf = this.read_pointer(buf_op)?; if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("GetCurrentDirectoryW", reject_with)?; + this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; return Ok(0); } @@ -380,7 +380,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("chdir", reject_with)?; + this.reject_in_isolation("`chdir`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; return Ok(-1); @@ -408,7 +408,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let path = this.read_path_from_wide_str(this.read_pointer(path_op)?)?; if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("SetCurrentDirectoryW", reject_with)?; + this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; return Ok(0); diff --git a/src/shims/posix/fs.rs b/src/shims/posix/fs.rs index a14a9c907ee..3653fc43ebb 100644 --- a/src/shims/posix/fs.rs +++ b/src/shims/posix/fs.rs @@ -4,7 +4,7 @@ use std::convert::{TryFrom, TryInto}; use std::fs::{ read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir, }; -use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; use std::path::Path; use std::time::SystemTime; @@ -504,7 +504,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("`open`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`open`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } let flag = this.read_scalar(flag_op)?.to_i32()?; @@ -594,7 +599,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn fcntl(&mut self, args: &[OpTy<'tcx, Tag>]) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("`fcntl`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`fcntl`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } if args.len() < 2 { throw_ub_format!( @@ -785,7 +795,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn unlink(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("`unlink`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`unlink`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; @@ -811,7 +826,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); - this.check_no_isolation("`symlink`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`symlink`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } let target = this.read_path_from_c_str(this.read_pointer(target_op)?)?; let linkpath = this.read_path_from_c_str(this.read_pointer(linkpath_op)?)?; @@ -827,7 +847,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os("macos", "stat"); - this.check_no_isolation("`stat`")?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`stat`", reject_with)?; + // macos stat never sets "EPERM". Set error code "ENOENT". + this.set_last_error_from_io_error(ErrorKind::NotFound)?; + return Ok(-1); + } + // `stat` always follows symlinks. this.macos_stat_or_lstat(true, path_op, buf_op) } @@ -840,7 +868,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os("macos", "lstat"); - this.check_no_isolation("`lstat`")?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`lstat`", reject_with)?; + // macos lstat never sets "EPERM". Set error code "ENOENT". + this.set_last_error_from_io_error(ErrorKind::NotFound)?; + return Ok(-1); + } + this.macos_stat_or_lstat(false, path_op, buf_op) } @@ -852,7 +888,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("macos", "fstat"); - this.check_no_isolation("`fstat`")?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`fstat`", reject_with)?; + // Set error code as "EBADF" (bad fd) + return this.handle_not_found(); + } let fd = this.read_scalar(fd_op)?.to_i32()?; @@ -874,7 +916,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("linux", "statx"); - this.check_no_isolation("`statx`")?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`statx`", reject_with)?; + // statx never sets "EPERM". Set error code "ENOENT". + this.set_last_error_from_io_error(ErrorKind::NotFound)?; + return Ok(-1); + } let statxbuf_ptr = this.read_pointer(statxbuf_op)?; let pathname_ptr = this.read_pointer(pathname_op)?; @@ -1032,7 +1081,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("`rename`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`rename`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } let oldpath_ptr = this.read_pointer(oldpath_op)?; let newpath_ptr = this.read_pointer(newpath_op)?; @@ -1058,7 +1112,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("`mkdir`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`mkdir`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } #[cfg_attr(not(unix), allow(unused_variables))] let mode = if this.tcx.sess.target.os == "macos" { @@ -1088,7 +1147,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn rmdir(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("`rmdir`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`rmdir`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; @@ -1100,7 +1164,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn opendir(&mut self, name_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - this.check_no_isolation("`opendir`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`opendir`", reject_with)?; + // opendir function never sets "EPERM". Set "ENOENT". + this.set_last_error_from_io_error(ErrorKind::NotFound)?; + return Ok(Scalar::null_ptr(this)); + } let name = this.read_path_from_c_str(this.read_pointer(name_op)?)?; @@ -1131,7 +1201,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("linux", "readdir64_r"); - this.check_no_isolation("`readdir64_r`")?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`readdir64_r`", reject_with)?; + // Set error code as "EBADF" (bad fd) + return this.handle_not_found(); + } let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; @@ -1224,7 +1300,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("macos", "readdir_r"); - this.check_no_isolation("`readdir_r`")?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`readdir_r`", reject_with)?; + // Set error code as "EBADF" (bad fd) + return this.handle_not_found(); + } let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; @@ -1313,7 +1395,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn closedir(&mut self, dirp_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("`closedir`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`closedir`", reject_with)?; + // Set error code as "EBADF" (bad fd) + return this.handle_not_found(); + } let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; @@ -1332,7 +1419,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("`ftruncate64`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`ftruncate64`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } let fd = this.read_scalar(fd_op)?.to_i32()?; let length = this.read_scalar(length_op)?.to_i64()?; @@ -1367,7 +1459,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); - this.check_no_isolation("`fsync`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`fsync`", reject_with)?; + // Set error code as "EBADF" (bad fd) + return this.handle_not_found(); + } let fd = this.read_scalar(fd_op)?.to_i32()?; if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) { @@ -1383,7 +1480,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn fdatasync(&mut self, fd_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("`fdatasync`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`fdatasync`", reject_with)?; + // Set error code as "EBADF" (bad fd) + return this.handle_not_found(); + } let fd = this.read_scalar(fd_op)?.to_i32()?; if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) { @@ -1405,7 +1507,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("`sync_file_range`")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`sync_file_range`", reject_with)?; + // Set error code as "EBADF" (bad fd) + return this.handle_not_found(); + } let fd = this.read_scalar(fd_op)?.to_i32()?; let offset = this.read_scalar(offset_op)?.to_i64()?; @@ -1444,7 +1551,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); - this.check_no_isolation("readlink")?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`readlink`", reject_with)?; + // readlink never sets "EPERM". Set "ENOENT". + this.set_last_error_from_io_error(ErrorKind::NotFound)?; + return Ok(-1); + } let pathname = this.read_path_from_c_str(this.read_pointer(pathname_op)?)?; let buf = this.read_pointer(buf_op)?; From da6880427ac6bf2735019e34448c43900a5fc6df Mon Sep 17 00:00:00 2001 From: Smit Soni Date: Sun, 18 Jul 2021 14:40:59 -0700 Subject: [PATCH 2/3] 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. --- src/shims/posix/fs.rs | 44 +++++++++++--------- tests/run-pass/fs_with_isolation.rs | 54 +++++++++++++++++++++++++ tests/run-pass/fs_with_isolation.stderr | 20 +++++++++ 3 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 tests/run-pass/fs_with_isolation.rs create mode 100644 tests/run-pass/fs_with_isolation.stderr diff --git a/src/shims/posix/fs.rs b/src/shims/posix/fs.rs index 3653fc43ebb..2693dc09628 100644 --- a/src/shims/posix/fs.rs +++ b/src/shims/posix/fs.rs @@ -851,8 +851,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`stat`", reject_with)?; - // macos stat never sets "EPERM". Set error code "ENOENT". - this.set_last_error_from_io_error(ErrorKind::NotFound)?; + let eacc = this.eval_libc("EACCES")?; + this.set_last_error(eacc)?; return Ok(-1); } @@ -872,8 +872,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`lstat`", reject_with)?; - // macos lstat never sets "EPERM". Set error code "ENOENT". - this.set_last_error_from_io_error(ErrorKind::NotFound)?; + let eacc = this.eval_libc("EACCES")?; + this.set_last_error(eacc)?; return Ok(-1); } @@ -917,14 +917,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.assert_target_os("linux", "statx"); - // Reject if isolation is enabled. - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`statx`", reject_with)?; - // statx never sets "EPERM". Set error code "ENOENT". - this.set_last_error_from_io_error(ErrorKind::NotFound)?; - return Ok(-1); - } - let statxbuf_ptr = this.read_pointer(statxbuf_op)?; let pathname_ptr = this.read_pointer(pathname_op)?; @@ -973,6 +965,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) } + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`statx`", reject_with)?; + let ecode = if path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD")? { + // since `path` is provided, either absolute or + // relative to CWD, `EACCES` is the most relevant. + this.eval_libc("EACCES")? + } else { + // `dirfd` is set to target file, and `path` is + // empty. `EACCES` would violate the spec. + this.eval_libc("EBADF")? + }; + this.set_last_error(ecode)?; + return Ok(-1); + } + // the `_mask_op` paramter specifies the file information that the caller requested. // However `statx` is allowed to return information that was not requested or to not // 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 // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`opendir`", reject_with)?; - // opendir function never sets "EPERM". Set "ENOENT". - this.set_last_error_from_io_error(ErrorKind::NotFound)?; + let eacc = this.eval_libc("EACCES")?; + this.set_last_error(eacc)?; return Ok(Scalar::null_ptr(this)); } @@ -1422,8 +1430,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`ftruncate64`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - return Ok(-1); + // Set error code as "EBADF" (bad fd) + return this.handle_not_found(); } let fd = this.read_scalar(fd_op)?.to_i32()?; @@ -1554,8 +1562,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`readlink`", reject_with)?; - // readlink never sets "EPERM". Set "ENOENT". - this.set_last_error_from_io_error(ErrorKind::NotFound)?; + let eacc = this.eval_libc("EACCES")?; + this.set_last_error(eacc)?; return Ok(-1); } diff --git a/tests/run-pass/fs_with_isolation.rs b/tests/run-pass/fs_with_isolation.rs new file mode 100644 index 00000000000..a9e1e5094fa --- /dev/null +++ b/tests/run-pass/fs_with_isolation.rs @@ -0,0 +1,54 @@ +// ignore-windows: File handling is not implemented yet +// compile-flags: -Zmiri-isolation-error=warn-nobacktrace +// normalize-stderr-test "(stat(x)?)" -> "$$STAT" + +#![feature(rustc_private)] + +extern crate libc; + +use std::ffi::CString; +use std::os::unix; +use std::fs::{self, File}; +use std::io::{Error, ErrorKind}; + +fn main() { + // test `open` + assert_eq!(File::create("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied); + + // test `fcntl` + unsafe { + assert_eq!(libc::fcntl(1, libc::F_DUPFD, 0), -1); + assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EPERM)); + } + + // test `unlink` + assert_eq!(fs::remove_file("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied); + + // test `symlink` + assert_eq!(unix::fs::symlink("foo.txt", "foo_link.txt").unwrap_err().kind(), ErrorKind::PermissionDenied); + + // test `readlink` + let symlink_c_str = CString::new("foo.txt").unwrap(); + let mut buf = vec![0; "foo_link.txt".len() + 1]; + unsafe { + assert_eq!(libc::readlink(symlink_c_str.as_ptr(), buf.as_mut_ptr(), buf.len()), -1); + assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES)); + } + + // test `stat` + assert_eq!(fs::metadata("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied); + assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES)); + + // test `rename` + assert_eq!(fs::rename("a.txt", "b.txt").unwrap_err().kind(), ErrorKind::PermissionDenied); + + // test `mkdir` + assert_eq!(fs::create_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied); + + // test `rmdir` + assert_eq!(fs::remove_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied); + + // test `opendir` + assert_eq!(fs::read_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied); + assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES)); +} diff --git a/tests/run-pass/fs_with_isolation.stderr b/tests/run-pass/fs_with_isolation.stderr new file mode 100644 index 00000000000..ad75e42831b --- /dev/null +++ b/tests/run-pass/fs_with_isolation.stderr @@ -0,0 +1,20 @@ +warning: `open` was made to return an error due to isolation + +warning: `fcntl` was made to return an error due to isolation + +warning: `unlink` was made to return an error due to isolation + +warning: `symlink` was made to return an error due to isolation + +warning: `readlink` was made to return an error due to isolation + +warning: `$STAT` was made to return an error due to isolation + +warning: `rename` was made to return an error due to isolation + +warning: `mkdir` was made to return an error due to isolation + +warning: `rmdir` was made to return an error due to isolation + +warning: `opendir` was made to return an error due to isolation + From 20d0f2ee2607a1eecf095223250ca07307b9dbb0 Mon Sep 17 00:00:00 2001 From: Smit Soni Date: Tue, 20 Jul 2021 22:27:33 -0700 Subject: [PATCH 3/3] Move shim argument checks before isolation check This allows catching extremely incorrect arguments before rejecting due to isolation. --- src/shims/posix/fs.rs | 171 +++++++++++++++++++++--------------------- 1 file changed, 85 insertions(+), 86 deletions(-) diff --git a/src/shims/posix/fs.rs b/src/shims/posix/fs.rs index 2693dc09628..ac6e878da96 100644 --- a/src/shims/posix/fs.rs +++ b/src/shims/posix/fs.rs @@ -304,28 +304,6 @@ impl<'tcx> FileHandler { impl<'mir, 'tcx: 'mir> EvalContextExtPrivate<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - /// Emulate `stat` or `lstat` on `macos`. This function is not intended to be - /// called directly from `emulate_foreign_item_by_name`, so it does not check if isolation is - /// disabled or if the target OS is the correct one. Please use `macos_stat` or - /// `macos_lstat` instead. - fn macos_stat_or_lstat( - &mut self, - follow_symlink: bool, - path_op: &OpTy<'tcx, Tag>, - buf_op: &OpTy<'tcx, Tag>, - ) -> InterpResult<'tcx, i32> { - let this = self.eval_context_mut(); - - let path_scalar = this.read_pointer(path_op)?; - let path = this.read_path_from_c_str(path_scalar)?.into_owned(); - - let metadata = match FileMetadata::from_path(this, &path, follow_symlink)? { - Some(metadata) => metadata, - None => return Ok(-1), - }; - this.macos_stat_write_buf(metadata, buf_op) - } - fn macos_stat_write_buf( &mut self, metadata: FileMetadata, @@ -504,13 +482,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - // Reject if isolation is enabled. - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`open`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - return Ok(-1); - } - let flag = this.read_scalar(flag_op)?.to_i32()?; // Get the mode. On macOS, the argument type `mode_t` is actually `u16`, but @@ -588,6 +559,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`open`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } + let fd = options.open(&path).map(|file| { let fh = &mut this.machine.file_handler; fh.insert_fd(Box::new(FileHandle { file, writable })) @@ -599,13 +577,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn fcntl(&mut self, args: &[OpTy<'tcx, Tag>]) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - // Reject if isolation is enabled. - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`fcntl`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - return Ok(-1); - } - if args.len() < 2 { throw_ub_format!( "incorrect number of arguments for fcntl: got {}, expected at least 2", @@ -614,6 +585,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } let fd = this.read_scalar(&args[0])?.to_i32()?; let cmd = this.read_scalar(&args[1])?.to_i32()?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`fcntl`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } + // We only support getting the flags for a descriptor. if cmd == this.eval_libc_i32("F_GETFD")? { // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the @@ -795,6 +774,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn unlink(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`unlink`", reject_with)?; @@ -802,8 +783,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(-1); } - let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; - let result = remove_file(path).map(|_| 0); this.try_unwrap_io_result(result) } @@ -825,6 +804,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } let this = self.eval_context_mut(); + let target = this.read_path_from_c_str(this.read_pointer(target_op)?)?; + let linkpath = this.read_path_from_c_str(this.read_pointer(linkpath_op)?)?; // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { @@ -833,9 +814,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(-1); } - let target = this.read_path_from_c_str(this.read_pointer(target_op)?)?; - let linkpath = this.read_path_from_c_str(this.read_pointer(linkpath_op)?)?; - let result = create_link(&target, &linkpath).map(|_| 0); this.try_unwrap_io_result(result) } @@ -848,6 +826,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("macos", "stat"); + let path_scalar = this.read_pointer(path_op)?; + let path = this.read_path_from_c_str(path_scalar)?.into_owned(); + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`stat`", reject_with)?; @@ -857,7 +838,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } // `stat` always follows symlinks. - this.macos_stat_or_lstat(true, path_op, buf_op) + let metadata = match FileMetadata::from_path(this, &path, true)? { + Some(metadata) => metadata, + None => return Ok(-1), + }; + + this.macos_stat_write_buf(metadata, buf_op) } // `lstat` is used to get symlink metadata. @@ -869,6 +855,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("macos", "lstat"); + let path_scalar = this.read_pointer(path_op)?; + let path = this.read_path_from_c_str(path_scalar)?.into_owned(); + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`lstat`", reject_with)?; @@ -877,7 +866,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(-1); } - this.macos_stat_or_lstat(false, path_op, buf_op) + let metadata = match FileMetadata::from_path(this, &path, false)? { + Some(metadata) => metadata, + None => return Ok(-1), + }; + + this.macos_stat_write_buf(metadata, buf_op) } fn macos_fstat( @@ -889,6 +883,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.assert_target_os("macos", "fstat"); + let fd = this.read_scalar(fd_op)?.to_i32()?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fstat`", reject_with)?; @@ -896,8 +892,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return this.handle_not_found(); } - let fd = this.read_scalar(fd_op)?.to_i32()?; - let metadata = match FileMetadata::from_fd(this, fd)? { Some(metadata) => metadata, None => return Ok(-1), @@ -973,8 +967,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // relative to CWD, `EACCES` is the most relevant. this.eval_libc("EACCES")? } else { - // `dirfd` is set to target file, and `path` is - // empty. `EACCES` would violate the spec. + // `dirfd` is set to target file, and `path` is empty + // (or we would have hit the `throw_unsup_format` + // above). `EACCES` would violate the spec. + assert!(empty_path_flag); this.eval_libc("EBADF")? }; this.set_last_error(ecode)?; @@ -1089,13 +1085,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - // Reject if isolation is enabled. - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`rename`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - return Ok(-1); - } - let oldpath_ptr = this.read_pointer(oldpath_op)?; let newpath_ptr = this.read_pointer(newpath_op)?; @@ -1108,6 +1097,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let oldpath = this.read_path_from_c_str(oldpath_ptr)?; let newpath = this.read_path_from_c_str(newpath_ptr)?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`rename`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } + let result = rename(oldpath, newpath).map(|_| 0); this.try_unwrap_io_result(result) @@ -1120,13 +1116,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - // Reject if isolation is enabled. - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`mkdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - return Ok(-1); - } - #[cfg_attr(not(unix), allow(unused_variables))] let mode = if this.tcx.sess.target.os == "macos" { u32::from(this.read_scalar(mode_op)?.to_u16()?) @@ -1136,6 +1125,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`mkdir`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(-1); + } + #[cfg_attr(not(unix), allow(unused_mut))] let mut builder = DirBuilder::new(); @@ -1155,6 +1151,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn rmdir(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rmdir`", reject_with)?; @@ -1162,8 +1160,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(-1); } - let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; - let result = remove_dir(path).map(|_| 0i32); this.try_unwrap_io_result(result) @@ -1172,6 +1168,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn opendir(&mut self, name_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); + let name = this.read_path_from_c_str(this.read_pointer(name_op)?)?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`opendir`", reject_with)?; @@ -1180,8 +1178,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(Scalar::null_ptr(this)); } - let name = this.read_path_from_c_str(this.read_pointer(name_op)?)?; - let result = read_dir(name); match result { @@ -1210,6 +1206,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.assert_target_os("linux", "readdir64_r"); + let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`readdir64_r`", reject_with)?; @@ -1217,8 +1215,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return this.handle_not_found(); } - let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; - let dir_iter = this.machine.dir_handler.streams.get_mut(&dirp).ok_or_else(|| { err_unsup_format!("the DIR pointer passed to readdir64_r did not come from opendir") })?; @@ -1309,6 +1305,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.assert_target_os("macos", "readdir_r"); + let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`readdir_r`", reject_with)?; @@ -1316,8 +1314,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return this.handle_not_found(); } - let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; - let dir_iter = this.machine.dir_handler.streams.get_mut(&dirp).ok_or_else(|| { err_unsup_format!("the DIR pointer passed to readdir_r did not come from opendir") })?; @@ -1403,6 +1399,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn closedir(&mut self, dirp_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`closedir`", reject_with)?; @@ -1410,8 +1408,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return this.handle_not_found(); } - let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; - if let Some(dir_iter) = this.machine.dir_handler.streams.remove(&dirp) { drop(dir_iter); Ok(0) @@ -1427,6 +1423,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + let fd = this.read_scalar(fd_op)?.to_i32()?; + let length = this.read_scalar(length_op)?.to_i64()?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`ftruncate64`", reject_with)?; @@ -1434,8 +1433,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return this.handle_not_found(); } - let fd = this.read_scalar(fd_op)?.to_i32()?; - let length = this.read_scalar(length_op)?.to_i64()?; if let Some(file_descriptor) = this.machine.file_handler.handles.get_mut(&fd) { // FIXME: Support ftruncate64 for all FDs let FileHandle { file, writable } = file_descriptor.as_file_handle()?; @@ -1467,6 +1464,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); + let fd = this.read_scalar(fd_op)?.to_i32()?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fsync`", reject_with)?; @@ -1474,7 +1473,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return this.handle_not_found(); } - let fd = this.read_scalar(fd_op)?.to_i32()?; if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) { // FIXME: Support fsync for all FDs let FileHandle { file, writable } = file_descriptor.as_file_handle()?; @@ -1488,6 +1486,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn fdatasync(&mut self, fd_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + let fd = this.read_scalar(fd_op)?.to_i32()?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fdatasync`", reject_with)?; @@ -1495,7 +1495,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return this.handle_not_found(); } - let fd = this.read_scalar(fd_op)?.to_i32()?; if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) { // FIXME: Support fdatasync for all FDs let FileHandle { file, writable } = file_descriptor.as_file_handle()?; @@ -1515,13 +1514,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - // Reject if isolation is enabled. - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`sync_file_range`", reject_with)?; - // Set error code as "EBADF" (bad fd) - return this.handle_not_found(); - } - let fd = this.read_scalar(fd_op)?.to_i32()?; let offset = this.read_scalar(offset_op)?.to_i64()?; let nbytes = this.read_scalar(nbytes_op)?.to_i64()?; @@ -1541,6 +1533,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(-1); } + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`sync_file_range`", reject_with)?; + // Set error code as "EBADF" (bad fd) + return this.handle_not_found(); + } + if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) { // FIXME: Support sync_data_range for all FDs let FileHandle { file, writable } = file_descriptor.as_file_handle()?; @@ -1559,6 +1558,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); + let pathname = this.read_path_from_c_str(this.read_pointer(pathname_op)?)?; + let buf = this.read_pointer(buf_op)?; + let bufsize = this.read_scalar(bufsize_op)?.to_machine_usize(this)?; + // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`readlink`", reject_with)?; @@ -1567,10 +1570,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(-1); } - let pathname = this.read_path_from_c_str(this.read_pointer(pathname_op)?)?; - let buf = this.read_pointer(buf_op)?; - let bufsize = this.read_scalar(bufsize_op)?.to_machine_usize(this)?; - let result = std::fs::read_link(pathname); match result { Ok(resolved) => {