1
1
use std::any::Any;
2
2
use std::collections::BTreeMap;
3
- use std::io::{IsTerminal, Read, SeekFrom, Write};
3
+ use std::io::{IsTerminal, SeekFrom, Write};
4
4
use std::marker::CoercePointee;
5
5
use std::ops::Deref;
6
6
use std::rc::{Rc, Weak};
@@ -140,8 +140,8 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
140
140
_communicate_allowed: bool,
141
141
_ptr: Pointer,
142
142
_len: usize,
143
- _dest: &MPlaceTy<'tcx>,
144
143
_ecx: &mut MiriInterpCx<'tcx>,
144
+ _finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
145
145
) -> InterpResult<'tcx> {
146
146
throw_unsup_format!("cannot read from {}", self.name());
147
147
}
@@ -154,8 +154,8 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
154
154
_communicate_allowed: bool,
155
155
_ptr: Pointer,
156
156
_len: usize,
157
- _dest: &MPlaceTy<'tcx>,
158
157
_ecx: &mut MiriInterpCx<'tcx>,
158
+ _finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
159
159
) -> InterpResult<'tcx> {
160
160
throw_unsup_format!("cannot write to {}", self.name());
161
161
}
@@ -207,19 +207,16 @@ impl FileDescription for io::Stdin {
207
207
communicate_allowed: bool,
208
208
ptr: Pointer,
209
209
len: usize,
210
- dest: &MPlaceTy<'tcx>,
211
210
ecx: &mut MiriInterpCx<'tcx>,
211
+ finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
212
212
) -> InterpResult<'tcx> {
213
- let mut bytes = vec![0; len];
214
213
if !communicate_allowed {
215
214
// We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
216
215
helpers::isolation_abort_error("`read` from stdin")?;
217
216
}
218
- let result = Read::read(&mut &*self, &mut bytes);
219
- match result {
220
- Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest),
221
- Err(e) => ecx.set_last_error_and_return(e, dest),
222
- }
217
+
218
+ let result = ecx.read_from_host(&*self, len, ptr)?;
219
+ finish.call(ecx, result)
223
220
}
224
221
225
222
fn is_tty(&self, communicate_allowed: bool) -> bool {
@@ -237,22 +234,19 @@ impl FileDescription for io::Stdout {
237
234
_communicate_allowed: bool,
238
235
ptr: Pointer,
239
236
len: usize,
240
- dest: &MPlaceTy<'tcx>,
241
237
ecx: &mut MiriInterpCx<'tcx>,
238
+ finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
242
239
) -> InterpResult<'tcx> {
243
- let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
244
- // We allow writing to stderr even with isolation enabled.
245
- let result = Write::write(&mut &*self, bytes);
240
+ // We allow writing to stdout even with isolation enabled.
241
+ let result = ecx.write_to_host(&*self, len, ptr)?;
246
242
// Stdout is buffered, flush to make sure it appears on the
247
243
// screen. This is the write() syscall of the interpreted
248
244
// program, we want it to correspond to a write() syscall on
249
245
// the host -- there is no good in adding extra buffering
250
246
// here.
251
247
io::stdout().flush().unwrap();
252
- match result {
253
- Ok(write_size) => ecx.return_write_success(write_size, dest),
254
- Err(e) => ecx.set_last_error_and_return(e, dest),
255
- }
248
+
249
+ finish.call(ecx, result)
256
250
}
257
251
258
252
fn is_tty(&self, communicate_allowed: bool) -> bool {
@@ -270,17 +264,13 @@ impl FileDescription for io::Stderr {
270
264
_communicate_allowed: bool,
271
265
ptr: Pointer,
272
266
len: usize,
273
- dest: &MPlaceTy<'tcx>,
274
267
ecx: &mut MiriInterpCx<'tcx>,
268
+ finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
275
269
) -> InterpResult<'tcx> {
276
- let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
277
270
// We allow writing to stderr even with isolation enabled.
271
+ let result = ecx.write_to_host(&*self, len, ptr)?;
278
272
// No need to flush, stderr is not buffered.
279
- let result = Write::write(&mut &*self, bytes);
280
- match result {
281
- Ok(write_size) => ecx.return_write_success(write_size, dest),
282
- Err(e) => ecx.set_last_error_and_return(e, dest),
283
- }
273
+ finish.call(ecx, result)
284
274
}
285
275
286
276
fn is_tty(&self, communicate_allowed: bool) -> bool {
@@ -302,11 +292,11 @@ impl FileDescription for NullOutput {
302
292
_communicate_allowed: bool,
303
293
_ptr: Pointer,
304
294
len: usize,
305
- dest: &MPlaceTy<'tcx>,
306
295
ecx: &mut MiriInterpCx<'tcx>,
296
+ finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
307
297
) -> InterpResult<'tcx> {
308
298
// We just don't write anything, but report to the user that we did.
309
- ecx.return_write_success(len, dest )
299
+ finish.call(ecx, Ok(len) )
310
300
}
311
301
}
312
302
@@ -405,40 +395,41 @@ impl FdTable {
405
395
406
396
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
407
397
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
408
- /// Helper to implement `FileDescription::read`:
409
- /// This is only used when `read` is successful.
410
- /// `actual_read_size` should be the return value of some underlying `read` call that used
411
- /// `bytes` as its output buffer.
412
- /// The length of `bytes` must not exceed either the host's or the target's `isize`.
413
- /// `bytes` is written to `buf` and the size is written to `dest`.
414
- fn return_read_success(
398
+ /// Read data from a host `Read` type, store the result into machine memory,
399
+ /// and return whether that worked.
400
+ fn read_from_host(
415
401
&mut self,
416
- buf: Pointer,
417
- bytes: &[u8],
418
- actual_read_size: usize,
419
- dest: &MPlaceTy<'tcx>,
420
- ) -> InterpResult<'tcx> {
402
+ mut file: impl io::Read,
403
+ len: usize,
404
+ ptr: Pointer,
405
+ ) -> InterpResult<'tcx, Result<usize, IoError>> {
421
406
let this = self.eval_context_mut();
422
- // If reading to `bytes` did not fail, we write those bytes to the buffer.
423
- // Crucially, if fewer than `bytes.len()` bytes were read, only write
424
- // that much into the output buffer!
425
- this.write_bytes_ptr(buf, bytes[..actual_read_size].iter().copied())?;
426
407
427
- // The actual read size is always less than what got originally requested so this cannot fail.
428
- this.write_int(u64::try_from(actual_read_size).unwrap(), dest)?;
429
- interp_ok(())
408
+ let mut bytes = vec![0; len];
409
+ let result = file.read(&mut bytes);
410
+ match result {
411
+ Ok(read_size) => {
412
+ // If reading to `bytes` did not fail, we write those bytes to the buffer.
413
+ // Crucially, if fewer than `bytes.len()` bytes were read, only write
414
+ // that much into the output buffer!
415
+ this.write_bytes_ptr(ptr, bytes[..read_size].iter().copied())?;
416
+ interp_ok(Ok(read_size))
417
+ }
418
+ Err(e) => interp_ok(Err(IoError::HostError(e))),
419
+ }
430
420
}
431
421
432
- /// Helper to implement `FileDescription::write`:
433
- /// This function is only used when `write` is successful, and writes `actual_write_size` to `dest`
434
- fn return_write_success(
422
+ /// Write data to a host `Write` type, withthe bytes taken from machine memory.
423
+ fn write_to_host(
435
424
&mut self,
436
- actual_write_size: usize,
437
- dest: &MPlaceTy<'tcx>,
438
- ) -> InterpResult<'tcx> {
425
+ mut file: impl io::Write,
426
+ len: usize,
427
+ ptr: Pointer,
428
+ ) -> InterpResult<'tcx, Result<usize, IoError>> {
439
429
let this = self.eval_context_mut();
440
- // The actual write size is always less than what got originally requested so this cannot fail.
441
- this.write_int(u64::try_from(actual_write_size).unwrap(), dest)?;
442
- interp_ok(())
430
+
431
+ let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
432
+ let result = file.write(bytes);
433
+ interp_ok(result.map_err(IoError::HostError))
443
434
}
444
435
}
0 commit comments