Skip to content

Commit bc5e839

Browse files
committed
files: make write take callback to store result, rather than writing to 'dest' directly
1 parent 35842d5 commit bc5e839

File tree

5 files changed

+72
-69
lines changed

5 files changed

+72
-69
lines changed

src/tools/miri/src/shims/files.rs

+21-27
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
154154
_communicate_allowed: bool,
155155
_ptr: Pointer,
156156
_len: usize,
157-
_dest: &MPlaceTy<'tcx>,
158157
_ecx: &mut MiriInterpCx<'tcx>,
158+
_finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
159159
) -> InterpResult<'tcx> {
160160
throw_unsup_format!("cannot write to {}", self.name());
161161
}
@@ -234,22 +234,19 @@ impl FileDescription for io::Stdout {
234234
_communicate_allowed: bool,
235235
ptr: Pointer,
236236
len: usize,
237-
dest: &MPlaceTy<'tcx>,
238237
ecx: &mut MiriInterpCx<'tcx>,
238+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
239239
) -> InterpResult<'tcx> {
240-
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
241-
// We allow writing to stderr even with isolation enabled.
242-
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)?;
243242
// Stdout is buffered, flush to make sure it appears on the
244243
// screen. This is the write() syscall of the interpreted
245244
// program, we want it to correspond to a write() syscall on
246245
// the host -- there is no good in adding extra buffering
247246
// here.
248247
io::stdout().flush().unwrap();
249-
match result {
250-
Ok(write_size) => ecx.return_write_success(write_size, dest),
251-
Err(e) => ecx.set_last_error_and_return(e, dest),
252-
}
248+
249+
finish.call(ecx, result)
253250
}
254251

255252
fn is_tty(&self, communicate_allowed: bool) -> bool {
@@ -267,17 +264,13 @@ impl FileDescription for io::Stderr {
267264
_communicate_allowed: bool,
268265
ptr: Pointer,
269266
len: usize,
270-
dest: &MPlaceTy<'tcx>,
271267
ecx: &mut MiriInterpCx<'tcx>,
268+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
272269
) -> InterpResult<'tcx> {
273-
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
274270
// We allow writing to stderr even with isolation enabled.
271+
let result = ecx.write_to_host(&*self, len, ptr)?;
275272
// No need to flush, stderr is not buffered.
276-
let result = Write::write(&mut &*self, bytes);
277-
match result {
278-
Ok(write_size) => ecx.return_write_success(write_size, dest),
279-
Err(e) => ecx.set_last_error_and_return(e, dest),
280-
}
273+
finish.call(ecx, result)
281274
}
282275

283276
fn is_tty(&self, communicate_allowed: bool) -> bool {
@@ -299,11 +292,11 @@ impl FileDescription for NullOutput {
299292
_communicate_allowed: bool,
300293
_ptr: Pointer,
301294
len: usize,
302-
dest: &MPlaceTy<'tcx>,
303295
ecx: &mut MiriInterpCx<'tcx>,
296+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
304297
) -> InterpResult<'tcx> {
305298
// We just don't write anything, but report to the user that we did.
306-
ecx.return_write_success(len, dest)
299+
finish.call(ecx, Ok(len))
307300
}
308301
}
309302

@@ -426,16 +419,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
426419
}
427420
}
428421

429-
/// Helper to implement `FileDescription::write`:
430-
/// This function is only used when `write` is successful, and writes `actual_write_size` to `dest`
431-
fn return_write_success(
422+
/// Write data to a host `Write` type, withthe bytes taken from machine memory.
423+
fn write_to_host(
432424
&mut self,
433-
actual_write_size: usize,
434-
dest: &MPlaceTy<'tcx>,
435-
) -> InterpResult<'tcx> {
425+
mut file: impl io::Write,
426+
len: usize,
427+
ptr: Pointer,
428+
) -> InterpResult<'tcx, Result<usize, IoError>> {
436429
let this = self.eval_context_mut();
437-
// The actual write size is always less than what got originally requested so this cannot fail.
438-
this.write_int(u64::try_from(actual_write_size).unwrap(), dest)?;
439-
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))
440434
}
441435
}

