Skip to content
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

Closed
wants to merge 3 commits into from

Conversation

oli-obk
Copy link
Contributor

@oli-obk oli-obk commented Jul 30, 2024

Instead of having to repeat the conversion to Scalar everywhere.

@@ -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>(
Copy link
Member

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.

@@ -1434,3 +1447,35 @@ pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
u32::try_from(len).unwrap()
}
}

pub trait WriteIoResult {
Copy link
Member

Choose a reason for hiding this comment

The 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.

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))
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 i64s in. The read method already could not be ported before and was written out fully. I want to fix both at the same time

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> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this one still return Scalar?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It (and others) return error codes via this.eval_libc("EACCES"); without a corresponding ErrorKind existing in libcore. I plan to use an error enum that wraps io errors and has variants for all the eval_libc errors we use in miri. May turn out overkill, but I wanna see how it looks before discarding it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems easier to use Scalar as the universal type for what to store in errno -- we can already convert both io::Error and libc constants into Scalar.

@@ -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)?;
Copy link
Member

@RalfJung RalfJung Jul 30, 2024

Choose a reason for hiding this comment

The 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 -1 anywhere, that seems like a more promising approach to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 i64, i32, and i64 but is actually target usize, as that would require explicit type annotations and reliance on runtime checks (Scalar size vs destination size). I then thought it would be nicer to keep some semblence of result handling around, as now all the return types must match up again, like before moving to scalars. I'll finish this PR to its conclusion and open a separate PR for just using helpers everywhere so you can compare.

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()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For instance, this could become return Ok(this.return_io_error(ErrorKind::PermissionDenied.into())) where return_io_error sets the errno and then returns -1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, the type...

Another option might be to pass dest to these functions and not have them return anything. A function that blocks usually already needs to do that anyway. Then they can use your new write_io_result internally for when they naturally have an io::Result, and some write_io_error for cases like this where we hard-code an error.

@oli-obk
Copy link
Contributor Author

oli-obk commented Jul 31, 2024

I think the other scheme will allow us to better simplify things, so I'm closing this PR and will cherry-pick the parts that are useful

@oli-obk oli-obk closed this Jul 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants