Skip to content

Commit

Permalink
Add NtWriteFile to foreign_items
Browse files Browse the repository at this point in the history
Co-Authored-By: Ralf Jung <post@ralfj.de>
  • Loading branch information
ChrisDenton and RalfJung committed Oct 31, 2022
1 parent 6a00c5c commit 2dc419d
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 59 deletions.
61 changes: 2 additions & 59 deletions src/tools/miri/src/shims/windows/dlsym.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::foreign_items::nt_write_file;
use rustc_middle::mir;
use rustc_target::abi::Size;
use rustc_target::spec::abi::Abi;

use log::trace;
Expand Down Expand Up @@ -56,64 +56,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
);
}

let [
handle,
_event,
_apc_routine,
_apc_context,
io_status_block,
buf,
n,
byte_offset,
_key,
] = check_arg_count(args)?;
let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
let buf = this.read_pointer(buf)?;
let n = this.read_scalar(n)?.to_u32()?;
let byte_offset = this.read_scalar(byte_offset)?.to_machine_usize(this)?; // is actually a pointer
let io_status_block = this.deref_operand(io_status_block)?;

if byte_offset != 0 {
throw_unsup_format!(
"`NtWriteFile` `ByteOffset` paremeter is non-null, which is unsupported"
);
}

let written = if handle == -11 || handle == -12 {
// stdout/stderr
use std::io::{self, Write};

let buf_cont =
this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
let res = if this.machine.mute_stdout_stderr {
Ok(buf_cont.len())
} else if handle == -11 {
io::stdout().write(buf_cont)
} else {
io::stderr().write(buf_cont)
};
// We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
res.ok().map(|n| u32::try_from(n).unwrap())
} else {
throw_unsup_format!(
"on Windows, writing to anything except stdout/stderr is not supported"
)
};
// We have to put the result into io_status_block.
if let Some(n) = written {
let io_status_information =
this.mplace_field_named(&io_status_block, "Information")?;
this.write_scalar(
Scalar::from_machine_usize(n.into(), this),
&io_status_information.into(),
)?;
}
// Return whether this was a success. >= 0 is success.
// For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
this.write_scalar(
Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }),
dest,
)?;
nt_write_file(this, args, dest)?;
}
Dlsym::SetThreadDescription => {
let [handle, name] = check_arg_count(args)?;
Expand Down
61 changes: 61 additions & 0 deletions src/tools/miri/src/shims/windows/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use rustc_span::Symbol;
use rustc_target::abi::Size;
use rustc_target::spec::abi::Abi;

use crate::helpers::check_arg_count;
use crate::*;
use shims::foreign_items::EmulateByNameResult;
use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
Expand Down Expand Up @@ -453,10 +454,70 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// FIXME: this should return a nonzero value if this call does result in switching to another thread.
this.write_null(dest)?;
}
"NtWriteFile" if this.frame_in_std() => {
this.check_abi_and_shim_symbol_clash(
abi,
Abi::System { unwind: false },
link_name,
)?;
nt_write_file(this, args, dest)?;
}

_ => return Ok(EmulateByNameResult::NotSupported),
}

Ok(EmulateByNameResult::NeedsJumping)
}
}

// Incomplete implementation of `NtWriteFile`.
pub(super) fn nt_write_file<'a, 'mir, 'tcx>(
this: &'a mut MiriInterpCx<'mir, 'tcx>,
args: &[OpTy<'tcx, Provenance>],
dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ()> {
let [handle, _event, _apc_routine, _apc_context, io_status_block, buf, n, byte_offset, _key] =
check_arg_count(args)?;

let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
let buf = this.read_pointer(buf)?;
let n = this.read_scalar(n)?.to_u32()?;
let byte_offset = this.read_scalar(byte_offset)?.to_machine_usize(this)?; // is actually a pointer
let io_status_block = this.deref_operand(io_status_block)?;

if byte_offset != 0 {
throw_unsup_format!(
"`NtWriteFile` `ByteOffset` paremeter is non-null, which is unsupported"
);
}

let written = if handle == -11 || handle == -12 {
// stdout/stderr
use std::io::{self, Write};

let buf_cont = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
let res = if this.machine.mute_stdout_stderr {
Ok(buf_cont.len())
} else if handle == -11 {
io::stdout().write(buf_cont)
} else {
io::stderr().write(buf_cont)
};
// We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
res.ok().map(|n| u32::try_from(n).unwrap())
} else {
throw_unsup_format!("on Windows, writing to anything except stdout/stderr is not supported")
};
// We have to put the result into io_status_block.
if let Some(n) = written {
let io_status_information = this.mplace_field_named(&io_status_block, "Information")?;
this.write_scalar(
Scalar::from_machine_usize(n.into(), this),
&io_status_information.into(),
)?;
}
// Return whether this was a success. >= 0 is success.
// For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
this.write_scalar(Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }), dest)?;
Ok(())
}

0 comments on commit 2dc419d

Please sign in to comment.