From b127b61dccafafcc90d9bd283ebbd8d69355ff78 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 27 Feb 2025 00:01:15 +0800 Subject: [PATCH 1/4] Implement `chmod` and `fchmod` in Unix shims --- src/shims/unix/foreign_items.rs | 10 +++++ src/shims/unix/fs.rs | 73 ++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 0975707107..649e345cdc 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -339,6 +339,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.mkstemp(template)?; this.write_scalar(result, dest)?; } + "chmod" => { + let [path, mode] = this.check_shim(abi, Conv::C, link_name, args)?; + let result = this.chmod(path, mode)?; + this.write_scalar(result, dest)?; + }, + "fchmod" => { + let [fd, mode] = this.check_shim(abi, Conv::C, link_name, args)?; + let result = this.fchmod(fd, mode)?; + this.write_scalar(result, dest)?; + } // Unnamed sockets and pipes "socketpair" => { diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index f8e0c638c9..330555adc3 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -2,8 +2,8 @@ use std::borrow::Cow; use std::fs::{ - DirBuilder, File, FileType, Metadata, OpenOptions, ReadDir, read_dir, remove_dir, remove_file, - rename, + DirBuilder, File, FileType, Metadata, OpenOptions, Permissions, ReadDir, read_dir, remove_dir, + remove_file, rename, }; use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; @@ -1664,6 +1664,75 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We ran out of attempts to create the file, return an error. this.set_last_error_and_return_i32(LibcError("EEXIST")) } + + fn chmod(&mut self, path_op: &OpTy<'tcx>, perm_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + // Permissions::from_mode is Unix-specific. + this.assert_target_os_is_unix("chmod"); + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + + let pathname = this.read_path_from_c_str(this.read_pointer(path_op)?)?; + let perm = this.read_scalar(perm_op)?.to_u32()?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`chmod`", reject_with)?; + return this.set_last_error_and_return_i32(LibcError("EACCES")); + } + + let result = std::fs::set_permissions(pathname, Permissions::from_mode(perm)); + let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; + + interp_ok(Scalar::from_i32(result)) + } + #[cfg(not(unix))] + { + unreachable!() + } + } + + fn fchmod(&mut self, fd_op: &OpTy<'tcx>, perm_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + // `Permissions::from_mode` is Unix-specific. + this.assert_target_os_is_unix("fchmod"); + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + + let fd = this.read_scalar(fd_op)?.to_i32()?; + let perm = this.read_scalar(perm_op)?.to_u32()?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`fchmod`", reject_with)?; + // Set error code as "EBADF" (bad fd) + return this.set_last_error_and_return_i32(LibcError("EBADF")); + } + + let Some(fd) = this.machine.fds.get(fd) else { + return this.set_last_error_and_return_i32(LibcError("EBADF")); + }; + + let file = fd.downcast::().ok_or_else(|| { + err_unsup_format!("`fchmod` is only supported on file-backed file descriptors") + })?; + + let result = file.file.set_permissions(Permissions::from_mode(perm)); + let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; + + interp_ok(Scalar::from_i32(result)) + } + #[cfg(not(unix))] + { + unreachable!() + } + } } /// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when From a51d7f5a55c87d14380ff38cfc2ab0745dabe320 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 27 Feb 2025 00:16:22 +0800 Subject: [PATCH 2/4] fix fmt --- src/shims/unix/foreign_items.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 649e345cdc..f85a729e67 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -343,7 +343,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [path, mode] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.chmod(path, mode)?; this.write_scalar(result, dest)?; - }, + } "fchmod" => { let [fd, mode] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.fchmod(fd, mode)?; From 989c19b98e838eca944a004a1a7fb7d5df292e38 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 27 Feb 2025 11:41:25 +0800 Subject: [PATCH 3/4] fix perm width and things --- src/shims/unix/fs.rs | 53 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index 330555adc3..40edd52c24 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -1668,53 +1668,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn chmod(&mut self, path_op: &OpTy<'tcx>, perm_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - // Permissions::from_mode is Unix-specific. - this.assert_target_os_is_unix("chmod"); + let pathname = this.read_path_from_c_str(this.read_pointer(path_op)?)?; + let perm = this.read_scalar(perm_op)?.to_uint(this.libc_ty_layout("mode_t").size)?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`chmod`", reject_with)?; + return this.set_last_error_and_return_i32(LibcError("EACCES")); + } + // Permissions::from_mode is Unix-specific. #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; - let pathname = this.read_path_from_c_str(this.read_pointer(path_op)?)?; - let perm = this.read_scalar(perm_op)?.to_u32()?; - - // Reject if isolation is enabled. - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`chmod`", reject_with)?; - return this.set_last_error_and_return_i32(LibcError("EACCES")); - } - - let result = std::fs::set_permissions(pathname, Permissions::from_mode(perm)); + let result = std::fs::set_permissions( + pathname, + Permissions::from_mode(perm.try_into().unwrap()), + ); let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; interp_ok(Scalar::from_i32(result)) } #[cfg(not(unix))] { - unreachable!() + throw_unsup_format!("`chmod` is not supported on this platform") } } fn fchmod(&mut self, fd_op: &OpTy<'tcx>, perm_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - // `Permissions::from_mode` is Unix-specific. - this.assert_target_os_is_unix("fchmod"); + let fd = this.read_scalar(fd_op)?.to_i32()?; + let perm = this.read_scalar(perm_op)?.to_uint(this.libc_ty_layout("mode_t").size)?; + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`fchmod`", reject_with)?; + // Set error code as "EBADF" (bad fd) + return this.set_last_error_and_return_i32(LibcError("EBADF")); + } + // `Permissions::from_mode` is Unix-specific. #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; - let fd = this.read_scalar(fd_op)?.to_i32()?; - let perm = this.read_scalar(perm_op)?.to_u32()?; - - // Reject if isolation is enabled. - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`fchmod`", reject_with)?; - // Set error code as "EBADF" (bad fd) - return this.set_last_error_and_return_i32(LibcError("EBADF")); - } - let Some(fd) = this.machine.fds.get(fd) else { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; @@ -1723,14 +1721,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { err_unsup_format!("`fchmod` is only supported on file-backed file descriptors") })?; - let result = file.file.set_permissions(Permissions::from_mode(perm)); + let result = + file.file.set_permissions(Permissions::from_mode(perm.try_into().unwrap())); let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; interp_ok(Scalar::from_i32(result)) } #[cfg(not(unix))] { - unreachable!() + throw_unsup_format!("`fchmod` is not supported on this platform") } } } From 34a7131e2505db08484ee03264d3ceb1997f4fa9 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 27 Feb 2025 12:08:40 +0800 Subject: [PATCH 4/4] fix clippy warning --- src/shims/unix/fs.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index 40edd52c24..25c1daeb2b 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -2,8 +2,8 @@ use std::borrow::Cow; use std::fs::{ - DirBuilder, File, FileType, Metadata, OpenOptions, Permissions, ReadDir, read_dir, remove_dir, - remove_file, rename, + DirBuilder, File, FileType, Metadata, OpenOptions, ReadDir, read_dir, remove_dir, remove_file, + rename, }; use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; @@ -1668,9 +1668,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn chmod(&mut self, path_op: &OpTy<'tcx>, perm_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let pathname = this.read_path_from_c_str(this.read_pointer(path_op)?)?; - let perm = this.read_scalar(perm_op)?.to_uint(this.libc_ty_layout("mode_t").size)?; - // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`chmod`", reject_with)?; @@ -1680,8 +1677,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Permissions::from_mode is Unix-specific. #[cfg(unix)] { + use std::fs::Permissions; use std::os::unix::fs::PermissionsExt; + let pathname = this.read_path_from_c_str(this.read_pointer(path_op)?)?; + let perm = this.read_scalar(perm_op)?.to_uint(this.libc_ty_layout("mode_t").size)?; + let result = std::fs::set_permissions( pathname, Permissions::from_mode(perm.try_into().unwrap()), @@ -1692,6 +1693,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } #[cfg(not(unix))] { + let (_, _) = (path_op, perm_op); throw_unsup_format!("`chmod` is not supported on this platform") } } @@ -1699,20 +1701,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn fchmod(&mut self, fd_op: &OpTy<'tcx>, perm_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let fd = this.read_scalar(fd_op)?.to_i32()?; - let perm = this.read_scalar(perm_op)?.to_uint(this.libc_ty_layout("mode_t").size)?; - // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fchmod`", reject_with)?; // Set error code as "EBADF" (bad fd) return this.set_last_error_and_return_i32(LibcError("EBADF")); } + // `Permissions::from_mode` is Unix-specific. #[cfg(unix)] { + use std::fs::Permissions; use std::os::unix::fs::PermissionsExt; + let fd = this.read_scalar(fd_op)?.to_i32()?; + let perm = this.read_scalar(perm_op)?.to_uint(this.libc_ty_layout("mode_t").size)?; + let Some(fd) = this.machine.fds.get(fd) else { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; @@ -1729,6 +1733,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } #[cfg(not(unix))] { + let (_, _) = (fd_op, perm_op); throw_unsup_format!("`fchmod` is not supported on this platform") } }