Skip to content

Commit bea7113

Browse files
committed
Add impl FileDescriptor for stdin, stdout, stderr
- Use `FileDescriptor::read` for stdin reads - Use `FileDescriptor::write` for stdout/err writes - Handle stdout/err reads in `FileDescriptor::read`
1 parent 69d050f commit bea7113

File tree

4 files changed

+104
-72
lines changed

4 files changed

+104
-72
lines changed

src/shims/posix/foreign_items.rs

Lines changed: 6 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use std::convert::TryFrom;
2-
use std::io::{self, Read, Write};
1+
use std::io::{self, Write};
32

43
use log::trace;
54

@@ -67,43 +66,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
6766
let fd = this.read_scalar(fd)?.to_i32()?;
6867
let buf = this.read_scalar(buf)?.check_init()?;
6968
let count = this.read_scalar(count)?.to_machine_usize(this)?;
70-
let result = if fd == 0 {
71-
72-
this.check_no_isolation("read")?;
73-
74-
// We cap the number of read bytes to the largest
75-
// value that we are able to fit in both the
76-
// host's and target's `isize`. This saves us from
77-
// having to handle overflows later.
78-
let count = count.min(this.machine_isize_max() as u64).min(isize::MAX as u64);
79-
80-
// We want to read at most `count` bytes. We are
81-
// sure that `count` is not negative because it
82-
// was a target's `usize`. Also we are sure that
83-
// its smaller than `usize::MAX` because it is a
84-
// host's `isize`.
85-
let mut buffer = vec![0; count as usize];
86-
let res = io::stdin()
87-
.read(&mut buffer)
88-
// `Stdin::read` never returns a value larger
89-
// than `count`, so this cannot fail.
90-
.map(|c| i64::try_from(c).unwrap());
91-
92-
match res {
93-
Ok(bytes) => {
94-
this.memory.write_bytes(buf, buffer)?;
95-
i64::try_from(bytes).unwrap()
96-
},
97-
Err(e) => {
98-
this.set_last_error_from_io_error(e)?;
99-
-1
100-
},
101-
}
102-
} else if fd == 1 || fd == 2 {
103-
throw_unsup_format!("cannot read from stdout/stderr")
104-
} else {
105-
this.read(fd, buf, count)?
106-
};
69+
let result = this.read(fd, buf, count)?;
10770
this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
10871
}
10972
"write" => {
@@ -112,35 +75,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11275
let buf = this.read_scalar(buf)?.check_init()?;
11376
let count = this.read_scalar(n)?.to_machine_usize(this)?;
11477
trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
115-
let result = if fd == 0 {
116-
throw_unsup_format!("cannot write to stdin")
117-
} else if fd == 1 || fd == 2 {
118-
// stdout/stderr
119-
120-
let buf_cont = this.memory.read_bytes(buf, Size::from_bytes(count))?;
121-
// We need to flush to make sure this actually appears on the screen
122-
let res = if fd == 1 {
123-
// Stdout is buffered, flush to make sure it appears on the screen.
124-
// This is the write() syscall of the interpreted program, we want it
125-
// to correspond to a write() syscall on the host -- there is no good
126-
// in adding extra buffering here.
127-
let res = io::stdout().write(buf_cont);
128-
io::stdout().flush().unwrap();
129-
res
130-
} else {
131-
// No need to flush, stderr is not buffered.
132-
io::stderr().write(buf_cont)
133-
};
134-
match res {
135-
Ok(n) => i64::try_from(n).unwrap(),
136-
Err(e) => {
137-
this.set_last_error_from_io_error(e)?;
138-
-1
139-
}
140-
}
141-
} else {
142-
this.write(fd, buf, count)?
143-
};
78+
let result = this.write(fd, buf, count)?;
79+
if fd == 1 {
80+
io::stdout().flush().unwrap();
81+
}
14482
// Now, `result` is the value we return back to the program.
14583
this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
14684
}

src/shims/posix/fs.rs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,77 @@ impl<'tcx> FileDescriptor<'tcx> for FileHandle {
4848
}
4949
}
5050

