From 03b5d95cffd587224fc16b4cafca25e0e52df337 Mon Sep 17 00:00:00 2001 From: David Cook Date: Sun, 26 Jan 2020 12:36:36 -0600 Subject: [PATCH 1/4] Add shim for lseek64 --- src/shims/foreign_items.rs | 5 +++++ src/shims/fs.rs | 34 +++++++++++++++++++++++++++++++++- tests/run-pass/fs.rs | 8 +++++++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index deeb37da44a3..9d025b2b7b24 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -489,6 +489,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } + "lseek64" => { + let result = this.lseek64(args[0], args[1], args[2])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + "unlink" => { let result = this.unlink(args[0])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; diff --git a/src/shims/fs.rs b/src/shims/fs.rs index 8a7cf9d31c0b..4cfff9446b85 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::fs::{remove_file, File, OpenOptions}; -use std::io::{Read, Write}; +use std::io::{Read, Seek, SeekFrom, Write}; use std::path::PathBuf; use std::time::SystemTime; @@ -264,6 +264,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } + fn lseek64( + &mut self, + fd_op: OpTy<'tcx, Tag>, + offset_op: OpTy<'tcx, Tag>, + whence_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, i64> { + let this = self.eval_context_mut(); + + this.check_no_isolation("lseek64")?; + + let fd = this.read_scalar(fd_op)?.to_i32()?; + let offset = this.read_scalar(offset_op)?.to_i64()?; + let whence = this.read_scalar(whence_op)?.to_i32()?; + + let seek_from = if whence == this.eval_libc_i32("SEEK_SET")? { + SeekFrom::Start(offset as u64) + } else if whence == this.eval_libc_i32("SEEK_CUR")? { + SeekFrom::Current(offset) + } else if whence == this.eval_libc_i32("SEEK_END")? { + SeekFrom::End(offset) + } else { + throw_unsup_format!("Unsupported whence argument to lseek64: {}", whence) + }; + + if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) { + let result = handle.file.seek(seek_from).map(|offset| offset as i64); + this.try_unwrap_io_result(result) + } else { + this.handle_not_found() + } + } + fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); diff --git a/tests/run-pass/fs.rs b/tests/run-pass/fs.rs index 81c56e4aafc7..4da67369aef8 100644 --- a/tests/run-pass/fs.rs +++ b/tests/run-pass/fs.rs @@ -2,7 +2,7 @@ // compile-flags: -Zmiri-disable-isolation use std::fs::{File, remove_file}; -use std::io::{Read, Write, ErrorKind, Result}; +use std::io::{Read, Write, ErrorKind, Result, Seek, SeekFrom}; use std::path::{PathBuf, Path}; fn test_metadata(bytes: &[u8], path: &Path) -> Result<()> { @@ -40,6 +40,12 @@ fn main() { file.read_to_end(&mut contents).unwrap(); assert_eq!(bytes, contents.as_slice()); + // Test that seeking to the beginning and reading until EOF gets the text again. + file.seek(SeekFrom::Start(0)).unwrap(); + let mut contents = Vec::new(); + file.read_to_end(&mut contents).unwrap(); + assert_eq!(bytes, contents.as_slice()); + // Test that metadata of an absolute path is correct. test_metadata(bytes, &path).unwrap(); // Test that metadata of a relative path is correct. From 0d0902a1e131f57995e705c362b46d59bbf63234 Mon Sep 17 00:00:00 2001 From: David Cook Date: Sun, 26 Jan 2020 18:07:31 -0600 Subject: [PATCH 2/4] Apply shim to lseek too (for macOS) --- src/shims/foreign_items.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 9d025b2b7b24..a6c1a31b4feb 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -489,7 +489,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "lseek64" => { + | "lseek64" + | "lseek" + => { let result = this.lseek64(args[0], args[1], args[2])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } From 2f25e4cd17e0f97d4da9547c9b2201b5918c34b5 Mon Sep 17 00:00:00 2001 From: David Cook Date: Wed, 29 Jan 2020 19:04:18 -0600 Subject: [PATCH 3/4] Error with EINVAL on unsupported `whence` argument --- src/shims/fs.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shims/fs.rs b/src/shims/fs.rs index 4cfff9446b85..388117daee6b 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -285,7 +285,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } else if whence == this.eval_libc_i32("SEEK_END")? { SeekFrom::End(offset) } else { - throw_unsup_format!("Unsupported whence argument to lseek64: {}", whence) + let einval = this.eval_libc("EINVAL")?; + this.set_last_error(einval)?; + return Ok(-1); }; if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) { From 98a1cac4ef4cb766c64fb476fabf7d162dd30361 Mon Sep 17 00:00:00 2001 From: David Cook Date: Thu, 6 Feb 2020 17:50:33 -0600 Subject: [PATCH 4/4] Add tests to cover SEEK_CUR and SEEK_END --- tests/run-pass/fs.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/run-pass/fs.rs b/tests/run-pass/fs.rs index 4da67369aef8..73e6f37453fa 100644 --- a/tests/run-pass/fs.rs +++ b/tests/run-pass/fs.rs @@ -45,6 +45,17 @@ fn main() { let mut contents = Vec::new(); file.read_to_end(&mut contents).unwrap(); assert_eq!(bytes, contents.as_slice()); + // Test seeking relative to the end of the file. + file.seek(SeekFrom::End(-1)).unwrap(); + let mut contents = Vec::new(); + file.read_to_end(&mut contents).unwrap(); + assert_eq!(&bytes[bytes.len() - 1..], contents.as_slice()); + // Test seeking relative to the current position. + file.seek(SeekFrom::Start(5)).unwrap(); + file.seek(SeekFrom::Current(-3)).unwrap(); + let mut contents = Vec::new(); + file.read_to_end(&mut contents).unwrap(); + assert_eq!(&bytes[2..], contents.as_slice()); // Test that metadata of an absolute path is correct. test_metadata(bytes, &path).unwrap();