Skip to content

Commit 0cf3e9f

Browse files
committed
Auto merge of #1497 - samrat:posix-fs-refactor, r=oli-obk
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` The `FileDescriptor` trait was added in PR #1495 Closes #1486
2 parents 69d050f + bdef57e commit 0cf3e9f

File tree

4 files changed

+107
-73
lines changed

4 files changed

+107
-73
lines changed

src/shims/posix/foreign_items.rs

Lines changed: 2 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
use std::convert::TryFrom;
2-
use std::io::{self, Read, Write};
3-
41
use log::trace;
52

63
use rustc_middle::mir;
@@ -67,43 +64,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
6764
let fd = this.read_scalar(fd)?.to_i32()?;
6865
let buf = this.read_scalar(buf)?.check_init()?;
6966
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-
};
67+
let result = this.read(fd, buf, count)?;
10768
this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
10869
}
10970
"write" => {
@@ -112,35 +73,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11273
let buf = this.read_scalar(buf)?.check_init()?;
11374
let count = this.read_scalar(n)?.to_machine_usize(this)?;
11475
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-
};
76+
let result = this.write(fd, buf, count)?;
14477
// Now, `result` is the value we return back to the program.
14578
this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
14679
}

src/shims/posix/fs.rs

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,85 @@ 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+
let result = Write::write(self, bytes);
80+
// Stdout is buffered, flush to make sure it appears on the
81+
// screen. This is the write() syscall of the interpreted
82+
// program, we want it to correspond to a write() syscall on
83+
// the host -- there is no good in adding extra buffering
84+
// here.
85+
io::stdout().flush().unwrap();
86+
87+
Ok(result)
88+
}
89+
90+
fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
91+
throw_unsup_format!("cannot seek on stdout");
92+
}
93+
}
94+
95+
impl<'tcx> FileDescriptor<'tcx> for io::Stderr {
96+
fn as_file_handle(&self) -> InterpResult<'tcx, &FileHandle> {
97+
throw_unsup_format!("stdout cannot be used as FileHandle");
98+
}
99+
100+
fn read(&mut self, _bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>> {
101+
throw_unsup_format!("cannot read from stderr");
102+
}
103+
104+
fn write(&mut self, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
105+
Ok(Write::write(self, bytes))
106+
}
107+
108+
fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
109+
throw_unsup_format!("cannot seek on stderr");
110+
}
111+
}
112+
113+
#[derive(Debug)]
52114
pub struct FileHandler<'tcx> {
53115
handles: BTreeMap<i32, Box<dyn FileDescriptor<'tcx>>>,
54116
}
55117

118+
impl<'tcx> Default for FileHandler<'tcx> {
119+
fn default() -> Self {
120+
let mut handles = BTreeMap::new();
121+
handles.insert(0i32, Box::new(io::stdin()) as Box<dyn FileDescriptor<'_>>);
122+
handles.insert(1i32, Box::new(io::stdout()) as Box<dyn FileDescriptor<'_>>);
123+
handles.insert(2i32, Box::new(io::stderr()) as Box<dyn FileDescriptor<'_>>);
124+
FileHandler {
125+
handles
126+
}
127+
}
128+
}
129+
56130

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

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

490563
trace!("Reading from FD {}, size {}", fd, count);
491564

@@ -537,8 +610,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
537610
) -> InterpResult<'tcx, i64> {
538611
let this = self.eval_context_mut();
539612

540-
this.check_no_isolation("write")?;
541-
assert!(fd >= 3);
613+
if fd >= 3 {
614+
this.check_no_isolation("write")?;
615+
}
542616

543617
// Check that the *entire* buffer is actually valid memory.
544618
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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// ignore-windows: No libc on Windows
2+
3+
#![feature(rustc_private)]
4+
5+
extern crate libc;
6+
7+
fn main() -> std::io::Result<()> {
8+
let bytes = b"hello";
9+
unsafe {
10+
libc::write(0, bytes.as_ptr() as *const libc::c_void, 5); //~ ERROR cannot write to stdin
11+
}
12+
Ok(())
13+
}

0 commit comments

Comments
 (0)