51-
#[derive(Debug, Default)]
51+
impl<'tcx> FileDescriptor<'tcx> for io::Stdin {
52+
fn as_file_handle(&self) -> InterpResult<'tcx, &FileHandle> {
53+
throw_unsup_format!("stdin cannot be used as FileHandle");
54+
}
55+
56+
fn read(&mut self, bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>> {
57+
Ok(Read::read(self, bytes))
58+
}
59+
60+
fn write(&mut self, _bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
61+
throw_unsup_format!("cannot write to stdin");
62+
}
63+
64+
fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
65+
throw_unsup_format!("cannot seek on stdin");
66+
}
67+
}
68+
69+
impl<'tcx> FileDescriptor<'tcx> for io::Stdout {
70+
fn as_file_handle(&self) -> InterpResult<'tcx, &FileHandle> {
71+
throw_unsup_format!("stdout cannot be used as FileHandle");
72+
}
73+
74+
fn read(&mut self, _bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>> {
75+
throw_unsup_format!("cannot read from stdout");
76+
}
77+
78+
fn write(&mut self, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
79+
Ok(Write::write(self, bytes))
80+
}
81+
82+
fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
83+
throw_unsup_format!("cannot seek on stdout");
84+
}
85+
}
86+
87+
impl<'tcx> FileDescriptor<'tcx> for io::Stderr {
88+
fn as_file_handle(&self) -> InterpResult<'tcx, &FileHandle> {
89+
throw_unsup_format!("stdout cannot be used as FileHandle");
90+
}
91+
92+
fn read(&mut self, _bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>> {
93+
throw_unsup_format!("cannot read from stderr");
94+
}
95+
96+
fn write(&mut self, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
97+
Ok(Write::write(self, bytes))
98+
}
99+
100+
fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
101+
throw_unsup_format!("cannot seek on stderr");
102+
}
103+
}
104+
105+
#[derive(Debug)]
52106
pub struct FileHandler<'tcx> {
53107
handles: BTreeMap<i32, Box<dyn FileDescriptor<'tcx>>>,
54108
}
55109

110+
impl<'tcx> Default for FileHandler<'tcx> {
111+
fn default() -> Self {
112+
let mut handles = BTreeMap::new();
113+
handles.insert(0i32, Box::new(io::stdin()) as Box<dyn FileDescriptor<'_>>);
114+
handles.insert(1i32, Box::new(io::stdout()) as Box<dyn FileDescriptor<'_>>);
115+
handles.insert(2i32, Box::new(io::stderr()) as Box<dyn FileDescriptor<'_>>);
116+
FileHandler {
117+
handles
118+
}
119+
}
120+
}
121+
56122

57123
// fd numbers 0, 1, and 2 are reserved for stdin, stdout, and stderr
58124
const MIN_NORMAL_FILE_FD: i32 = 3;
@@ -485,7 +551,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
485551
let this = self.eval_context_mut();
486552

487553
this.check_no_isolation("read")?;
488-
assert!(fd >= 3);
489554

490555
trace!("Reading from FD {}, size {}", fd, count);
491556

@@ -537,8 +602,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
537602
) -> InterpResult<'tcx, i64> {
538603
let this = self.eval_context_mut();
539604

540-
this.check_no_isolation("write")?;
541-
assert!(fd >= 3);
605+
if fd >= 3 {
606+
this.check_no_isolation("write")?;
607+
}
542608

543609
// Check that the *entire* buffer is actually valid memory.
544610
this.memory.check_ptr_access(
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// compile-flags: -Zmiri-disable-isolation
2+
// ignore-windows: No libc on Windows
3+
4+
#![feature(rustc_private)]
5+
6+
extern crate libc;
7+
8+
fn main() -> std::io::Result<()> {
9+
let mut bytes = [0u8; 512];
10+
unsafe {
11+
libc::read(1, bytes.as_mut_ptr() as *mut libc::c_void, 512); //~ ERROR cannot read from stdout
12+
}
13+
Ok(())
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// compile-flags: -Zmiri-disable-isolation
2+
// ignore-windows: No libc on Windows
3+
4+
#![feature(rustc_private)]
5+
6+
extern crate libc;
7+
8+
fn main() -> std::io::Result<()> {
9+
let bytes = b"hello";
10+
unsafe {
11+
libc::write(0, bytes.as_ptr() as *const libc::c_void, 5); //~ ERROR cannot write to stdin
12+
}
13+
Ok(())
14+
}

0 commit comments

Comments
 (0)