Skip to content

Commit 2dc419d

Browse files
ChrisDentonRalfJung
andcommitted
Add NtWriteFile to foreign_items
Co-Authored-By: Ralf Jung <post@ralfj.de>
1 parent 6a00c5c commit 2dc419d

File tree

2 files changed

+63
-59
lines changed

2 files changed

+63
-59
lines changed

src/tools/miri/src/shims/windows/dlsym.rs

+2-59
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
use super::foreign_items::nt_write_file;
12
use rustc_middle::mir;
2-
use rustc_target::abi::Size;
33
use rustc_target::spec::abi::Abi;
44

55
use log::trace;
@@ -56,64 +56,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
5656
);
5757
}
5858

59-
let [
60-
handle,
61-
_event,
62-
_apc_routine,
63-
_apc_context,
64-
io_status_block,
65-
buf,
66-
n,
67-
byte_offset,
68-
_key,
69-
] = check_arg_count(args)?;
70-
let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
71-
let buf = this.read_pointer(buf)?;
72-
let n = this.read_scalar(n)?.to_u32()?;
73-
let byte_offset = this.read_scalar(byte_offset)?.to_machine_usize(this)?; // is actually a pointer
74-
let io_status_block = this.deref_operand(io_status_block)?;
75-
76-
if byte_offset != 0 {
77-
throw_unsup_format!(
78-
"`NtWriteFile` `ByteOffset` paremeter is non-null, which is unsupported"
79-
);
80-
}
81-
82-
let written = if handle == -11 || handle == -12 {
83-
// stdout/stderr
84-
use std::io::{self, Write};
85-
86-
let buf_cont =
87-
this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
88-
let res = if this.machine.mute_stdout_stderr {
89-
Ok(buf_cont.len())
90-
} else if handle == -11 {
91-
io::stdout().write(buf_cont)
92-
} else {
93-
io::stderr().write(buf_cont)
94-
};
95-
// We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
96-
res.ok().map(|n| u32::try_from(n).unwrap())
97-
} else {
98-
throw_unsup_format!(
99-
"on Windows, writing to anything except stdout/stderr is not supported"
100-
)
101-
};
102-
// We have to put the result into io_status_block.
103-
if let Some(n) = written {
104-
let io_status_information =
105-
this.mplace_field_named(&io_status_block, "Information")?;
106-
this.write_scalar(
107-
Scalar::from_machine_usize(n.into(), this),
108-
&io_status_information.into(),
109-
)?;
110-
}
111-
// Return whether this was a success. >= 0 is success.
112-
// For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
113-
this.write_scalar(
114-
Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }),
115-
dest,
116-
)?;
59+
nt_write_file(this, args, dest)?;
11760
}
11861
Dlsym::SetThreadDescription => {
11962
let [handle, name] = check_arg_count(args)?;

src/tools/miri/src/shims/windows/foreign_items.rs

+61
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_span::Symbol;
44
use rustc_target::abi::Size;
55
use rustc_target::spec::abi::Abi;
66

7+
use crate::helpers::check_arg_count;
78
use crate::*;
89
use shims::foreign_items::EmulateByNameResult;
910
use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
@@ -453,10 +454,70 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
453454
// FIXME: this should return a nonzero value if this call does result in switching to another thread.
454455
this.write_null(dest)?;
455456
}
457+
"NtWriteFile" if this.frame_in_std() => {
458+
this.check_abi_and_shim_symbol_clash(
459+
abi,
460+
Abi::System { unwind: false },
461+
link_name,
462+
)?;
463+
nt_write_file(this, args, dest)?;
464+
}
456465

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

460469
Ok(EmulateByNameResult::NeedsJumping)
461470
}
462471
}
472+
473+
// Incomplete implementation of `NtWriteFile`.
474+
pub(super) fn nt_write_file<'a, 'mir, 'tcx>(
475+
this: &'a mut MiriInterpCx<'mir, 'tcx>,
476+
args: &[OpTy<'tcx, Provenance>],
477+
dest: &PlaceTy<'tcx, Provenance>,
478+
) -> InterpResult<'tcx, ()> {
479+
let [handle, _event, _apc_routine, _apc_context, io_status_block, buf, n, byte_offset, _key] =
480+
check_arg_count(args)?;
481+
482+
let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
483+
let buf = this.read_pointer(buf)?;
484+
let n = this.read_scalar(n)?.to_u32()?;
485+
let byte_offset = this.read_scalar(byte_offset)?.to_machine_usize(this)?; // is actually a pointer
486+
let io_status_block = this.deref_operand(io_status_block)?;
487+
488+
if byte_offset != 0 {
489+
throw_unsup_format!(
490+
"`NtWriteFile` `ByteOffset` paremeter is non-null, which is unsupported"
491+
);
492+
}
493+
494+
let written = if handle == -11 || handle == -12 {
495+
// stdout/stderr
496+
use std::io::{self, Write};
497+
498+
let buf_cont = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
499+
let res = if this.machine.mute_stdout_stderr {
500+
Ok(buf_cont.len())
501+
} else if handle == -11 {
502+
io::stdout().write(buf_cont)
503+
} else {
504+
io::stderr().write(buf_cont)
505+
};
506+
// We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
507+
res.ok().map(|n| u32::try_from(n).unwrap())
508+
} else {
509+
throw_unsup_format!("on Windows, writing to anything except stdout/stderr is not supported")
510+
};
511+
// We have to put the result into io_status_block.
512+
if let Some(n) = written {
513+
let io_status_information = this.mplace_field_named(&io_status_block, "Information")?;
514+
this.write_scalar(
515+
Scalar::from_machine_usize(n.into(), this),
516+
&io_status_information.into(),
517+
)?;
518+
}
519+
// Return whether this was a success. >= 0 is success.
520+
// For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
521+
this.write_scalar(Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }), dest)?;
522+
Ok(())
523+
}

0 commit comments

Comments
 (0)