1use std::cell::{Cell, RefCell, RefMut};
2use std::io;
3use std::io::Read;
4use std::net::{Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4};
5use std::sync::atomic::AtomicBool;
6use std::time::Duration;
7
8use mio::event::Source;
9use mio::net::{TcpListener, TcpStream};
10use rustc_abi::Size;
11use rustc_const_eval::interpret::{InterpResult, interp_ok};
12use rustc_middle::throw_unsup_format;
13use rustc_target::spec::Os;
14
15use crate::shims::files::{EvalContextExt as _, FdId, FileDescription, FileDescriptionRef};
16use crate::shims::unix::UnixFileDescription;
17use crate::shims::unix::linux_like::epoll::{EpollReadiness, EvalContextExt as _};
18use crate::shims::unix::socket_address::EvalContextExt as _;
19use crate::*;
20
21#[derive(Debug, PartialEq)]
22enum SocketFamily {
23 IPv4,
25 IPv6,
27}
28
29#[derive(Debug)]
30enum SocketState {
31 Initial,
33 Bound(SocketAddr),
36 Listening(TcpListener),
39 Connecting(TcpStream),
43 Connected(TcpStream),
49 ConnectionFailed(TcpStream),
56}
57
58#[derive(Debug)]
59struct Socket {
60 family: SocketFamily,
63 state: RefCell<SocketState>,
65 is_non_block: Cell<bool>,
67 io_readiness: RefCell<BlockingIoSourceReadiness>,
69 error: RefCell<Option<io::Error>>,
71 read_timeout: Cell<Option<Duration>>,
77 write_timeout: Cell<Option<Duration>>,
83}
84
85impl FileDescription for Socket {
86 fn name(&self) -> &'static str {
87 "socket"
88 }
89
90 fn destroy<'tcx>(
91 self,
92 self_id: FdId,
93 communicate_allowed: bool,
94 ecx: &mut MiriInterpCx<'tcx>,
95 ) -> InterpResult<'tcx, io::Result<()>> {
96 assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
97
98 if matches!(
99 &*self.state.borrow(),
100 SocketState::Listening(_)
101 | SocketState::Connecting(_)
102 | SocketState::Connected(_)
103 | SocketState::ConnectionFailed(_)
104 ) {
105 ecx.machine.blocking_io.deregister(self_id, self)
108 };
109
110 interp_ok(Ok(()))
111 }
112
113 fn read<'tcx>(
114 self: FileDescriptionRef<Self>,
115 communicate_allowed: bool,
116 ptr: Pointer,
117 len: usize,
118 ecx: &mut MiriInterpCx<'tcx>,
119 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
120 ) -> InterpResult<'tcx> {
121 assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
122
123 let socket = self;
124 let deadline = ecx.action_deadline(socket.is_non_block.get(), socket.read_timeout.get());
125
126 ecx.ensure_connected(
127 socket.clone(),
128 deadline.clone(),
129 "read",
130 callback!(
131 @capture<'tcx> {
132 socket: FileDescriptionRef<Socket>,
133 deadline: Option<Deadline>,
134 ptr: Pointer,
135 len: usize,
136 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
137 } |this, result: Result<(), ()>| {
138 if result.is_err() {
139 return finish.call(this, Err(LibcError("ENOTCONN")))
140 }
141
142 if socket.is_non_block.get() {
146 let result = this.try_non_block_recv(&socket, ptr, len, false)?;
149 finish.call(this, result)
150 } else {
151 this.block_for_recv(socket, deadline, ptr, len, false, finish)
154 }
155 }
156 ),
157 )
158 }
159
160 fn write<'tcx>(
161 self: FileDescriptionRef<Self>,
162 communicate_allowed: bool,
163 ptr: Pointer,
164 len: usize,
165 ecx: &mut MiriInterpCx<'tcx>,
166 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
167 ) -> InterpResult<'tcx> {
168 assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
169
170 let socket = self;
171 let deadline = ecx.action_deadline(socket.is_non_block.get(), socket.write_timeout.get());
172
173 ecx.ensure_connected(
174 socket.clone(),
175 deadline.clone(),
176 "write",
177 callback!(
178 @capture<'tcx> {
179 socket: FileDescriptionRef<Socket>,
180 deadline: Option<Deadline>,
181 ptr: Pointer,
182 len: usize,
183 finish: DynMachineCallback<'tcx, Result<usize, IoError>>
184 } |this, result: Result<(), ()>| {
185 if result.is_err() {
186 return finish.call(this, Err(LibcError("ENOTCONN")))
187 }
188
189 if socket.is_non_block.get() {
193 let result = this.try_non_block_send(&socket, ptr, len)?;
196 return finish.call(this, result)
197 } else {
198 this.block_for_send(socket, deadline, ptr, len, finish)
201 }
202 }
203 ),
204 )
205 }
206
207 fn short_fd_operations(&self) -> bool {
208 false
213 }
214
215 fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {
216 self
217 }
218
219 fn get_flags<'tcx>(&self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> {
220 let mut flags = ecx.eval_libc_i32("O_RDWR");
221
222 if self.is_non_block.get() {
223 flags |= ecx.eval_libc_i32("O_NONBLOCK");
224 }
225
226 interp_ok(Scalar::from_i32(flags))
227 }
228
229 fn set_flags<'tcx>(
230 &self,
231 mut flag: i32,
232 ecx: &mut MiriInterpCx<'tcx>,
233 ) -> InterpResult<'tcx, Scalar> {
234 let o_nonblock = ecx.eval_libc_i32("O_NONBLOCK");
235
236 if flag & o_nonblock == o_nonblock {
238 self.is_non_block.set(true);
239 flag &= !o_nonblock;
240 } else {
241 self.is_non_block.set(false);
242 }
243
244 if flag != 0 {
246 throw_unsup_format!("fcntl: only O_NONBLOCK is supported for sockets")
247 }
248
249 interp_ok(Scalar::from_i32(0))
250 }
251}
252
253impl UnixFileDescription for Socket {
254 fn ioctl<'tcx>(
255 &self,
256 op: Scalar,
257 arg: Option<&OpTy<'tcx>>,
258 ecx: &mut MiriInterpCx<'tcx>,
259 ) -> InterpResult<'tcx, i32> {
260 assert!(ecx.machine.communicate(), "cannot have `Socket` with isolation enabled!");
261
262 let fionbio = ecx.eval_libc("FIONBIO");
263
264 if op == fionbio {
265 if !matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android | Os::MacOs | Os::FreeBsd)
268 {
269 throw_unsup_format!(
274 "ioctl: setting FIONBIO on sockets is unsupported on target {}",
275 ecx.tcx.sess.target.os
276 );
277 }
278
279 let Some(value_ptr) = arg else {
280 throw_ub_format!("ioctl: setting FIONBIO on sockets requires a third argument");
281 };
282 let value = ecx.deref_pointer_as(value_ptr, ecx.machine.layouts.i32)?;
283 let non_block = ecx.read_scalar(&value)?.to_i32()? != 0;
284 self.is_non_block.set(non_block);
285 return interp_ok(0);
286 }
287
288 throw_unsup_format!("ioctl: unsupported operation {op:#x} on socket");
289 }
290
291 fn epoll_active_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadiness> {
292 interp_ok(EpollReadiness::from(&*self.io_readiness.borrow()))
293 }
294}
295
296impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
297pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
298 fn socket(
301 &mut self,
302 domain: &OpTy<'tcx>,
303 type_: &OpTy<'tcx>,
304 protocol: &OpTy<'tcx>,
305 ) -> InterpResult<'tcx, Scalar> {
306 let this = self.eval_context_mut();
307
308 let domain = this.read_scalar(domain)?.to_i32()?;
309 let mut flags = this.read_scalar(type_)?.to_i32()?;
310 let protocol = this.read_scalar(protocol)?.to_i32()?;
311
312 if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
314 this.reject_in_isolation("`socket`", reject_with)?;
315 return this.set_errno_and_return_neg1_i32(LibcError("EACCES"));
316 }
317
318 let mut is_sock_nonblock = false;
319
320 if matches!(
323 this.tcx.sess.target.os,
324 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
325 ) {
326 let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
329 let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
330 if flags & sock_nonblock == sock_nonblock {
331 is_sock_nonblock = true;
332 flags &= !sock_nonblock;
333 }
334 if flags & sock_cloexec == sock_cloexec {
335 flags &= !sock_cloexec;
337 }
338 }
339
340 let family = if domain == this.eval_libc_i32("AF_INET") {
341 SocketFamily::IPv4
342 } else if domain == this.eval_libc_i32("AF_INET6") {
343 SocketFamily::IPv6
344 } else {
345 throw_unsup_format!(
346 "socket: domain {:#x} is unsupported, only AF_INET and \
347 AF_INET6 are allowed.",
348 domain
349 );
350 };
351
352 if flags != this.eval_libc_i32("SOCK_STREAM") {
353 throw_unsup_format!(
354 "socket: type {:#x} is unsupported, only SOCK_STREAM, \
355 SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
356 flags
357 );
358 }
359 if protocol != 0 && protocol != this.eval_libc_i32("IPPROTO_TCP") {
360 throw_unsup_format!(
361 "socket: socket protocol {protocol} is unsupported, \
362 only IPPROTO_TCP and 0 are allowed"
363 );
364 }
365
366 let fds = &mut this.machine.fds;
367 let fd = fds.new_ref(Socket {
368 family,
369 state: RefCell::new(SocketState::Initial),
370 is_non_block: Cell::new(is_sock_nonblock),
371 io_readiness: RefCell::new(BlockingIoSourceReadiness::empty()),
372 error: RefCell::new(None),
373 read_timeout: Cell::new(None),
374 write_timeout: Cell::new(None),
375 });
376
377 interp_ok(Scalar::from_i32(fds.insert(fd)))
378 }
379
380 fn bind(
381 &mut self,
382 socket: &OpTy<'tcx>,
383 address: &OpTy<'tcx>,
384 address_len: &OpTy<'tcx>,
385 ) -> InterpResult<'tcx, Scalar> {
386 let this = self.eval_context_mut();
387
388 let socket = this.read_scalar(socket)?.to_i32()?;
389 let address = match this.read_socket_address(address, address_len, "bind")? {
390 Ok(addr) => addr,
391 Err(e) => return this.set_errno_and_return_neg1_i32(e),
392 };
393
394 let Some(fd) = this.machine.fds.get(socket) else {
396 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
397 };
398
399 let Some(socket) = fd.downcast::<Socket>() else {
400 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
402 };
403
404 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
405 this.ensure_not_failed(&socket, "bind")?;
406
407 let mut state = socket.state.borrow_mut();
408
409 match *state {
410 SocketState::Initial => {
411 let address_family = match &address {
412 SocketAddr::V4(_) => SocketFamily::IPv4,
413 SocketAddr::V6(_) => SocketFamily::IPv6,
414 };
415
416 if socket.family != address_family {
417 let err = if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android) {
420 LibcError("EINVAL")
423 } else {
424 LibcError("EAFNOSUPPORT")
428 };
429 return this.set_errno_and_return_neg1_i32(err);
430 }
431
432 *state = SocketState::Bound(address);
433 }
434 SocketState::Connecting(_) | SocketState::Connected(_) =>
435 throw_unsup_format!(
436 "bind: socket is already connected and binding a
437 connected socket is unsupported"
438 ),
439 SocketState::Bound(_) | SocketState::Listening(_) =>
440 throw_unsup_format!(
441 "bind: socket is already bound and binding a socket \
442 multiple times is unsupported"
443 ),
444 SocketState::ConnectionFailed(_) => unreachable!(),
445 }
446
447 interp_ok(Scalar::from_i32(0))
448 }
449
450 fn listen(&mut self, socket: &OpTy<'tcx>, backlog: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
451 let this = self.eval_context_mut();
452
453 let socket = this.read_scalar(socket)?.to_i32()?;
454 let _backlog = this.read_scalar(backlog)?.to_i32()?;
456
457 let Some(fd) = this.machine.fds.get(socket) else {
459 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
460 };
461
462 let Some(socket) = fd.downcast::<Socket>() else {
463 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
465 };
466
467 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
468 this.ensure_not_failed(&socket, "listen")?;
469
470 let mut state = socket.state.borrow_mut();
471
472 match *state {
473 SocketState::Bound(socket_addr) =>
474 match TcpListener::bind(socket_addr) {
475 Ok(listener) => {
476 *state = SocketState::Listening(listener);
477 drop(state);
478 this.machine.blocking_io.register(socket);
481 }
482 Err(e) => return this.set_errno_and_return_neg1_i32(e),
483 },
484 SocketState::Initial => {
485 throw_unsup_format!(
486 "listen: listening on a socket which isn't bound is unsupported"
487 )
488 }
489 SocketState::Listening(_) => {
490 throw_unsup_format!("listen: listening on a socket multiple times is unsupported")
491 }
492 SocketState::Connecting(_) | SocketState::Connected(_) => {
493 throw_unsup_format!("listen: listening on a connected socket is unsupported")
494 }
495 SocketState::ConnectionFailed(_) => unreachable!(),
496 }
497
498 interp_ok(Scalar::from_i32(0))
499 }
500
501 fn accept4(
504 &mut self,
505 socket: &OpTy<'tcx>,
506 address: &OpTy<'tcx>,
507 address_len: &OpTy<'tcx>,
508 flags: Option<&OpTy<'tcx>>,
509 dest: &MPlaceTy<'tcx>,
511 ) -> InterpResult<'tcx> {
512 let this = self.eval_context_mut();
513
514 let socket = this.read_scalar(socket)?.to_i32()?;
515 let address_ptr = this.read_pointer(address)?;
516 let address_len_ptr = this.read_pointer(address_len)?;
517 let mut flags =
518 if let Some(flags) = flags { this.read_scalar(flags)?.to_i32()? } else { 0 };
519
520 let Some(fd) = this.machine.fds.get(socket) else {
522 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
523 };
524
525 let Some(socket) = fd.downcast::<Socket>() else {
526 return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
528 };
529
530 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
531 this.ensure_not_failed(&socket, "accept4")?;
532
533 if !matches!(*socket.state.borrow(), SocketState::Listening(_)) {
534 throw_unsup_format!(
535 "accept4: accepting incoming connections is only allowed when socket is listening"
536 )
537 };
538
539 let mut is_client_sock_nonblock = false;
540
541 if matches!(
544 this.tcx.sess.target.os,
545 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
546 ) {
547 let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
550 let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
551 if flags & sock_nonblock == sock_nonblock {
552 is_client_sock_nonblock = true;
553 flags &= !sock_nonblock;
554 }
555 if flags & sock_cloexec == sock_cloexec {
556 flags &= !sock_cloexec;
558 }
559 }
560
561 if flags != 0 {
562 throw_unsup_format!(
563 "accept4: flag {flags:#x} is unsupported, only SOCK_CLOEXEC \
564 and SOCK_NONBLOCK are allowed",
565 );
566 }
567
568 if socket.is_non_block.get() {
569 match this.try_non_block_accept(
572 &socket,
573 address_ptr,
574 address_len_ptr,
575 is_client_sock_nonblock,
576 )? {
577 Ok(sockfd) => {
578 this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), dest)
583 }
584 Err(e) => this.set_errno_and_return_neg1(e, dest),
585 }
586 } else {
587 if socket.read_timeout.get().is_some() {
591 throw_unsup_format!(
596 "accept4: blocking accept is not supported when SO_RCVTIMEO is non-zero"
597 )
598 }
599
600 this.block_for_accept(
601 socket,
602 address_ptr,
603 address_len_ptr,
604 is_client_sock_nonblock,
605 dest.clone(),
606 )
607 }
608 }
609
610 fn connect(
611 &mut self,
612 socket: &OpTy<'tcx>,
613 address: &OpTy<'tcx>,
614 address_len: &OpTy<'tcx>,
615 dest: &MPlaceTy<'tcx>,
617 ) -> InterpResult<'tcx> {
618 let this = self.eval_context_mut();
619
620 let socket = this.read_scalar(socket)?.to_i32()?;
621 let address = match this.read_socket_address(address, address_len, "connect")? {
622 Ok(address) => address,
623 Err(e) => return this.set_errno_and_return_neg1(e, dest),
624 };
625
626 let Some(fd) = this.machine.fds.get(socket) else {
628 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
629 };
630
631 let Some(socket) = fd.downcast::<Socket>() else {
632 return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
634 };
635
636 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
637 this.ensure_not_failed(&socket, "connect")?;
638
639 match &*socket.state.borrow() {
640 SocketState::Initial => { }
641 SocketState::Connecting(_) =>
643 return this.set_errno_and_return_neg1(LibcError("EALREADY"), dest),
644 _ =>
648 throw_unsup_format!(
649 "connect: connecting is only supported for sockets which are neither \
650 bound, listening nor already connected"
651 ),
652 }
653
654 match TcpStream::connect(address) {
657 Ok(stream) => {
658 *socket.state.borrow_mut() = SocketState::Connecting(stream);
659 this.machine.blocking_io.register(socket.clone());
662 }
663 Err(e) => return this.set_errno_and_return_neg1(e, dest),
664 };
665
666 if socket.is_non_block.get() {
667 this.set_errno_and_return_neg1(LibcError("EINPROGRESS"), dest)
674 } else {
675 if socket.write_timeout.get().is_some() {
679 throw_unsup_format!(
684 "connect: blocking connect is not supported when SO_SNDTIMEO is non-zero"
685 )
686 }
687
688 let dest = dest.clone();
689 this.ensure_connected(
690 socket.clone(),
691 None,
692 "connect",
693 callback!(
694 @capture<'tcx> {
695 socket: FileDescriptionRef<Socket>,
696 dest: MPlaceTy<'tcx>
697 } |this, result: Result<(), ()>| {
698 if result.is_err() {
699 let err = socket.error.take().unwrap();
703 this.set_errno_and_return_neg1(err, &dest)
704 } else {
705 this.write_scalar(Scalar::from_i32(0), &dest)
706 }
707 }
708 ),
709 )
710 }
711 }
712
713 fn send(
714 &mut self,
715 socket: &OpTy<'tcx>,
716 buffer: &OpTy<'tcx>,
717 length: &OpTy<'tcx>,
718 flags: &OpTy<'tcx>,
719 dest: &MPlaceTy<'tcx>,
721 ) -> InterpResult<'tcx> {
722 let this = self.eval_context_mut();
723
724 let socket = this.read_scalar(socket)?.to_i32()?;
725 let buffer_ptr = this.read_pointer(buffer)?;
726 let size_layout = this.libc_ty_layout("size_t");
727 let length: usize =
728 this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
729 let mut flags = this.read_scalar(flags)?.to_i32()?;
730
731 let Some(fd) = this.machine.fds.get(socket) else {
733 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
734 };
735
736 let Some(socket) = fd.downcast::<Socket>() else {
737 return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
739 };
740
741 let mut is_op_non_block = false;
742
743 if matches!(
746 this.tcx.sess.target.os,
747 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
748 ) {
749 let msg_nosignal = this.eval_libc_i32("MSG_NOSIGNAL");
752 let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
753 if flags & msg_nosignal == msg_nosignal {
754 flags &= !msg_nosignal;
758 }
759 if flags & msg_dontwait == msg_dontwait {
760 flags &= !msg_dontwait;
761 is_op_non_block = true;
762 }
763 }
764
765 if flags != 0 {
766 throw_unsup_format!(
767 "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL and MSG_DONTWAIT are allowed",
768 );
769 }
770
771 let is_non_block = is_op_non_block || socket.is_non_block.get();
772 let deadline = this.action_deadline(is_non_block, socket.write_timeout.get());
773 let dest = dest.clone();
774
775 this.ensure_connected(
776 socket.clone(),
777 deadline.clone(),
778 "send",
779 callback!(
780 @capture<'tcx> {
781 socket: FileDescriptionRef<Socket>,
782 deadline: Option<Deadline>,
783 flags: i32,
784 buffer_ptr: Pointer,
785 length: usize,
786 is_non_block: bool,
787 dest: MPlaceTy<'tcx>,
788 } |this, result: Result<(), ()>| {
789 if result.is_err() {
790 return this.set_errno_and_return_neg1(LibcError("ENOTCONN"), &dest)
791 }
792
793 if is_non_block {
794 match this.try_non_block_send(&socket, buffer_ptr, length)? {
797 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
798 Err(e) => this.set_errno_and_return_neg1(e, &dest),
799 }
800 } else {
801 this.block_for_send(
804 socket,
805 deadline,
806 buffer_ptr,
807 length,
808 callback!(@capture<'tcx> {
809 dest: MPlaceTy<'tcx>
810 } |this, result: Result<usize, IoError>| {
811 match result {
812 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
813 Err(e) => this.set_errno_and_return_neg1(e, &dest)
814 }
815 }),
816 )
817 }
818 }
819 ),
820 )
821 }
822
823 fn recv(
824 &mut self,
825 socket: &OpTy<'tcx>,
826 buffer: &OpTy<'tcx>,
827 length: &OpTy<'tcx>,
828 flags: &OpTy<'tcx>,
829 dest: &MPlaceTy<'tcx>,
831 ) -> InterpResult<'tcx> {
832 let this = self.eval_context_mut();
833
834 let socket = this.read_scalar(socket)?.to_i32()?;
835 let buffer_ptr = this.read_pointer(buffer)?;
836 let size_layout = this.libc_ty_layout("size_t");
837 let length: usize =
838 this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
839 let mut flags = this.read_scalar(flags)?.to_i32()?;
840
841 let Some(fd) = this.machine.fds.get(socket) else {
843 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
844 };
845
846 let Some(socket) = fd.downcast::<Socket>() else {
847 return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
849 };
850
851 let mut should_peek = false;
852 let mut is_op_non_block = false;
853
854 let msg_peek = this.eval_libc_i32("MSG_PEEK");
858 if flags & msg_peek == msg_peek {
859 should_peek = true;
860 flags &= !msg_peek;
861 }
862
863 if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
864 let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC");
867 if flags & msg_cmsg_cloexec == msg_cmsg_cloexec {
868 flags &= !msg_cmsg_cloexec;
870 }
871 }
872
873 if matches!(
874 this.tcx.sess.target.os,
875 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
876 ) {
877 let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
880 if flags & msg_dontwait == msg_dontwait {
881 flags &= !msg_dontwait;
882 is_op_non_block = true;
883 }
884 }
885
886 if flags != 0 {
887 throw_unsup_format!(
888 "recv: flag {flags:#x} is unsupported, only MSG_PEEK, MSG_DONTWAIT \
889 and MSG_CMSG_CLOEXEC are allowed",
890 );
891 }
892
893 let is_non_block = is_op_non_block || socket.is_non_block.get();
894 let deadline = this.action_deadline(is_non_block, socket.read_timeout.get());
895 let dest = dest.clone();
896
897 this.ensure_connected(
898 socket.clone(),
899 deadline.clone(),
900 "recv",
901 callback!(
902 @capture<'tcx> {
903 socket: FileDescriptionRef<Socket>,
904 deadline: Option<Deadline>,
905 buffer_ptr: Pointer,
906 length: usize,
907 should_peek: bool,
908 is_non_block: bool,
909 dest: MPlaceTy<'tcx>,
910 } |this, result: Result<(), ()>| {
911 if result.is_err() {
912 return this.set_errno_and_return_neg1(LibcError("ENOTCONN"), &dest)
913 }
914
915 if is_non_block {
916 match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {
919 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
920 Err(e) => this.set_errno_and_return_neg1(e, &dest),
921 }
922 } else {
923 this.block_for_recv(
926 socket,
927 deadline,
928 buffer_ptr,
929 length,
930 should_peek,
931 callback!(@capture<'tcx> {
932 dest: MPlaceTy<'tcx>
933 } |this, result: Result<usize, IoError>| {
934 match result {
935 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
936 Err(e) => this.set_errno_and_return_neg1(e, &dest)
937 }
938 }),
939 )
940 }
941 }
942 ),
943 )
944 }
945
946 fn setsockopt(
947 &mut self,
948 socket: &OpTy<'tcx>,
949 level: &OpTy<'tcx>,
950 option_name: &OpTy<'tcx>,
951 option_value: &OpTy<'tcx>,
952 option_len: &OpTy<'tcx>,
953 ) -> InterpResult<'tcx, Scalar> {
954 let this = self.eval_context_mut();
955
956 let socket = this.read_scalar(socket)?.to_i32()?;
957 let level = this.read_scalar(level)?.to_i32()?;
958 let option_name = this.read_scalar(option_name)?.to_i32()?;
959 let option_value_ptr = this.read_pointer(option_value)?;
960 let socklen_layout = this.libc_ty_layout("socklen_t");
961 let option_len = this.read_scalar(option_len)?.to_int(socklen_layout.size)?;
962
963 let Some(fd) = this.machine.fds.get(socket) else {
965 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
966 };
967
968 let Some(socket) = fd.downcast::<Socket>() else {
969 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
971 };
972
973 if level == this.eval_libc_i32("SOL_SOCKET") {
974 let opt_so_rcvtimeo = this.eval_libc_i32("SO_RCVTIMEO");
975 let opt_so_sndtimeo = this.eval_libc_i32("SO_SNDTIMEO");
976 let opt_so_reuseaddr = this.eval_libc_i32("SO_REUSEADDR");
977
978 if matches!(this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::NetBsd) {
979 let opt_so_nosigpipe = this.eval_libc_i32("SO_NOSIGPIPE");
981
982 if option_name == opt_so_nosigpipe {
983 if option_len != 4 {
984 return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
986 }
987 let option_value =
988 this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32);
989 let _val = this.read_scalar(&option_value)?.to_i32()?;
990 return interp_ok(Scalar::from_i32(0));
993 }
994 }
995
996 if option_name == opt_so_rcvtimeo || option_name == opt_so_sndtimeo {
997 let timeval_layout = this.libc_ty_layout("timeval");
998 let option_value = this.ptr_to_mplace(option_value_ptr, timeval_layout);
999
1000 let timeout = match this.read_timeval(&option_value)? {
1001 None => return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")),
1002 Some(Duration::ZERO) => None,
1003 Some(duration) => Some(duration),
1004 };
1005
1006 if option_name == opt_so_rcvtimeo {
1007 socket.read_timeout.set(timeout);
1008 } else {
1009 socket.write_timeout.set(timeout);
1010 }
1011
1012 return interp_ok(Scalar::from_i32(0));
1013 }
1014
1015 if option_name == opt_so_reuseaddr {
1016 if option_len != 4 {
1017 return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
1019 }
1020 let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32);
1021 let _val = this.read_scalar(&option_value)?.to_i32()?;
1022 return interp_ok(Scalar::from_i32(0));
1025 } else {
1026 throw_unsup_format!(
1027 "setsockopt: option {option_name:#x} is unsupported for level SOL_SOCKET",
1028 );
1029 }
1030 } else if level == this.eval_libc_i32("IPPROTO_IP") {
1031 let opt_ip_ttl = this.eval_libc_i32("IP_TTL");
1032
1033 if option_name == opt_ip_ttl {
1034 if option_len != 4 {
1035 return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
1037 }
1038 let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.u32);
1039 let ttl = this.read_scalar(&option_value)?.to_u32()?;
1040
1041 let result = match &*socket.state.borrow() {
1042 SocketState::Initial | SocketState::Bound(_) =>
1043 throw_unsup_format!(
1044 "setsockopt: setting option IP_TTL on level IPPROTO_IP is only supported \
1045 on connected and listening sockets"
1046 ),
1047 SocketState::Listening(listener) => listener.set_ttl(ttl),
1048 SocketState::Connecting(stream) | SocketState::Connected(stream) =>
1049 stream.set_ttl(ttl),
1050 SocketState::ConnectionFailed(_) => unreachable!(),
1051 };
1052
1053 return match result {
1054 Ok(_) => interp_ok(Scalar::from_i32(0)),
1055 Err(e) => this.set_errno_and_return_neg1_i32(e),
1056 };
1057 } else {
1058 throw_unsup_format!(
1059 "setsockopt: option {option_name:#x} is unsupported for level IPPROTO_IP",
1060 );
1061 }
1062 } else if level == this.eval_libc_i32("IPPROTO_TCP") {
1063 let opt_tcp_nodelay = this.eval_libc_i32("TCP_NODELAY");
1064
1065 if option_name == opt_tcp_nodelay {
1066 if option_len != 4 {
1067 return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
1069 }
1070 let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32);
1071 let nodelay = this.read_scalar(&option_value)?.to_i32()? != 0;
1072
1073 let result = match &*socket.state.borrow() {
1074 SocketState::Initial | SocketState::Bound(_) | SocketState::Listening(_) =>
1075 throw_unsup_format!(
1076 "setsockopt: setting option TCP_NODELAY on level IPPROTO_TCP is only supported \
1077 on connected sockets"
1078 ),
1079 SocketState::Connecting(stream) | SocketState::Connected(stream) =>
1080 stream.set_nodelay(nodelay),
1081 SocketState::ConnectionFailed(_) => unreachable!(),
1082 };
1083
1084 return match result {
1085 Ok(_) => interp_ok(Scalar::from_i32(0)),
1086 Err(e) => this.set_errno_and_return_neg1_i32(e),
1087 };
1088 } else {
1089 throw_unsup_format!(
1090 "setsockopt: option {option_name:#x} is unsupported for level IPPROTO_TCP"
1091 );
1092 }
1093 }
1094
1095 throw_unsup_format!(
1096 "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET, IPPROTO_IP \
1097 and IPPROTO_TCP are allowed"
1098 );
1099 }
1100
1101 fn getsockopt(
1102 &mut self,
1103 socket: &OpTy<'tcx>,
1104 level: &OpTy<'tcx>,
1105 option_name: &OpTy<'tcx>,
1106 option_value: &OpTy<'tcx>,
1107 option_len: &OpTy<'tcx>,
1108 ) -> InterpResult<'tcx, Scalar> {
1109 let this = self.eval_context_mut();
1110
1111 let socket = this.read_scalar(socket)?.to_i32()?;
1112 let level = this.read_scalar(level)?.to_i32()?;
1113 let option_name = this.read_scalar(option_name)?.to_i32()?;
1114 let option_value_ptr = this.read_pointer(option_value)?;
1120 let option_len_ptr = this.read_pointer(option_len)?;
1121
1122 let Some(fd) = this.machine.fds.get(socket) else {
1124 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
1125 };
1126
1127 let Some(socket) = fd.downcast::<Socket>() else {
1128 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
1130 };
1131
1132 if option_value_ptr == Pointer::null() || option_len_ptr == Pointer::null() {
1133 return this.set_errno_and_return_neg1_i32(LibcError("EFAULT"));
1136 }
1137
1138 let socklen_layout = this.libc_ty_layout("socklen_t");
1139 let option_len_ptr_mplace = this.ptr_to_mplace(option_len_ptr, socklen_layout);
1140 let option_len: usize = this
1141 .read_scalar(&option_len_ptr_mplace)?
1142 .to_int(socklen_layout.size)?
1143 .try_into()
1144 .unwrap();
1145
1146 let value_buffer = if level == this.eval_libc_i32("SOL_SOCKET") {
1149 let opt_so_error = this.eval_libc_i32("SO_ERROR");
1150 let opt_so_rcvtimeo = this.eval_libc_i32("SO_RCVTIMEO");
1151 let opt_so_sndtimeo = this.eval_libc_i32("SO_SNDTIMEO");
1152
1153 if option_name == opt_so_error {
1154 this.update_last_error(&socket);
1157
1158 let return_value = match socket.error.take() {
1159 Some(err) => this.io_error_to_errnum(err)?.to_i32()?,
1160 None => 0,
1162 };
1163
1164 socket.error.replace(None);
1166
1167 socket.io_readiness.borrow_mut().error = false;
1170 this.update_epoll_active_events(socket, false)?;
1171
1172 let value_buffer = this.allocate(this.machine.layouts.i32, MemoryKind::Stack)?;
1174 this.write_int(return_value, &value_buffer)?;
1175 value_buffer
1176 } else if option_name == opt_so_rcvtimeo || option_name == opt_so_sndtimeo {
1177 let timeout = if option_name == opt_so_rcvtimeo {
1178 socket.read_timeout.get()
1179 } else {
1180 socket.write_timeout.get()
1181 }
1182 .unwrap_or_default();
1183
1184 let secs = timeout.as_secs();
1185 let usecs = timeout.subsec_micros();
1186
1187 let timeval_layout = this.libc_ty_layout("timeval");
1188 let timeval_buffer = this.allocate(timeval_layout, MemoryKind::Stack)?;
1190
1191 let sec_field = this.project_field_named(&timeval_buffer, "tv_sec")?;
1192 this.write_int(secs, &sec_field)?;
1193
1194 let usec_field = this.project_field_named(&timeval_buffer, "tv_usec")?;
1195 this.write_int(usecs, &usec_field)?;
1196
1197 timeval_buffer
1198 } else {
1199 throw_unsup_format!(
1200 "getsockopt: option {option_name:#x} is unsupported for level SOL_SOCKET",
1201 );
1202 }
1203 } else if level == this.eval_libc_i32("IPPROTO_IP") {
1204 let opt_ip_ttl = this.eval_libc_i32("IP_TTL");
1205
1206 if option_name == opt_ip_ttl {
1207 let ttl = match &*socket.state.borrow() {
1208 SocketState::Initial | SocketState::Bound(_) =>
1209 throw_unsup_format!(
1210 "getsockopt: reading option IP_TTL on level IPPROTO_IP is only supported \
1211 on connected and listening sockets"
1212 ),
1213 SocketState::Listening(listener) => listener.ttl(),
1214 SocketState::Connecting(stream) | SocketState::Connected(stream) =>
1215 stream.ttl(),
1216 SocketState::ConnectionFailed(_) => unreachable!(),
1217 };
1218
1219 let ttl = match ttl {
1220 Ok(ttl) => ttl,
1221 Err(e) => return this.set_errno_and_return_neg1_i32(e),
1222 };
1223
1224 let value_buffer = this.allocate(this.machine.layouts.u32, MemoryKind::Stack)?;
1226 this.write_int(ttl, &value_buffer)?;
1227 value_buffer
1228 } else {
1229 throw_unsup_format!(
1230 "getsockopt: option {option_name:#x} is unsupported for level IPPROTO_IP",
1231 );
1232 }
1233 } else if level == this.eval_libc_i32("IPPROTO_TCP") {
1234 let opt_tcp_nodelay = this.eval_libc_i32("TCP_NODELAY");
1235
1236 if option_name == opt_tcp_nodelay {
1237 let nodelay = match &*socket.state.borrow() {
1238 SocketState::Initial | SocketState::Bound(_) | SocketState::Listening(_) =>
1239 throw_unsup_format!(
1240 "getsockopt: reading option TCP_NODELAY on level IPPROTO_TCP is only supported \
1241 on connected sockets"
1242 ),
1243 SocketState::Connecting(stream) | SocketState::Connected(stream) =>
1244 stream.nodelay(),
1245 SocketState::ConnectionFailed(_) => unreachable!(),
1246 };
1247
1248 let nodelay = match nodelay {
1249 Ok(nodelay) => nodelay,
1250 Err(e) => return this.set_errno_and_return_neg1_i32(e),
1251 };
1252
1253 let value_buffer = this.allocate(this.machine.layouts.i32, MemoryKind::Stack)?;
1255 this.write_int(i32::from(nodelay), &value_buffer)?;
1256 value_buffer
1257 } else {
1258 throw_unsup_format!(
1259 "getsockopt: option {option_name:#x} is unsupported for level IPPROTO_TCP"
1260 );
1261 }
1262 } else {
1263 throw_unsup_format!(
1264 "getsockopt: level {level:#x} is unsupported, only SOL_SOCKET, IPPROTO_IP \
1265 and IPPROTO_TCP are allowed"
1266 )
1267 };
1268
1269 let output_value_len = value_buffer.layout.size.min(Size::from_bytes(option_len));
1271 this.mem_copy(
1273 value_buffer.ptr(),
1274 option_value_ptr,
1275 output_value_len,
1277 true,
1280 )?;
1281 this.deallocate_ptr(value_buffer.ptr(), None, MemoryKind::Stack)?;
1284
1285 this.write_scalar(
1288 Scalar::from_uint(output_value_len.bytes(), socklen_layout.size),
1289 &option_len_ptr_mplace,
1290 )?;
1291
1292 interp_ok(Scalar::from_i32(0))
1293 }
1294
1295 fn getsockname(
1296 &mut self,
1297 socket: &OpTy<'tcx>,
1298 address: &OpTy<'tcx>,
1299 address_len: &OpTy<'tcx>,
1300 ) -> InterpResult<'tcx, Scalar> {
1301 let this = self.eval_context_mut();
1302
1303 let socket = this.read_scalar(socket)?.to_i32()?;
1304 let address_ptr = this.read_pointer(address)?;
1305 let address_len_ptr = this.read_pointer(address_len)?;
1306
1307 let Some(fd) = this.machine.fds.get(socket) else {
1309 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
1310 };
1311
1312 let Some(socket) = fd.downcast::<Socket>() else {
1313 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
1315 };
1316
1317 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
1318 this.ensure_not_failed(&socket, "getsockname")?;
1319
1320 let state = socket.state.borrow();
1321
1322 let address = match &*state {
1323 SocketState::Bound(address) => {
1324 if address.port() == 0 {
1325 throw_unsup_format!(
1329 "getsockname: when the port is 0, getting the socket address before \
1330 calling `listen` or `connect` is unsupported"
1331 )
1332 }
1333
1334 *address
1335 }
1336 SocketState::Listening(listener) =>
1337 match listener.local_addr() {
1338 Ok(address) => address,
1339 Err(e) => return this.set_errno_and_return_neg1_i32(e),
1340 },
1341 SocketState::Connecting(stream) | SocketState::Connected(stream) => {
1342 if cfg!(windows) && matches!(&*state, SocketState::Connecting(_)) {
1343 static DEDUP: AtomicBool = AtomicBool::new(false);
1350 if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) {
1351 this.emit_diagnostic(NonHaltingDiagnostic::ConnectingSocketGetsockname);
1352 }
1353 }
1354 match stream.local_addr() {
1355 Ok(address) => address,
1356 Err(e) => return this.set_errno_and_return_neg1_i32(e),
1357 }
1358 }
1359 SocketState::Initial => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)),
1362 SocketState::ConnectionFailed(_) => unreachable!(),
1363 };
1364
1365 this.write_socket_address(&address, address_ptr, address_len_ptr, "getsockname")
1366 .map(|_| Scalar::from_i32(0))
1367 }
1368
1369 fn getpeername(
1370 &mut self,
1371 socket: &OpTy<'tcx>,
1372 address: &OpTy<'tcx>,
1373 address_len: &OpTy<'tcx>,
1374 dest: &MPlaceTy<'tcx>,
1376 ) -> InterpResult<'tcx> {
1377 let this = self.eval_context_mut();
1378
1379 let socket = this.read_scalar(socket)?.to_i32()?;
1380 let address_ptr = this.read_pointer(address)?;
1381 let address_len_ptr = this.read_pointer(address_len)?;
1382
1383 let Some(fd) = this.machine.fds.get(socket) else {
1385 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
1386 };
1387
1388 let Some(socket) = fd.downcast::<Socket>() else {
1389 return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
1391 };
1392
1393 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
1394
1395 let dest = dest.clone();
1396
1397 this.ensure_connected(
1400 socket.clone(),
1401 Some(this.machine.monotonic_clock.now().into()),
1403 "getpeername",
1404 callback!(
1405 @capture<'tcx> {
1406 socket: FileDescriptionRef<Socket>,
1407 address_ptr: Pointer,
1408 address_len_ptr: Pointer,
1409 dest: MPlaceTy<'tcx>,
1410 } |this, result: Result<(), ()>| {
1411 if result.is_err() {
1412 return this.set_errno_and_return_neg1(LibcError("ENOTCONN"), &dest)
1413 };
1414
1415 let SocketState::Connected(stream) = &*socket.state.borrow() else {
1416 unreachable!()
1417 };
1418
1419 let address = match stream.peer_addr() {
1420 Ok(address) => address,
1421 Err(e) => return this.set_errno_and_return_neg1(e, &dest),
1422 };
1423
1424 this.write_socket_address(
1425 &address,
1426 address_ptr,
1427 address_len_ptr,
1428 "getpeername",
1429 )?;
1430 this.write_scalar(Scalar::from_i32(0), &dest)
1431 }
1432 ),
1433 )
1434 }
1435
1436 fn shutdown(&mut self, socket: &OpTy<'tcx>, how: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
1437 let this = self.eval_context_mut();
1438
1439 let socket = this.read_scalar(socket)?.to_i32()?;
1440 let how = this.read_scalar(how)?.to_i32()?;
1441
1442 let Some(fd) = this.machine.fds.get(socket) else {
1444 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
1445 };
1446
1447 let Some(socket) = fd.downcast::<Socket>() else {
1448 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
1450 };
1451
1452 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
1453 this.ensure_not_failed(&socket, "shutdown")?;
1454
1455 let state = socket.state.borrow();
1456
1457 let (SocketState::Connecting(stream) | SocketState::Connected(stream)) = &*state else {
1458 return this.set_errno_and_return_neg1_i32(LibcError("ENOTCONN"));
1459 };
1460
1461 let is_read_shutdown = how == this.eval_libc_i32("SHUT_RD");
1462 let is_write_shutdown = how == this.eval_libc_i32("SHUT_WR");
1463 let is_read_write_shutdown = how == this.eval_libc_i32("SHUT_RDWR");
1464
1465 let how = match () {
1466 _ if is_read_shutdown => Shutdown::Read,
1467 _ if is_write_shutdown => Shutdown::Write,
1468 _ if is_read_write_shutdown => Shutdown::Both,
1469 _ => return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")),
1471 };
1472
1473 if let Err(e) = stream.shutdown(how) {
1474 return this.set_errno_and_return_neg1_i32(e);
1475 };
1476
1477 drop(state);
1478
1479 let mut readiness = socket.io_readiness.borrow_mut();
1485 readiness.read_closed |= is_read_shutdown || is_read_write_shutdown;
1487 readiness.write_closed |= is_read_write_shutdown;
1490 readiness.readable |= is_read_write_shutdown;
1493
1494 drop(readiness);
1495
1496 this.update_epoll_active_events(socket, false)?;
1498
1499 interp_ok(Scalar::from_i32(0))
1500 }
1501}
1502
1503impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
1504trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1505 fn action_deadline(
1512 &self,
1513 is_non_block: bool,
1514 action_timeout: Option<Duration>,
1515 ) -> Option<Deadline> {
1516 let this = self.eval_context_ref();
1517
1518 if is_non_block {
1519 Some(this.machine.monotonic_clock.now().into())
1521 } else {
1522 action_timeout
1523 .map(|duration| this.machine.monotonic_clock.now().add_lossy(duration).into())
1524 }
1525 }
1526
1527 fn block_for_accept(
1534 &mut self,
1535 socket: FileDescriptionRef<Socket>,
1536 address_ptr: Pointer,
1537 address_len_ptr: Pointer,
1538 is_client_sock_nonblock: bool,
1539 dest: MPlaceTy<'tcx>,
1540 ) -> InterpResult<'tcx> {
1541 let this = self.eval_context_mut();
1542 this.block_thread_for_io(
1543 socket.clone(),
1544 BlockingIoInterest::Read,
1545 None,
1546 callback!(@capture<'tcx> {
1547 socket: FileDescriptionRef<Socket>,
1548 address_ptr: Pointer,
1549 address_len_ptr: Pointer,
1550 is_client_sock_nonblock: bool,
1551 dest: MPlaceTy<'tcx>,
1552 } |this, kind: UnblockKind| {
1553 this.machine.blocking_io.remove_blocked_thread(socket.id(), this.machine.threads.active_thread());
1555
1556 match kind {
1557 UnblockKind::Ready => { },
1558 UnblockKind::TimedOut => return this.set_errno_and_return_neg1(LibcError("EWOULDBLOCK"), &dest)
1560 }
1561
1562 match this.try_non_block_accept(&socket, address_ptr, address_len_ptr, is_client_sock_nonblock)? {
1563 Ok(sockfd) => {
1564 this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), &dest)
1569 },
1570 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1571 this.block_for_accept(socket, address_ptr, address_len_ptr, is_client_sock_nonblock, dest)
1573 }
1574 Err(e) => this.set_errno_and_return_neg1(e, &dest),
1575 }
1576 }),
1577 )
1578 }
1579
1580 fn try_non_block_accept(
1586 &mut self,
1587 socket: &FileDescriptionRef<Socket>,
1588 address_ptr: Pointer,
1589 address_len_ptr: Pointer,
1590 is_client_sock_nonblock: bool,
1591 ) -> InterpResult<'tcx, Result<i32, IoError>> {
1592 let this = self.eval_context_mut();
1593
1594 let state = socket.state.borrow();
1595 let SocketState::Listening(listener) = &*state else {
1596 panic!(
1597 "try_non_block_accept must only be called when socket is in `SocketState::Listening`"
1598 )
1599 };
1600
1601 let (stream, addr) = match listener.accept() {
1602 Ok(peer) => peer,
1603 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
1604 socket.io_readiness.borrow_mut().readable = false;
1606 this.update_epoll_active_events(socket.clone(), false)?;
1607
1608 return interp_ok(Err(IoError::HostError(e)));
1609 }
1610 Err(e) => return interp_ok(Err(IoError::HostError(e))),
1611 };
1612
1613 let family = match addr {
1614 SocketAddr::V4(_) => SocketFamily::IPv4,
1615 SocketAddr::V6(_) => SocketFamily::IPv6,
1616 };
1617
1618 if address_ptr != Pointer::null() {
1619 this.write_socket_address(&addr, address_ptr, address_len_ptr, "accept4")?;
1623 }
1624
1625 let fd = this.machine.fds.new_ref(Socket {
1626 family,
1627 state: RefCell::new(SocketState::Connected(stream)),
1628 is_non_block: Cell::new(is_client_sock_nonblock),
1629 io_readiness: RefCell::new(BlockingIoSourceReadiness::empty()),
1630 error: RefCell::new(None),
1631 read_timeout: Cell::new(None),
1632 write_timeout: Cell::new(None),
1633 });
1634 this.machine.blocking_io.register(fd.clone());
1637 let sockfd = this.machine.fds.insert(fd);
1638 interp_ok(Ok(sockfd))
1639 }
1640
1641 fn block_for_send(
1649 &mut self,
1650 socket: FileDescriptionRef<Socket>,
1651 deadline: Option<Deadline>,
1652 buffer_ptr: Pointer,
1653 length: usize,
1654 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1655 ) -> InterpResult<'tcx> {
1656 let this = self.eval_context_mut();
1657 this.block_thread_for_io(
1658 socket.clone(),
1659 BlockingIoInterest::Write,
1660 deadline.clone(),
1661 callback!(@capture<'tcx> {
1662 socket: FileDescriptionRef<Socket>,
1663 deadline: Option<Deadline>,
1664 buffer_ptr: Pointer,
1665 length: usize,
1666 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1667 } |this, kind: UnblockKind| {
1668 this.machine.blocking_io.remove_blocked_thread(socket.id(), this.machine.threads.active_thread());
1670
1671 match kind {
1672 UnblockKind::Ready => { },
1673 UnblockKind::TimedOut => return finish.call(this, Err(LibcError("EWOULDBLOCK")))
1675 }
1676
1677 match this.try_non_block_send(&socket, buffer_ptr, length)? {
1678 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1679 this.block_for_send(socket, deadline, buffer_ptr, length, finish)
1681 },
1682 result => finish.call(this, result)
1683 }
1684 }),
1685 )
1686 }
1687
1688 fn try_non_block_send(
1693 &mut self,
1694 socket: &FileDescriptionRef<Socket>,
1695 buffer_ptr: Pointer,
1696 length: usize,
1697 ) -> InterpResult<'tcx, Result<usize, IoError>> {
1698 let this = self.eval_context_mut();
1699
1700 let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1701 panic!("try_non_block_send must only be called when the socket is connected")
1702 };
1703
1704 let result = this.write_to_host(stream, length, buffer_ptr)?;
1706 match result {
1707 Err(IoError::HostError(e))
1708 if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) =>
1709 {
1710 socket.io_readiness.borrow_mut().writable = false;
1712 this.update_epoll_active_events(socket.clone(), false)?;
1713
1714 interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1717 }
1718 Ok(bytes_written) if bytes_written < length => {
1719 if cfg!(any(
1725 target_os = "android",
1727 target_os = "illumos",
1728 target_os = "linux",
1729 target_os = "redox",
1730 target_os = "dragonfly",
1732 target_os = "freebsd",
1733 target_os = "ios",
1734 target_os = "macos",
1735 target_os = "netbsd",
1736 target_os = "openbsd",
1737 target_os = "tvos",
1738 target_os = "visionos",
1739 target_os = "watchos",
1740 )) {
1741 socket.io_readiness.borrow_mut().writable = false;
1742 this.update_epoll_active_events(socket.clone(), false)?;
1743 } else {
1744 this.update_epoll_active_events(socket.clone(), true)?;
1754 }
1755 interp_ok(result)
1756 }
1757 result => interp_ok(result),
1758 }
1759 }
1760
1761 fn block_for_recv(
1769 &mut self,
1770 socket: FileDescriptionRef<Socket>,
1771 deadline: Option<Deadline>,
1772 buffer_ptr: Pointer,
1773 length: usize,
1774 should_peek: bool,
1775 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1776 ) -> InterpResult<'tcx> {
1777 let this = self.eval_context_mut();
1778 this.block_thread_for_io(
1779 socket.clone(),
1780 BlockingIoInterest::Read,
1781 deadline.clone(),
1782 callback!(@capture<'tcx> {
1783 socket: FileDescriptionRef<Socket>,
1784 deadline: Option<Deadline>,
1785 buffer_ptr: Pointer,
1786 length: usize,
1787 should_peek: bool,
1788 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1789 } |this, kind: UnblockKind| {
1790 this.machine.blocking_io.remove_blocked_thread(socket.id(), this.machine.threads.active_thread());
1792
1793 match kind {
1794 UnblockKind::Ready => { },
1795 UnblockKind::TimedOut => return finish.call(this, Err(LibcError("EWOULDBLOCK")))
1797 }
1798
1799 match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {
1800 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1801 this.block_for_recv(socket, deadline, buffer_ptr, length, should_peek, finish)
1803 },
1804 result => finish.call(this, result)
1805 }
1806 }),
1807 )
1808 }
1809
1810 fn try_non_block_recv(
1815 &mut self,
1816 socket: &FileDescriptionRef<Socket>,
1817 buffer_ptr: Pointer,
1818 length: usize,
1819 should_peek: bool,
1820 ) -> InterpResult<'tcx, Result<usize, IoError>> {
1821 let this = self.eval_context_mut();
1822
1823 let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1824 panic!("try_non_block_recv must only be called when the socket is connected")
1825 };
1826
1827 let result = this.read_from_host(
1829 |buf| {
1830 if should_peek { stream.peek(buf) } else { stream.read(buf) }
1831 },
1832 length,
1833 buffer_ptr,
1834 )?;
1835 match result {
1836 Err(IoError::HostError(e))
1837 if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) =>
1838 {
1839 socket.io_readiness.borrow_mut().readable = false;
1841 this.update_epoll_active_events(socket.clone(), false)?;
1842
1843 interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1846 }
1847 Ok(bytes_read) if !should_peek && bytes_read < length && bytes_read > 0 => {
1848 if cfg!(any(
1856 target_os = "android",
1858 target_os = "illumos",
1859 target_os = "linux",
1860 target_os = "redox",
1861 target_os = "dragonfly",
1863 target_os = "freebsd",
1864 target_os = "ios",
1865 target_os = "macos",
1866 target_os = "netbsd",
1867 target_os = "openbsd",
1868 target_os = "tvos",
1869 target_os = "visionos",
1870 target_os = "watchos",
1871 )) {
1872 socket.io_readiness.borrow_mut().readable = false;
1873 this.update_epoll_active_events(socket.clone(), false)?;
1874 } else {
1875 this.update_epoll_active_events(socket.clone(), true)?;
1885 }
1886 interp_ok(result)
1887 }
1888 result => interp_ok(result),
1889 }
1890 }
1891
1892 fn ensure_connected(
1905 &mut self,
1906 socket: FileDescriptionRef<Socket>,
1907 deadline: Option<Deadline>,
1908 foreign_name: &'static str,
1909 action: DynMachineCallback<'tcx, Result<(), ()>>,
1910 ) -> InterpResult<'tcx> {
1911 let this = self.eval_context_mut();
1912
1913 let state = socket.state.borrow();
1914 match &*state {
1915 SocketState::Connecting(_) => { }
1916 SocketState::Connected(_) => {
1917 drop(state);
1918 return action.call(this, Ok(()));
1919 }
1920 _ => {
1921 drop(state);
1922 this.ensure_not_failed(&socket, foreign_name)?;
1923 return action.call(this, Err(()));
1924 }
1925 };
1926
1927 drop(state);
1928
1929 this.block_thread_for_io(
1933 socket.clone(),
1934 BlockingIoInterest::Write,
1935 deadline,
1936 callback!(
1937 @capture<'tcx> {
1938 socket: FileDescriptionRef<Socket>,
1939 foreign_name: &'static str,
1940 action: DynMachineCallback<'tcx, Result<(), ()>>,
1941 } |this, kind: UnblockKind| {
1942 this.machine.blocking_io.remove_blocked_thread(socket.id(), this.machine.threads.active_thread());
1944
1945 if UnblockKind::TimedOut == kind {
1946 return action.call(this, Err(()))
1948 }
1949
1950 let state = socket.state.borrow();
1953 match &*state {
1954 SocketState::Connecting(_) => { },
1955 SocketState::Connected(_) => {
1956 drop(state);
1957 return action.call(this, Ok(()))
1960 },
1961 _ => {
1962 drop(state);
1963 this.ensure_not_failed(&socket, foreign_name)?;
1968 return action.call(this, Err(()))
1969 }
1970 };
1971
1972 drop(state);
1973
1974 this.update_last_error(&socket);
1976
1977 if socket.error.borrow().is_some() {
1978 return action.call(this, Err(()))
1981 }
1982
1983 let mut state = socket.state.borrow_mut();
2002 let SocketState::Connecting(stream) = std::mem::replace(&mut*state, SocketState::Initial) else {
2003 unreachable!()
2005 };
2006 *state = SocketState::Connected(stream);
2007 drop(state);
2008 action.call(this, Ok(()))
2009 }
2010 ),
2011 )
2012 }
2013
2014 fn ensure_not_failed(
2018 &self,
2019 socket: &FileDescriptionRef<Socket>,
2020 foreign_name: &'static str,
2021 ) -> InterpResult<'tcx> {
2022 if let SocketState::ConnectionFailed(_) = &*socket.state.borrow() {
2023 throw_unsup_format!(
2024 "{foreign_name}: sockets are in an unspecified state after a failed `connect`; \
2025 any operation on such a socket is thus unsupported"
2026 );
2027 } else {
2028 interp_ok(())
2029 }
2030 }
2031
2032 fn update_last_error(&self, socket: &FileDescriptionRef<Socket>) {
2040 let mut state = socket.state.borrow_mut();
2041
2042 let new_error = match &*state {
2043 SocketState::Listening(listener) =>
2044 listener.take_error().expect("Reading SO_ERROR should not fail"),
2045 SocketState::Connecting(stream) | SocketState::Connected(stream) =>
2046 stream.take_error().expect("Reading SO_ERROR should not fail"),
2047 SocketState::Initial | SocketState::Bound(_) | SocketState::ConnectionFailed(_) => None,
2048 };
2049
2050 let Some(new_error) = new_error else { return };
2051
2052 socket.error.replace(Some(new_error));
2055
2056 if matches!(&*state, SocketState::Connecting(_)) {
2057 let SocketState::Connecting(stream) =
2064 std::mem::replace(&mut *state, SocketState::Initial)
2065 else {
2066 unreachable!()
2067 };
2068 *state = SocketState::ConnectionFailed(stream);
2069 }
2070 }
2071}
2072
2073impl VisitProvenance for FileDescriptionRef<Socket> {
2074 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
2077}
2078
2079impl SourceFileDescription for Socket {
2080 fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()> {
2081 let mut state = self.state.borrow_mut();
2082 match &mut *state {
2083 SocketState::Listening(listener) => f(listener),
2084 SocketState::Connecting(stream)
2085 | SocketState::Connected(stream)
2086 | SocketState::ConnectionFailed(stream) => f(stream),
2087 _ => unreachable!(),
2089 }
2090 }
2091
2092 fn get_readiness_mut(&self) -> RefMut<'_, BlockingIoSourceReadiness> {
2093 self.io_readiness.borrow_mut()
2094 }
2095}