src/tools/miri/src/shims/unix/fd.rs

+23-3
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ pub trait UnixFileDescription: FileDescription {
4646
_ptr: Pointer,
4747
_len: usize,
4848
_offset: u64,
49-
_dest: &MPlaceTy<'tcx>,
5049
_ecx: &mut MiriInterpCx<'tcx>,
50+
_finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
5151
) -> InterpResult<'tcx> {
5252
throw_unsup_format!("cannot pwrite to {}", self.name());
5353
}
@@ -307,13 +307,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
307307
return this.set_last_error_and_return(LibcError("EBADF"), dest);
308308
};
309309

310+
let finish = {
311+
let dest = dest.clone();
312+
callback!(
313+
@capture<'tcx> {
314+
count: usize,
315+
dest: MPlaceTy<'tcx>,
316+
}
317+
|this, result: Result<usize, IoError>| {
318+
match result {
319+
Ok(write_size) => {
320+
assert!(write_size <= count);
321+
// This must fit since `count` fits.
322+
this.write_int(u64::try_from(write_size).unwrap(), &dest)
323+
}
324+
Err(e) => {
325+
this.set_last_error_and_return(e, &dest)
326+
}
327+
}}
328+
)
329+
};
310330
match offset {
311-
None => fd.write(communicate, buf, count, dest, this)?,
331+
None => fd.write(communicate, buf, count, this, finish)?,
312332
Some(offset) => {
313333
let Ok(offset) = u64::try_from(offset) else {
314334
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
315335
};
316-
fd.as_unix().pwrite(communicate, buf, count, offset, dest, this)?
336+
fd.as_unix().pwrite(communicate, buf, count, offset, this, finish)?
317337
}
318338
};
319339
interp_ok(())

src/tools/miri/src/shims/unix/fs.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,13 @@ impl FileDescription for FileHandle {
4949
communicate_allowed: bool,
5050
ptr: Pointer,
5151
len: usize,
52-
dest: &MPlaceTy<'tcx>,
5352
ecx: &mut MiriInterpCx<'tcx>,
53+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
5454
) -> InterpResult<'tcx> {
5555
assert!(communicate_allowed, "isolation should have prevented even opening a file");
56-
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
57-
let result = (&mut &self.file).write(bytes);
58-
match result {
59-
Ok(write_size) => ecx.return_write_success(write_size, dest),
60-
Err(e) => ecx.set_last_error_and_return(e, dest),
61-
}
56+
57+
let result = ecx.write_to_host(&self.file, len, ptr)?;
58+
finish.call(ecx, result)
6259
}
6360

6461
fn seek<'tcx>(
@@ -153,8 +150,8 @@ impl UnixFileDescription for FileHandle {
153150
ptr: Pointer,
154151
len: usize,
155152
offset: u64,
156-
dest: &MPlaceTy<'tcx>,
157153
ecx: &mut MiriInterpCx<'tcx>,
154+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
158155
) -> InterpResult<'tcx> {
159156
assert!(communicate_allowed, "isolation should have prevented even opening a file");
160157
// Emulates pwrite using seek + write + seek to restore cursor position.
@@ -172,10 +169,7 @@ impl UnixFileDescription for FileHandle {
172169
res
173170
};
174171
let result = f();
175-
match result {
176-
Ok(write_size) => ecx.return_write_success(write_size, dest),
177-
Err(e) => ecx.set_last_error_and_return(e, dest),
178-
}
172+
finish.call(ecx, result.map_err(IoError::HostError))
179173
}
180174

181175
fn flock<'tcx>(

src/tools/miri/src/shims/unix/linux_like/eventfd.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,20 @@ impl FileDescription for EventFd {
8484
_communicate_allowed: bool,
8585
ptr: Pointer,
8686
len: usize,
87-
dest: &MPlaceTy<'tcx>,
8887
ecx: &mut MiriInterpCx<'tcx>,
88+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
8989
) -> InterpResult<'tcx> {
9090
// We're treating the buffer as a `u64`.
9191
let ty = ecx.machine.layouts.u64;
9292
// Check the size of slice, and return error only if the size of the slice < 8.
9393
if len < ty.layout.size.bytes_usize() {
94-
return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest);
94+
return finish.call(ecx, Err(ErrorKind::InvalidInput.into()));
9595
}
9696

