diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 0975707107..f85a729e67 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..25c1daeb2b 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -1664,6 +1664,79 @@ 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(); + + // 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::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()), + ); + let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; + + interp_ok(Scalar::from_i32(result)) + } + #[cfg(not(unix))] + { + let (_, _) = (path_op, perm_op); + 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(); + + // 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")); + }; + + 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.try_into().unwrap())); + let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; + + interp_ok(Scalar::from_i32(result)) + } + #[cfg(not(unix))] + { + let (_, _) = (fd_op, perm_op); + throw_unsup_format!("`fchmod` is not supported on this platform") + } + } } /// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when