Skip to content

Commit 16cffc7

Browse files
committed
bring socket logic back together and fix logic bug
1 parent 3623dfd commit 16cffc7

File tree

1 file changed

+74
-86
lines changed

1 file changed

+74
-86
lines changed

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

Lines changed: 74 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -96,26 +96,7 @@ impl FileDescription for AnonSocket {
9696
dest: &MPlaceTy<'tcx>,
9797
ecx: &mut MiriInterpCx<'tcx>,
9898
) -> InterpResult<'tcx> {
99-
// Always succeed on read size 0.
100-
if len == 0 {
101-
return ecx.return_read_success(ptr, &[], 0, dest);
102-
}
103-
104-
let Some(readbuf) = &self.readbuf else {
105-
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
106-
// corresponding ErrorKind variant.
107-
throw_unsup_format!("reading from the write end of a pipe");
108-
};
109-
110-
if readbuf.borrow().buf.is_empty() && self.is_nonblock {
111-
// Non-blocking socketpair with writer and empty buffer.
112-
// https://linux.die.net/man/2/read
113-
// EAGAIN or EWOULDBLOCK can be returned for socket,
114-
// POSIX.1-2001 allows either error to be returned for this case.
115-
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
116-
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
117-
}
118-
anonsocket_read(self_ref.downgrade(), len, ptr, dest.clone(), ecx)
99+
anonsocket_read(self_ref, len, ptr, dest, ecx)
119100
}
120101

121102
fn write<'tcx>(
@@ -127,31 +108,7 @@ impl FileDescription for AnonSocket {
127108
dest: &MPlaceTy<'tcx>,
128109
ecx: &mut MiriInterpCx<'tcx>,
129110
) -> InterpResult<'tcx> {
130-
// Always succeed on write size 0.
131-
// ("If count is zero and fd refers to a file other than a regular file, the results are not specified.")
132-
if len == 0 {
133-
return ecx.return_write_success(0, dest);
134-
}
135-
136-
// We are writing to our peer's readbuf.
137-
let Some(peer_fd) = self.peer_fd().upgrade() else {
138-
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
139-
// closed.
140-
return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, dest);
141-
};
142-
143-
let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
144-
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
145-
// corresponding ErrorKind variant.
146-
throw_unsup_format!("writing to the reading end of a pipe");
147-
};
148-
let available_space =
149-
MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
150-
if available_space == 0 && self.is_nonblock {
151-
// Non-blocking socketpair with a full buffer.
152-
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
153-
}
154-
anonsocket_write(self_ref.downgrade(), ptr, len, dest.clone(), ecx)
111+
anonsocket_write(self_ref, ptr, len, dest, ecx)
155112
}
156113

157114
fn as_unix(&self) -> &dyn UnixFileDescription {
@@ -161,50 +118,66 @@ impl FileDescription for AnonSocket {
161118

162119
/// Write to AnonSocket based on the space available and return the written byte size.
163120
fn anonsocket_write<'tcx>(
164-
weak_self_ref: WeakFileDescriptionRef,
121+
self_ref: &FileDescriptionRef,
165122
ptr: Pointer,
166123
len: usize,
167-
dest: MPlaceTy<'tcx>,
124+
dest: &MPlaceTy<'tcx>,
168125
ecx: &mut MiriInterpCx<'tcx>,
169126
) -> InterpResult<'tcx> {
170-
let Some(self_ref) = weak_self_ref.upgrade() else {
171-
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
172-
throw_unsup_format!("This will be a deadlock error in future")
173-
};
174127
let self_anonsocket = self_ref.downcast::<AnonSocket>().unwrap();
128+
129+
// Always succeed on write size 0.
130+
// ("If count is zero and fd refers to a file other than a regular file, the results are not specified.")
131+
if len == 0 {
132+
return ecx.return_write_success(0, dest);
133+
}
134+
135+
// We are writing to our peer's readbuf.
175136
let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() else {
176137
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
177-
// closed.
178-
return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, &dest);
138+
// closed. It is an error to write even if there would be space.
139+
return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, dest);
179140
};
141+
180142
let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
181-
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
182-
// corresponding ErrorKind variant.
183-
throw_unsup_format!("writing to the reading end of a pipe")
143+
// Writing to the read end of a pipe.
144+
return ecx.set_last_error_and_return(IoError::LibcError("EBADF"), dest);
184145
};
185146

