diff options
| -rw-r--r-- | src/tools/miri/src/shims/unix/fd.rs | 41 | ||||
| -rw-r--r-- | src/tools/miri/src/shims/unix/foreign_items.rs | 13 | ||||
| -rw-r--r-- | src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 26 |
3 files changed, 72 insertions, 8 deletions
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 599f78e712a..7f6a0978103 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -273,6 +273,32 @@ impl FdTable { impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn dup(&mut self, old_fd: i32) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + let Some(dup_fd) = this.machine.fds.dup(old_fd) else { + return this.fd_not_found(); + }; + Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, 0)) + } + + fn dup2(&mut self, old_fd: i32, new_fd: i32) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + let Some(dup_fd) = this.machine.fds.dup(old_fd) else { + return this.fd_not_found(); + }; + if new_fd != old_fd { + // Close new_fd if it is previously opened. + // If old_fd and new_fd point to the same description, then `dup_fd` ensures we keep the underlying file description alive. + if let Some(file_descriptor) = this.machine.fds.fds.insert(new_fd, dup_fd) { + // Ignore close error (not interpreter's) according to dup2() doc. + file_descriptor.close(this.machine.communicate())?.ok(); + } + } + Ok(new_fd) + } + fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -334,14 +360,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let fd = this.read_scalar(fd_op)?.to_i32()?; - Ok(Scalar::from_i32(if let Some(file_descriptor) = this.machine.fds.remove(fd) { - let result = file_descriptor.close(this.machine.communicate())?; - // return `0` if close is successful - let result = result.map(|()| 0i32); - this.try_unwrap_io_result(result)? - } else { - this.fd_not_found()? - })) + let Some(file_descriptor) = this.machine.fds.remove(fd) else { + return Ok(Scalar::from_i32(this.fd_not_found()?)); + }; + let result = file_descriptor.close(this.machine.communicate())?; + // return `0` if close is successful + let result = result.map(|()| 0i32); + Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } /// Function used when a file descriptor does not exist. It returns `Ok(-1)`and sets diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 53ad40cfd2c..2421f9244f3 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -115,6 +115,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.fcntl(args)?; this.write_scalar(Scalar::from_i32(result), dest)?; } + "dup" => { + let [old_fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let old_fd = this.read_scalar(old_fd)?.to_i32()?; + let new_fd = this.dup(old_fd)?; + this.write_scalar(Scalar::from_i32(new_fd), dest)?; + } + "dup2" => { + let [old_fd, new_fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let old_fd = this.read_scalar(old_fd)?.to_i32()?; + let new_fd = this.read_scalar(new_fd)?.to_i32()?; + let result = this.dup2(old_fd, new_fd)?; + this.write_scalar(Scalar::from_i32(result), dest)?; + } // File and file system access "open" | "open64" => { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 80c9757e9c9..da685e5c6b7 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -15,6 +15,7 @@ use std::path::PathBuf; mod utils; fn main() { + test_dup(); test_dup_stdout_stderr(); test_canonicalize_too_long(); test_rename(); @@ -74,6 +75,31 @@ fn test_dup_stdout_stderr() { } } +fn test_dup() { + let bytes = b"dup and dup2"; + let path = utils::prepare_with_content("miri_test_libc_dup.txt", bytes); + + let mut name = path.into_os_string(); + name.push("\0"); + let name_ptr = name.as_bytes().as_ptr().cast::<libc::c_char>(); + unsafe { + let fd = libc::open(name_ptr, libc::O_RDONLY); + let mut first_buf = [0u8; 4]; + libc::read(fd, first_buf.as_mut_ptr() as *mut libc::c_void, 4); + assert_eq!(&first_buf, b"dup "); + + let new_fd = libc::dup(fd); + let mut second_buf = [0u8; 4]; + libc::read(new_fd, second_buf.as_mut_ptr() as *mut libc::c_void, 4); + assert_eq!(&second_buf, b"and "); + + let new_fd2 = libc::dup2(fd, 8); + let mut third_buf = [0u8; 4]; + libc::read(new_fd2, third_buf.as_mut_ptr() as *mut libc::c_void, 4); + assert_eq!(&third_buf, b"dup2"); + } +} + fn test_canonicalize_too_long() { // Make sure we get an error for long paths. let too_long = "x/".repeat(libc::PATH_MAX.try_into().unwrap()); |