9797
// Turn the pointer into a place at the right type.
9898
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
9999

100-
eventfd_write(buf_place, dest, self, ecx)
100+
eventfd_write(buf_place, self, ecx, finish)
101101
}
102102

103103
fn as_unix(&self) -> &dyn UnixFileDescription {
@@ -183,15 +183,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
183183
/// else just add the user-supplied value to current counter.
184184
fn eventfd_write<'tcx>(
185185
buf_place: MPlaceTy<'tcx>,
186-
dest: &MPlaceTy<'tcx>,
187186
eventfd: FileDescriptionRef<EventFd>,
188187
ecx: &mut MiriInterpCx<'tcx>,
188+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
189189
) -> InterpResult<'tcx> {
190190
// Figure out which value we should add.
191191
let num = ecx.read_scalar(&buf_place)?.to_u64()?;
192192
// u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1.
193193
if num == u64::MAX {
194-
return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest);
194+
return finish.call(ecx, Err(ErrorKind::InvalidInput.into()));
195195
}
196196

197197
match eventfd.counter.get().checked_add(num) {
@@ -219,16 +219,14 @@ fn eventfd_write<'tcx>(
219219
ecx.check_and_update_readiness(eventfd)?;
220220

221221
// Return how many bytes we consumed from the user-provided buffer.
222-
return ecx.write_int(buf_place.layout.size.bytes(), dest);
222+
return finish.call(ecx, Ok(buf_place.layout.size.bytes_usize()));
223223
}
224224
None | Some(u64::MAX) => {
225225
// We can't update the state, so we have to block.
226226
if eventfd.is_nonblock {
227-
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
227+
return finish.call(ecx, Err(ErrorKind::WouldBlock.into()));
228228
}
229229

230-
let dest = dest.clone();
231-
232230
eventfd.blocked_write_tid.borrow_mut().push(ecx.active_thread());
233231

234232
let weak_eventfd = FileDescriptionRef::downgrade(&eventfd);
@@ -239,15 +237,15 @@ fn eventfd_write<'tcx>(
239237
@capture<'tcx> {
240238
num: u64,
241239
buf_place: MPlaceTy<'tcx>,
242-
dest: MPlaceTy<'tcx>,
240+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
243241
weak_eventfd: WeakFileDescriptionRef<EventFd>,
244242
}
245243
|this, unblock: UnblockKind| {
246244
assert_eq!(unblock, UnblockKind::Ready);
247245
// When we get unblocked, try again. We know the ref is still valid,
248246
// otherwise there couldn't be a `write` that unblocks us.
249247
let eventfd_ref = weak_eventfd.upgrade().unwrap();
250-
eventfd_write(buf_place, &dest, eventfd_ref, this)
248+
eventfd_write(buf_place, eventfd_ref, this, finish)
251249
}
252250
),
253251
);

src/tools/miri/src/shims/unix/unnamed_socket.rs

+13-16
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ use std::collections::VecDeque;
77
use std::io;
88
use std::io::ErrorKind;
99

10-
use rustc_abi::Size;
11-
1210
use crate::concurrency::VClock;
1311
use crate::shims::files::{
1412
EvalContextExt as _, FileDescription, FileDescriptionRef, WeakFileDescriptionRef,
@@ -103,10 +101,10 @@ impl FileDescription for AnonSocket {
103101
_communicate_allowed: bool,
104102
ptr: Pointer,
105103
len: usize,
106-
dest: &MPlaceTy<'tcx>,
107104
ecx: &mut MiriInterpCx<'tcx>,
105+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
108106
) -> InterpResult<'tcx> {
109-
anonsocket_write(self, ptr, len, dest, ecx)
107+
anonsocket_write(self, ptr, len, ecx, finish)
110108
}
111109

112110
fn as_unix(&self) -> &dyn UnixFileDescription {
@@ -119,39 +117,38 @@ fn anonsocket_write<'tcx>(
119117
self_ref: FileDescriptionRef<AnonSocket>,
120118
ptr: Pointer,
121119
len: usize,
122-
dest: &MPlaceTy<'tcx>,
123120
ecx: &mut MiriInterpCx<'tcx>,
121+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
124122
) -> InterpResult<'tcx> {
125123
// Always succeed on write size 0.
126124
// ("If count is zero and fd refers to a file other than a regular file, the results are not specified.")
127125
if len == 0 {
128-
return ecx.return_write_success(0, dest);
126+
return finish.call(ecx, Ok(0));
129127
}
130128

131129
// We are writing to our peer's readbuf.
132130
let Some(peer_fd) = self_ref.peer_fd().upgrade() else {
133131
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
134132
// closed. It is an error to write even if there would be space.
135-
return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, dest);
133+
return finish.call(ecx, Err(ErrorKind::BrokenPipe.into()));
136134
};
137135

138136
let Some(writebuf) = &peer_fd.readbuf else {
139137
// Writing to the read end of a pipe.
140-
return ecx.set_last_error_and_return(IoError::LibcError("EBADF"), dest);
138+
return finish.call(ecx, Err(IoError::LibcError("EBADF")));
141139
};
142140

143141
// Let's see if we can write.
144142
let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
145143
if available_space == 0 {
146144
if self_ref.is_nonblock {
147145
// Non-blocking socketpair with a full buffer.
148-
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
146+
return finish.call(ecx, Err(ErrorKind::WouldBlock.into()));
149147
} else {
150148
self_ref.blocked_write_tid.borrow_mut().push(ecx.active_thread());
151149
// Blocking socketpair with a full buffer.
152150
// Block the current thread; only keep a weak ref for this.
153151
let weak_self_ref = FileDescriptionRef::downgrade(&self_ref);
154-
let dest = dest.clone();
155152
ecx.block_thread(
156153
BlockReason::UnnamedSocket,
157154
None,
@@ -160,14 +157,14 @@ fn anonsocket_write<'tcx>(
160157
weak_self_ref: WeakFileDescriptionRef<AnonSocket>,
161158
ptr: Pointer,
162159
len: usize,
163-
dest: MPlaceTy<'tcx>,
160+
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
164161
}
165162
|this, unblock: UnblockKind| {
166163
assert_eq!(unblock, UnblockKind::Ready);
167164
// If we got unblocked, then our peer successfully upgraded its weak
168165
// ref to us. That means we can also upgrade our weak ref.
169166
let self_ref = weak_self_ref.upgrade().unwrap();
170-
anonsocket_write(self_ref, ptr, len, &dest, this)
167+
anonsocket_write(self_ref, ptr, len, this, finish)
171168
}
172169
),
173170
);
@@ -180,9 +177,9 @@ fn anonsocket_write<'tcx>(
180177
writebuf.clock.join(clock);
181178
});
182179
// Do full write / partial write based on the space available.
183-
let actual_write_size = len.min(available_space);
184-
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
185-
writebuf.buf.extend(&bytes[..actual_write_size]);
180+
let write_size = len.min(available_space);
181+
let actual_write_size = ecx.write_to_host(&mut writebuf.buf, write_size, ptr)?.unwrap();
182+
assert_eq!(actual_write_size, write_size);
186183

187184
// Need to stop accessing peer_fd so that it can be notified.
188185
drop(writebuf);
@@ -197,7 +194,7 @@ fn anonsocket_write<'tcx>(
197194
// The kernel does this even if the fd was already readable before, so we follow suit.
198195
ecx.check_and_update_readiness(peer_fd)?;
199196

200-
return ecx.return_write_success(actual_write_size, dest);
197+
return finish.call(ecx, Ok(write_size));
201198
}
202199
interp_ok(())
203200
}

0 commit comments

Comments
 (0)