147+
// Let's see if we can write.
186148
let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
187-
188149
if available_space == 0 {
189-
// Blocking socketpair with a full buffer.
190-
let dest = dest.clone();
191-
self_anonsocket.blocked_write_tid.borrow_mut().push(ecx.active_thread());
192-
ecx.block_thread(
193-
BlockReason::UnnamedSocket,
194-
None,
195-
callback!(
196-
@capture<'tcx> {
197-
weak_self_ref: WeakFileDescriptionRef,
198-
ptr: Pointer,
199-
len: usize,
200-
dest: MPlaceTy<'tcx>,
201-
}
202-
@unblock = |this| {
203-
anonsocket_write(weak_self_ref, ptr, len, dest, this)
204-
}
205-
),
206-
);
150+
if self_anonsocket.is_nonblock {
151+
// Non-blocking socketpair with a full buffer.
152+
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
153+
} else {
154+
// Blocking socketpair with a full buffer.
155+
// Block the current thread; only keep a weak ref for this.
156+
let weak_self_ref = self_ref.downgrade();
157+
let dest = dest.clone();
158+
self_anonsocket.blocked_write_tid.borrow_mut().push(ecx.active_thread());
159+
ecx.block_thread(
160+
BlockReason::UnnamedSocket,
161+
None,
162+
callback!(
163+
@capture<'tcx> {
164+
weak_self_ref: WeakFileDescriptionRef,
165+
ptr: Pointer,
166+
len: usize,
167+
dest: MPlaceTy<'tcx>,
168+
}
169+
@unblock = |this| {
170+
let Some(self_ref) = weak_self_ref.upgrade() else {
171+
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
172+
throw_unsup_format!("This will be a deadlock error in future")
173+
};
174+
anonsocket_write(&self_ref, ptr, len, &dest, this)
175+
}
176+
),
177+
);
178+
}
207179
} else {
180+
// There is space to write!
208181
let mut writebuf = writebuf.borrow_mut();
209182
// Remember this clock so `read` can synchronize with us.
210183
ecx.release_clock(|clock| {
@@ -229,25 +202,26 @@ fn anonsocket_write<'tcx>(
229202
ecx.unblock_thread(thread_id, BlockReason::UnnamedSocket)?;
230203
}
231204

232-
return ecx.return_write_success(actual_write_size, &dest);
205+
return ecx.return_write_success(actual_write_size, dest);
233206
}
234207
interp_ok(())
235208
}
236209

237210
/// Read from AnonSocket and return the number of bytes read.
238211
fn anonsocket_read<'tcx>(
239-
weak_self_ref: WeakFileDescriptionRef,
212+
self_ref: &FileDescriptionRef,
240213
len: usize,
241214
ptr: Pointer,
242-
dest: MPlaceTy<'tcx>,
215+
dest: &MPlaceTy<'tcx>,
243216
ecx: &mut MiriInterpCx<'tcx>,
244217
) -> InterpResult<'tcx> {
245-
let Some(self_ref) = weak_self_ref.upgrade() else {
246-
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
247-
throw_unsup_format!("This will be a deadlock error in future")
248-
};
249218
let self_anonsocket = self_ref.downcast::<AnonSocket>().unwrap();
250219

220+
// Always succeed on read size 0.
221+
if len == 0 {
222+
return ecx.return_read_success(ptr, &[], 0, dest);
223+
}
224+
251225
let Some(readbuf) = &self_anonsocket.readbuf else {
252226
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
253227
// corresponding ErrorKind variant.
@@ -258,10 +232,19 @@ fn anonsocket_read<'tcx>(
258232
if self_anonsocket.peer_fd().upgrade().is_none() {
259233
// Socketpair with no peer and empty buffer.
260234
// 0 bytes successfully read indicates end-of-file.
261-
return ecx.return_read_success(ptr, &[], 0, &dest);
235+
return ecx.return_read_success(ptr, &[], 0, dest);
236+
} else if self_anonsocket.is_nonblock {
237+
// Non-blocking socketpair with writer and empty buffer.
238+
// https://linux.die.net/man/2/read
239+
// EAGAIN or EWOULDBLOCK can be returned for socket,
240+
// POSIX.1-2001 allows either error to be returned for this case.
241+
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
242+
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
262243
} else {
263244
// Blocking socketpair with writer and empty buffer.
264-
let weak_self_ref = weak_self_ref.clone();
245+
// Block the current thread; only keep a weak ref for this.
246+
let weak_self_ref = self_ref.downgrade();
247+
let dest = dest.clone();
265248
self_anonsocket.blocked_read_tid.borrow_mut().push(ecx.active_thread());
266249
ecx.block_thread(
267250
BlockReason::UnnamedSocket,
@@ -274,12 +257,17 @@ fn anonsocket_read<'tcx>(
274257
dest: MPlaceTy<'tcx>,
275258
}
276259
@unblock = |this| {
277-
anonsocket_read(weak_self_ref, len, ptr, dest, this)
260+
let Some(self_ref) = weak_self_ref.upgrade() else {
261+
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
262+
throw_unsup_format!("This will be a deadlock error in future")
263+
};
264+
anonsocket_read(&self_ref, len, ptr, &dest, this)
278265
}
279266
),
280267
);
281268
}
282269
} else {
270+
// There's data to be read!
283271
let mut bytes = vec![0; len];
284272
let mut readbuf = readbuf.borrow_mut();
285273
// Synchronize with all previous writes to this buffer.
@@ -313,7 +301,7 @@ fn anonsocket_read<'tcx>(
313301
}
314302
};
315303

316-
return ecx.return_read_success(ptr, &bytes, actual_read_size, &dest);
304+
return ecx.return_read_success(ptr, &bytes, actual_read_size, dest);
317305
}
318306
interp_ok(())
319307
}

0 commit comments

Comments
 (0)