-
Notifications
You must be signed in to change notification settings - Fork 347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a helper for directly writing io::Result
to places
#3777
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
use std::cmp; | ||
use std::collections::BTreeSet; | ||
use std::iter; | ||
use std::num::NonZero; | ||
use std::sync::Mutex; | ||
use std::time::Duration; | ||
use std::{cmp, io}; | ||
|
||
use rand::RngCore; | ||
|
||
|
@@ -857,19 +857,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
/// | ||
/// This function uses `T: From<i32>` instead of `i32` directly because some IO related | ||
/// functions return different integer types (like `read`, that returns an `i64`). | ||
fn try_unwrap_io_result<T: From<i32>>( | ||
fn try_unwrap_io_result<T: WriteIoResult>( | ||
&mut self, | ||
result: std::io::Result<T>, | ||
) -> InterpResult<'tcx, T> { | ||
) -> InterpResult<'tcx, Scalar> { | ||
match result { | ||
Ok(ok) => Ok(ok), | ||
Ok(ok) => Ok(ok.into_scalar()), | ||
Err(e) => { | ||
self.eval_context_mut().set_last_error_from_io_error(e)?; | ||
Ok((-1).into()) | ||
self.set_last_error_from_io_error(e)?; | ||
Ok(T::neg_one()) | ||
} | ||
} | ||
} | ||
|
||
/// Write an io result to a place. | ||
/// No error means writing via `WriteIoResult` | ||
/// Otherwise write `-1` and set the last error. | ||
#[inline(always)] | ||
fn write_io_result<T: WriteIoResult>( | ||
&mut self, | ||
val: io::Result<T>, | ||
dest: &impl Writeable<'tcx, Provenance>, | ||
) -> InterpResult<'tcx> { | ||
let result = self.try_unwrap_io_result(val)?; | ||
self.eval_context_mut().write_scalar(result, dest) | ||
} | ||
|
||
/// Dereference a pointer operand to a place using `layout` instead of the pointer's declared type | ||
fn deref_pointer_as( | ||
&self, | ||
|
@@ -1434,3 +1447,35 @@ pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 { | |
u32::try_from(len).unwrap() | ||
} | ||
} | ||
|
||
pub trait WriteIoResult { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a doc comment. Also the trait name is kind of confusing, maybe a doc comment will help coming up with a better one. |
||
fn into_scalar(self) -> Scalar; | ||
fn neg_one() -> Scalar; | ||
} | ||
|
||
impl WriteIoResult for () { | ||
fn into_scalar(self) -> Scalar { | ||
Scalar::from_i32(0) | ||
} | ||
fn neg_one() -> Scalar { | ||
Scalar::from_i32(-1) | ||
} | ||
} | ||
|
||
impl WriteIoResult for i32 { | ||
fn into_scalar(self) -> Scalar { | ||
Scalar::from_i32(self) | ||
} | ||
fn neg_one() -> Scalar { | ||
Scalar::from_i32(-1) | ||
} | ||
} | ||
|
||
impl WriteIoResult for i64 { | ||
fn into_scalar(self) -> Scalar { | ||
Scalar::from_i64(self) | ||
} | ||
fn neg_one() -> Scalar { | ||
Scalar::from_i64(-1) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -366,8 +366,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
let result = file_descriptor.flock(this.machine.communicate(), parsed_op)?; | ||
drop(file_descriptor); | ||
// return `0` if flock is successful | ||
let result = result.map(|()| 0i32); | ||
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) | ||
this.try_unwrap_io_result(result) | ||
} | ||
|
||
fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { | ||
|
@@ -438,7 +437,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
let result = file_description.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)?)) | ||
this.try_unwrap_io_result(result) | ||
} | ||
|
||
/// Function used when a file descriptor does not exist. It returns `Ok(-1)`and sets | ||
|
@@ -564,7 +563,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
drop(fd); | ||
|
||
let result = result?.map(|c| i64::try_from(c).unwrap()); | ||
Ok(Scalar::from_target_isize(this.try_unwrap_io_result(result)?, this)) | ||
match result { | ||
Ok(written_bytes) => Ok(Scalar::from_target_isize(written_bytes, this)), | ||
Err(e) => { | ||
this.set_last_error_from_io_error(e)?; | ||
Ok(Scalar::from_target_isize(-1, this)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code got worse than it used to be, what's happening here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are writing target sized integers, so i'll need to add a helper type to wrap the |
||
} | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,7 +78,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
"chdir" => { | ||
let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; | ||
let result = this.chdir(path)?; | ||
this.write_scalar(result, dest)?; | ||
this.write_io_result(result, dest)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it really such a big win to move the io::Result handling out of the shim? A priori I think I'd prefer the version that returns a Scalar, as that matches the C function we're implementing and since it is a pattern we can use for all functions, not just IO functions. We can have more helpers inside those functions that make them easier to write and avoid hard-coding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I initially wanted to add such helpers, but then it got annoying for handling |
||
} | ||
"getpid" => { | ||
let [] = this.check_shim(abi, Abi::C { unwind: false}, link_name, args)?; | ||
|
@@ -188,12 +188,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
"unlink" => { | ||
let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; | ||
let result = this.unlink(path)?; | ||
this.write_scalar(result, dest)?; | ||
this.write_io_result(result, dest)?; | ||
} | ||
"symlink" => { | ||
let [target, linkpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; | ||
let result = this.symlink(target, linkpath)?; | ||
this.write_scalar(result, dest)?; | ||
this.write_io_result(result, dest)?; | ||
} | ||
"rename" => { | ||
let [oldpath, newpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; | ||
|
@@ -203,12 +203,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
"mkdir" => { | ||
let [path, mode] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; | ||
let result = this.mkdir(path, mode)?; | ||
this.write_scalar(result, dest)?; | ||
this.write_io_result(result, dest)?; | ||
} | ||
"rmdir" => { | ||
let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; | ||
let result = this.rmdir(path)?; | ||
this.write_scalar(result, dest)?; | ||
this.write_io_result(result, dest)?; | ||
} | ||
"opendir" => { | ||
let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -547,7 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
.open(path) | ||
.map(|file| this.machine.fds.insert_fd(FileHandle { file, writable })); | ||
|
||
Ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?)) | ||
this.try_unwrap_io_result(fd) | ||
} | ||
|
||
fn lseek64(&mut self, fd: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> { | ||
|
@@ -584,31 +584,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
.map(|offset| i64::try_from(offset).unwrap()); | ||
drop(file_description); | ||
|
||
let result = this.try_unwrap_io_result(result)?; | ||
Ok(Scalar::from_i64(result)) | ||
this.try_unwrap_io_result(result) | ||
} | ||
|
||
fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { | ||
fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, io::Result<()>> { | ||
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)?; | ||
this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; | ||
return Ok(Scalar::from_i32(-1)); | ||
return Ok(Err(ErrorKind::PermissionDenied.into())); | ||
} | ||
|
||
let result = remove_file(path).map(|_| 0); | ||
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) | ||
Ok(remove_file(path)) | ||
} | ||
|
||
fn symlink( | ||
&mut self, | ||
target_op: &OpTy<'tcx>, | ||
linkpath_op: &OpTy<'tcx>, | ||
) -> InterpResult<'tcx, Scalar> { | ||
) -> InterpResult<'tcx, io::Result<()>> { | ||
#[cfg(unix)] | ||
fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> { | ||
std::os::unix::fs::symlink(src, dst) | ||
|
@@ -627,12 +624,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
// 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.into())?; | ||
return Ok(Scalar::from_i32(-1)); | ||
return Ok(Err(ErrorKind::PermissionDenied.into())); | ||
} | ||
|
||
let result = create_link(&target, &linkpath).map(|_| 0); | ||
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) | ||
Ok(create_link(&target, &linkpath)) | ||
} | ||
|
||
fn macos_fbsd_stat( | ||
|
@@ -934,10 +929,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
|
||
let result = rename(oldpath, newpath).map(|_| 0); | ||
|
||
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) | ||
this.try_unwrap_io_result(result) | ||
} | ||
|
||
fn mkdir(&mut self, path_op: &OpTy<'tcx>, mode_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { | ||
fn mkdir( | ||
&mut self, | ||
path_op: &OpTy<'tcx>, | ||
mode_op: &OpTy<'tcx>, | ||
) -> InterpResult<'tcx, io::Result<()>> { | ||
let this = self.eval_context_mut(); | ||
|
||
#[cfg_attr(not(unix), allow(unused_variables))] | ||
|
@@ -952,8 +951,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
// 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.into())?; | ||
return Ok(Scalar::from_i32(-1)); | ||
return Ok(Err(ErrorKind::PermissionDenied.into())); | ||
} | ||
|
||
#[cfg_attr(not(unix), allow(unused_mut))] | ||
|
@@ -967,26 +965,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
builder.mode(mode); | ||
} | ||
|
||
let result = builder.create(path).map(|_| 0i32); | ||
|
||
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) | ||
Ok(builder.create(path)) | ||
} | ||
|
||
fn rmdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { | ||
fn rmdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, io::Result<()>> { | ||
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)?; | ||
this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; | ||
return Ok(Scalar::from_i32(-1)); | ||
return Ok(Err(ErrorKind::PermissionDenied.into())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For instance, this could become There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, but I'd still need to say how big the scalar is supposed to be. Not the end of the world, but also not nice There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah right, the type... Another option might be to pass |
||
} | ||
|
||
let result = remove_dir(path).map(|_| 0i32); | ||
|
||
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) | ||
Ok(remove_dir(path)) | ||
} | ||
|
||
fn opendir(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this one still return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It (and others) return error codes via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems easier to use |
||
|
@@ -1280,8 +1273,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
if let Ok(length) = length.try_into() { | ||
let result = file.set_len(length); | ||
drop(file_description); | ||
let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; | ||
Ok(Scalar::from_i32(result)) | ||
this.try_unwrap_io_result(result) | ||
} else { | ||
drop(file_description); | ||
let einval = this.eval_libc("EINVAL"); | ||
|
@@ -1329,7 +1321,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
})?; | ||
let io_result = maybe_sync_file(file, *writable, File::sync_all); | ||
drop(file_description); | ||
Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) | ||
this.try_unwrap_io_result(io_result) | ||
} | ||
|
||
fn fdatasync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { | ||
|
@@ -1354,7 +1346,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
})?; | ||
let io_result = maybe_sync_file(file, *writable, File::sync_data); | ||
drop(file_description); | ||
Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) | ||
this.try_unwrap_io_result(io_result) | ||
} | ||
|
||
fn sync_file_range( | ||
|
@@ -1404,7 +1396,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
})?; | ||
let io_result = maybe_sync_file(file, *writable, File::sync_data); | ||
drop(file_description); | ||
Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) | ||
this.try_unwrap_io_result(io_result) | ||
} | ||
|
||
fn readlink( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The doc comment above is outdated now.