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;
6
7use mio::event::Source;
8use mio::net::{TcpListener, TcpStream};
9use rustc_abi::Size;
10use rustc_const_eval::interpret::{InterpResult, interp_ok};
11use rustc_middle::throw_unsup_format;
12use rustc_target::spec::Os;
13
14use crate::shims::files::{EvalContextExt as _, FdId, FileDescription, FileDescriptionRef};
15use crate::shims::unix::UnixFileDescription;
16use crate::shims::unix::linux_like::epoll::{EpollReadiness, EvalContextExt as _};
17use crate::shims::unix::socket_address::EvalContextExt as _;
18use crate::*;
19
20#[derive(Debug, PartialEq)]
21enum SocketFamily {
22 IPv4,
24 IPv6,
26}
27
28#[derive(Debug)]
29enum SocketState {
30 Initial,
32 Bound(SocketAddr),
35 Listening(TcpListener),
38 Connecting(TcpStream),
42 Connected(TcpStream),
48 ConnectionFailed(TcpStream),
55}
56
57#[derive(Debug)]
58struct Socket {
59 family: SocketFamily,
62 state: RefCell<SocketState>,
64 is_non_block: Cell<bool>,
66 io_readiness: RefCell<BlockingIoSourceReadiness>,
68 error: RefCell<Option<io::Error>>,
70}
71
72impl FileDescription for Socket {
73 fn name(&self) -> &'static str {
74 "socket"
75 }
76
77 fn destroy<'tcx>(
78 self,
79 self_id: FdId,
80 communicate_allowed: bool,
81 ecx: &mut MiriInterpCx<'tcx>,
82 ) -> InterpResult<'tcx, io::Result<()>> {
83 assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
84
85 if matches!(
86 &*self.state.borrow(),
87 SocketState::Listening(_)
88 | SocketState::Connecting(_)
89 | SocketState::Connected(_)
90 | SocketState::ConnectionFailed(_)
91 ) {
92 ecx.machine.blocking_io.deregister(self_id, self)
95 };
96
97 interp_ok(Ok(()))
98 }
99
100 fn read<'tcx>(
101 self: FileDescriptionRef<Self>,
102 communicate_allowed: bool,
103 ptr: Pointer,
104 len: usize,
105 ecx: &mut MiriInterpCx<'tcx>,
106 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
107 ) -> InterpResult<'tcx> {
108 assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
109
110 let socket = self;
111
112 ecx.ensure_connected(
113 socket.clone(),
114 !socket.is_non_block.get(),
115 "read",
116 callback!(
117 @capture<'tcx> {
118 socket: FileDescriptionRef<Socket>,
119 ptr: Pointer,
120 len: usize,
121 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
122 } |this, result: Result<(), ()>| {
123 if result.is_err() {
124 return finish.call(this, Err(LibcError("ENOTCONN")))
125 }
126
127 if socket.is_non_block.get() {
131 let result = this.try_non_block_recv(&socket, ptr, len, false)?;
134 finish.call(this, result)
135 } else {
136 this.block_for_recv(socket, ptr, len, false, finish)
139 }
140 }
141 ),
142 )
143 }
144
145 fn write<'tcx>(
146 self: FileDescriptionRef<Self>,
147 communicate_allowed: bool,
148 ptr: Pointer,
149 len: usize,
150 ecx: &mut MiriInterpCx<'tcx>,
151 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
152 ) -> InterpResult<'tcx> {
153 assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
154
155 let socket = self;
156
157 ecx.ensure_connected(
158 socket.clone(),
159 !socket.is_non_block.get(),
160 "write",
161 callback!(
162 @capture<'tcx> {
163 socket: FileDescriptionRef<Socket>,
164 ptr: Pointer,
165 len: usize,
166 finish: DynMachineCallback<'tcx, Result<usize, IoError>>
167 } |this, result: Result<(), ()>| {
168 if result.is_err() {
169 return finish.call(this, Err(LibcError("ENOTCONN")))
170 }
171
172 if socket.is_non_block.get() {
176 let result = this.try_non_block_send(&socket, ptr, len)?;
179 return finish.call(this, result)
180 } else {
181 this.block_for_send(socket, ptr, len, finish)
184 }
185 }
186 ),
187 )
188 }
189
190 fn short_fd_operations(&self) -> bool {
191 false
196 }
197
198 fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {
199 self
200 }
201
202 fn get_flags<'tcx>(&self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> {
203 let mut flags = ecx.eval_libc_i32("O_RDWR");
204
205 if self.is_non_block.get() {
206 flags |= ecx.eval_libc_i32("O_NONBLOCK");
207 }
208
209 interp_ok(Scalar::from_i32(flags))
210 }
211
212 fn set_flags<'tcx>(
213 &self,
214 mut flag: i32,
215 ecx: &mut MiriInterpCx<'tcx>,
216 ) -> InterpResult<'tcx, Scalar> {
217 let o_nonblock = ecx.eval_libc_i32("O_NONBLOCK");
218
219 if flag & o_nonblock == o_nonblock {
221 self.is_non_block.set(true);
222 flag &= !o_nonblock;
223 } else {
224 self.is_non_block.set(false);
225 }
226
227 if flag != 0 {
229 throw_unsup_format!("fcntl: only O_NONBLOCK is supported for sockets")
230 }
231
232 interp_ok(Scalar::from_i32(0))
233 }
234}
235
236impl UnixFileDescription for Socket {
237 fn ioctl<'tcx>(
238 &self,
239 op: Scalar,
240 arg: Option<&OpTy<'tcx>>,
241 ecx: &mut MiriInterpCx<'tcx>,
242 ) -> InterpResult<'tcx, i32> {
243 assert!(ecx.machine.communicate(), "cannot have `Socket` with isolation enabled!");
244
245 let fionbio = ecx.eval_libc("FIONBIO");
246
247 if op == fionbio {
248 if !matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android | Os::MacOs | Os::FreeBsd)
251 {
252 throw_unsup_format!(
257 "ioctl: setting FIONBIO on sockets is unsupported on target {}",
258 ecx.tcx.sess.target.os
259 );
260 }
261
262 let Some(value_ptr) = arg else {
263 throw_ub_format!("ioctl: setting FIONBIO on sockets requires a third argument");
264 };
265 let value = ecx.deref_pointer_as(value_ptr, ecx.machine.layouts.i32)?;
266 let non_block = ecx.read_scalar(&value)?.to_i32()? != 0;
267 self.is_non_block.set(non_block);
268 return interp_ok(0);
269 }
270
271 throw_unsup_format!("ioctl: unsupported operation {op:#x} on socket");
272 }
273
274 fn epoll_active_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadiness> {
275 interp_ok(EpollReadiness::from(&*self.io_readiness.borrow()))
276 }
277}
278
279impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
280pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
281 fn socket(
284 &mut self,
285 domain: &OpTy<'tcx>,
286 type_: &OpTy<'tcx>,
287 protocol: &OpTy<'tcx>,
288 ) -> InterpResult<'tcx, Scalar> {
289 let this = self.eval_context_mut();
290
291 let domain = this.read_scalar(domain)?.to_i32()?;
292 let mut flags = this.read_scalar(type_)?.to_i32()?;
293 let protocol = this.read_scalar(protocol)?.to_i32()?;
294
295 if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
297 this.reject_in_isolation("`socket`", reject_with)?;
298 return this.set_last_error_and_return_i32(LibcError("EACCES"));
299 }
300
301 let mut is_sock_nonblock = false;
302
303 if matches!(
306 this.tcx.sess.target.os,
307 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
308 ) {
309 let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
312 let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
313 if flags & sock_nonblock == sock_nonblock {
314 is_sock_nonblock = true;
315 flags &= !sock_nonblock;
316 }
317 if flags & sock_cloexec == sock_cloexec {
318 flags &= !sock_cloexec;
320 }
321 }
322
323 let family = if domain == this.eval_libc_i32("AF_INET") {
324 SocketFamily::IPv4
325 } else if domain == this.eval_libc_i32("AF_INET6") {
326 SocketFamily::IPv6
327 } else {
328 throw_unsup_format!(
329 "socket: domain {:#x} is unsupported, only AF_INET and \
330 AF_INET6 are allowed.",
331 domain
332 );
333 };
334
335 if flags != this.eval_libc_i32("SOCK_STREAM") {
336 throw_unsup_format!(
337 "socket: type {:#x} is unsupported, only SOCK_STREAM, \
338 SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
339 flags
340 );
341 }
342 if protocol != 0 && protocol != this.eval_libc_i32("IPPROTO_TCP") {
343 throw_unsup_format!(
344 "socket: socket protocol {protocol} is unsupported, \
345 only IPPROTO_TCP and 0 are allowed"
346 );
347 }
348
349 let fds = &mut this.machine.fds;
350 let fd = fds.new_ref(Socket {
351 family,
352 state: RefCell::new(SocketState::Initial),
353 is_non_block: Cell::new(is_sock_nonblock),
354 io_readiness: RefCell::new(BlockingIoSourceReadiness::empty()),
355 error: RefCell::new(None),
356 });
357
358 interp_ok(Scalar::from_i32(fds.insert(fd)))
359 }
360
361 fn bind(
362 &mut self,
363 socket: &OpTy<'tcx>,
364 address: &OpTy<'tcx>,
365 address_len: &OpTy<'tcx>,
366 ) -> InterpResult<'tcx, Scalar> {
367 let this = self.eval_context_mut();
368
369 let socket = this.read_scalar(socket)?.to_i32()?;
370 let address = match this.read_socket_address(address, address_len, "bind")? {
371 Ok(addr) => addr,
372 Err(e) => return this.set_last_error_and_return_i32(e),
373 };
374
375 let Some(fd) = this.machine.fds.get(socket) else {
377 return this.set_last_error_and_return_i32(LibcError("EBADF"));
378 };
379
380 let Some(socket) = fd.downcast::<Socket>() else {
381 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
383 };
384
385 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
386 this.ensure_not_failed(&socket, "bind")?;
387
388 let mut state = socket.state.borrow_mut();
389
390 match *state {
391 SocketState::Initial => {
392 let address_family = match &address {
393 SocketAddr::V4(_) => SocketFamily::IPv4,
394 SocketAddr::V6(_) => SocketFamily::IPv6,
395 };
396
397 if socket.family != address_family {
398 let err = if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android) {
401 LibcError("EINVAL")
404 } else {
405 LibcError("EAFNOSUPPORT")
409 };
410 return this.set_last_error_and_return_i32(err);
411 }
412
413 *state = SocketState::Bound(address);
414 }
415 SocketState::Connecting(_) | SocketState::Connected(_) =>
416 throw_unsup_format!(
417 "bind: socket is already connected and binding a
418 connected socket is unsupported"
419 ),
420 SocketState::Bound(_) | SocketState::Listening(_) =>
421 throw_unsup_format!(
422 "bind: socket is already bound and binding a socket \
423 multiple times is unsupported"
424 ),
425 SocketState::ConnectionFailed(_) => unreachable!(),
426 }
427
428 interp_ok(Scalar::from_i32(0))
429 }
430
431 fn listen(&mut self, socket: &OpTy<'tcx>, backlog: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
432 let this = self.eval_context_mut();
433
434 let socket = this.read_scalar(socket)?.to_i32()?;
435 let _backlog = this.read_scalar(backlog)?.to_i32()?;
437
438 let Some(fd) = this.machine.fds.get(socket) else {
440 return this.set_last_error_and_return_i32(LibcError("EBADF"));
441 };
442
443 let Some(socket) = fd.downcast::<Socket>() else {
444 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
446 };
447
448 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
449 this.ensure_not_failed(&socket, "listen")?;
450
451 let mut state = socket.state.borrow_mut();
452
453 match *state {
454 SocketState::Bound(socket_addr) =>
455 match TcpListener::bind(socket_addr) {
456 Ok(listener) => {
457 *state = SocketState::Listening(listener);
458 drop(state);
459 this.machine.blocking_io.register(socket);
462 }
463 Err(e) => return this.set_last_error_and_return_i32(e),
464 },
465 SocketState::Initial => {
466 throw_unsup_format!(
467 "listen: listening on a socket which isn't bound is unsupported"
468 )
469 }
470 SocketState::Listening(_) => {
471 throw_unsup_format!("listen: listening on a socket multiple times is unsupported")
472 }
473 SocketState::Connecting(_) | SocketState::Connected(_) => {
474 throw_unsup_format!("listen: listening on a connected socket is unsupported")
475 }
476 SocketState::ConnectionFailed(_) => unreachable!(),
477 }
478
479 interp_ok(Scalar::from_i32(0))
480 }
481
482 fn accept4(
485 &mut self,
486 socket: &OpTy<'tcx>,
487 address: &OpTy<'tcx>,
488 address_len: &OpTy<'tcx>,
489 flags: Option<&OpTy<'tcx>>,
490 dest: &MPlaceTy<'tcx>,
492 ) -> InterpResult<'tcx> {
493 let this = self.eval_context_mut();
494
495 let socket = this.read_scalar(socket)?.to_i32()?;
496 let address_ptr = this.read_pointer(address)?;
497 let address_len_ptr = this.read_pointer(address_len)?;
498 let mut flags =
499 if let Some(flags) = flags { this.read_scalar(flags)?.to_i32()? } else { 0 };
500
501 let Some(fd) = this.machine.fds.get(socket) else {
503 return this.set_last_error_and_return(LibcError("EBADF"), dest);
504 };
505
506 let Some(socket) = fd.downcast::<Socket>() else {
507 return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
509 };
510
511 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
512 this.ensure_not_failed(&socket, "accept4")?;
513
514 if !matches!(*socket.state.borrow(), SocketState::Listening(_)) {
515 throw_unsup_format!(
516 "accept4: accepting incoming connections is only allowed when socket is listening"
517 )
518 };
519
520 let mut is_client_sock_nonblock = false;
521
522 if matches!(
525 this.tcx.sess.target.os,
526 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
527 ) {
528 let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
531 let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
532 if flags & sock_nonblock == sock_nonblock {
533 is_client_sock_nonblock = true;
534 flags &= !sock_nonblock;
535 }
536 if flags & sock_cloexec == sock_cloexec {
537 flags &= !sock_cloexec;
539 }
540 }
541
542 if flags != 0 {
543 throw_unsup_format!(
544 "accept4: flag {flags:#x} is unsupported, only SOCK_CLOEXEC \
545 and SOCK_NONBLOCK are allowed",
546 );
547 }
548
549 if socket.is_non_block.get() {
550 match this.try_non_block_accept(
553 &socket,
554 address_ptr,
555 address_len_ptr,
556 is_client_sock_nonblock,
557 )? {
558 Ok(sockfd) => {
559 this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), dest)
564 }
565 Err(e) => this.set_last_error_and_return(e, dest),
566 }
567 } else {
568 this.block_for_accept(
571 socket,
572 address_ptr,
573 address_len_ptr,
574 is_client_sock_nonblock,
575 dest.clone(),
576 )
577 }
578 }
579
580 fn connect(
581 &mut self,
582 socket: &OpTy<'tcx>,
583 address: &OpTy<'tcx>,
584 address_len: &OpTy<'tcx>,
585 dest: &MPlaceTy<'tcx>,
587 ) -> InterpResult<'tcx> {
588 let this = self.eval_context_mut();
589
590 let socket = this.read_scalar(socket)?.to_i32()?;
591 let address = match this.read_socket_address(address, address_len, "connect")? {
592 Ok(address) => address,
593 Err(e) => return this.set_last_error_and_return(e, dest),
594 };
595
596 let Some(fd) = this.machine.fds.get(socket) else {
598 return this.set_last_error_and_return(LibcError("EBADF"), dest);
599 };
600
601 let Some(socket) = fd.downcast::<Socket>() else {
602 return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
604 };
605
606 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
607 this.ensure_not_failed(&socket, "connect")?;
608
609 match &*socket.state.borrow() {
610 SocketState::Initial => { }
611 SocketState::Connecting(_) =>
613 return this.set_last_error_and_return(LibcError("EALREADY"), dest),
614 _ =>
618 throw_unsup_format!(
619 "connect: connecting is only supported for sockets which are neither \
620 bound, listening nor already connected"
621 ),
622 }
623
624 match TcpStream::connect(address) {
627 Ok(stream) => {
628 *socket.state.borrow_mut() = SocketState::Connecting(stream);
629 this.machine.blocking_io.register(socket.clone());
632 }
633 Err(e) => return this.set_last_error_and_return(e, dest),
634 };
635
636 if socket.is_non_block.get() {
637 this.set_last_error_and_return(LibcError("EINPROGRESS"), dest)
644 } else {
645 let dest = dest.clone();
649
650 this.ensure_connected(
651 socket.clone(),
652 true,
653 "connect",
654 callback!(
655 @capture<'tcx> {
656 socket: FileDescriptionRef<Socket>,
657 dest: MPlaceTy<'tcx>
658 } |this, result: Result<(), ()>| {
659 if result.is_err() {
660 let err = socket.error.take().unwrap();
664 this.set_last_error_and_return(err, &dest)
665 } else {
666 this.write_scalar(Scalar::from_i32(0), &dest)
667 }
668 }
669 ),
670 )
671 }
672 }
673
674 fn send(
675 &mut self,
676 socket: &OpTy<'tcx>,
677 buffer: &OpTy<'tcx>,
678 length: &OpTy<'tcx>,
679 flags: &OpTy<'tcx>,
680 dest: &MPlaceTy<'tcx>,
682 ) -> InterpResult<'tcx> {
683 let this = self.eval_context_mut();
684
685 let socket = this.read_scalar(socket)?.to_i32()?;
686 let buffer_ptr = this.read_pointer(buffer)?;
687 let size_layout = this.libc_ty_layout("size_t");
688 let length: usize =
689 this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
690 let mut flags = this.read_scalar(flags)?.to_i32()?;
691
692 let Some(fd) = this.machine.fds.get(socket) else {
694 return this.set_last_error_and_return(LibcError("EBADF"), dest);
695 };
696
697 let Some(socket) = fd.downcast::<Socket>() else {
698 return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
700 };
701
702 let mut is_op_non_block = false;
703
704 if matches!(
707 this.tcx.sess.target.os,
708 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
709 ) {
710 let msg_nosignal = this.eval_libc_i32("MSG_NOSIGNAL");
713 let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
714 if flags & msg_nosignal == msg_nosignal {
715 flags &= !msg_nosignal;
719 }
720 if flags & msg_dontwait == msg_dontwait {
721 flags &= !msg_dontwait;
722 is_op_non_block = true;
723 }
724 }
725
726 if flags != 0 {
727 throw_unsup_format!(
728 "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL and MSG_DONTWAIT are allowed",
729 );
730 }
731
732 let should_wait = !is_op_non_block && !socket.is_non_block.get();
735 let dest = dest.clone();
736
737 this.ensure_connected(
738 socket.clone(),
739 should_wait,
740 "send",
741 callback!(
742 @capture<'tcx> {
743 socket: FileDescriptionRef<Socket>,
744 flags: i32,
745 buffer_ptr: Pointer,
746 length: usize,
747 is_op_non_block: bool,
748 dest: MPlaceTy<'tcx>,
749 } |this, result: Result<(), ()>| {
750 if result.is_err() {
751 return this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
752 }
753
754 if is_op_non_block || socket.is_non_block.get() {
755 match this.try_non_block_send(&socket, buffer_ptr, length)? {
758 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
759 Err(e) => this.set_last_error_and_return(e, &dest),
760 }
761 } else {
762 this.block_for_send(
765 socket,
766 buffer_ptr,
767 length,
768 callback!(@capture<'tcx> {
769 dest: MPlaceTy<'tcx>
770 } |this, result: Result<usize, IoError>| {
771 match result {
772 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
773 Err(e) => this.set_last_error_and_return(e, &dest)
774 }
775 }),
776 )
777 }
778 }
779 ),
780 )
781 }
782
783 fn recv(
784 &mut self,
785 socket: &OpTy<'tcx>,
786 buffer: &OpTy<'tcx>,
787 length: &OpTy<'tcx>,
788 flags: &OpTy<'tcx>,
789 dest: &MPlaceTy<'tcx>,
791 ) -> InterpResult<'tcx> {
792 let this = self.eval_context_mut();
793
794 let socket = this.read_scalar(socket)?.to_i32()?;
795 let buffer_ptr = this.read_pointer(buffer)?;
796 let size_layout = this.libc_ty_layout("size_t");
797 let length: usize =
798 this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
799 let mut flags = this.read_scalar(flags)?.to_i32()?;
800
801 let Some(fd) = this.machine.fds.get(socket) else {
803 return this.set_last_error_and_return(LibcError("EBADF"), dest);
804 };
805
806 let Some(socket) = fd.downcast::<Socket>() else {
807 return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
809 };
810
811 let mut should_peek = false;
812 let mut is_op_non_block = false;
813
814 let msg_peek = this.eval_libc_i32("MSG_PEEK");
818 if flags & msg_peek == msg_peek {
819 should_peek = true;
820 flags &= !msg_peek;
821 }
822
823 if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
824 let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC");
827 if flags & msg_cmsg_cloexec == msg_cmsg_cloexec {
828 flags &= !msg_cmsg_cloexec;
830 }
831 }
832
833 if matches!(
834 this.tcx.sess.target.os,
835 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
836 ) {
837 let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
840 if flags & msg_dontwait == msg_dontwait {
841 flags &= !msg_dontwait;
842 is_op_non_block = true;
843 }
844 }
845
846 if flags != 0 {
847 throw_unsup_format!(
848 "recv: flag {flags:#x} is unsupported, only MSG_PEEK, MSG_DONTWAIT \
849 and MSG_CMSG_CLOEXEC are allowed",
850 );
851 }
852
853 let should_wait = !is_op_non_block && !socket.is_non_block.get();
856 let dest = dest.clone();
857
858 this.ensure_connected(
859 socket.clone(),
860 should_wait,
861 "recv",
862 callback!(
863 @capture<'tcx> {
864 socket: FileDescriptionRef<Socket>,
865 buffer_ptr: Pointer,
866 length: usize,
867 should_peek: bool,
868 is_op_non_block: bool,
869 dest: MPlaceTy<'tcx>,
870 } |this, result: Result<(), ()>| {
871 if result.is_err() {
872 return this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
873 }
874
875 if is_op_non_block || socket.is_non_block.get() {
876 match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {
879 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
880 Err(e) => this.set_last_error_and_return(e, &dest),
881 }
882 } else {
883 this.block_for_recv(
886 socket,
887 buffer_ptr,
888 length,
889 should_peek,
890 callback!(@capture<'tcx> {
891 dest: MPlaceTy<'tcx>
892 } |this, result: Result<usize, IoError>| {
893 match result {
894 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
895 Err(e) => this.set_last_error_and_return(e, &dest)
896 }
897 }),
898 )
899 }
900 }
901 ),
902 )
903 }
904
905 fn setsockopt(
906 &mut self,
907 socket: &OpTy<'tcx>,
908 level: &OpTy<'tcx>,
909 option_name: &OpTy<'tcx>,
910 option_value: &OpTy<'tcx>,
911 option_len: &OpTy<'tcx>,
912 ) -> InterpResult<'tcx, Scalar> {
913 let this = self.eval_context_mut();
914
915 let socket = this.read_scalar(socket)?.to_i32()?;
916 let level = this.read_scalar(level)?.to_i32()?;
917 let option_name = this.read_scalar(option_name)?.to_i32()?;
918 let option_value_ptr = this.read_pointer(option_value)?;
919 let socklen_layout = this.libc_ty_layout("socklen_t");
920 let option_len = this.read_scalar(option_len)?.to_int(socklen_layout.size)?;
921
922 let Some(fd) = this.machine.fds.get(socket) else {
924 return this.set_last_error_and_return_i32(LibcError("EBADF"));
925 };
926
927 let Some(socket) = fd.downcast::<Socket>() else {
928 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
930 };
931
932 if level == this.eval_libc_i32("SOL_SOCKET") {
933 let opt_so_reuseaddr = this.eval_libc_i32("SO_REUSEADDR");
934
935 if matches!(this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::NetBsd) {
936 let opt_so_nosigpipe = this.eval_libc_i32("SO_NOSIGPIPE");
938
939 if option_name == opt_so_nosigpipe {
940 if option_len != 4 {
941 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
943 }
944 let option_value =
945 this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32);
946 let _val = this.read_scalar(&option_value)?.to_i32()?;
947 return interp_ok(Scalar::from_i32(0));
950 }
951 }
952
953 if option_name == opt_so_reuseaddr {
954 if option_len != 4 {
955 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
957 }
958 let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32);
959 let _val = this.read_scalar(&option_value)?.to_i32()?;
960 return interp_ok(Scalar::from_i32(0));
963 } else {
964 throw_unsup_format!(
965 "setsockopt: option {option_name:#x} is unsupported for level SOL_SOCKET",
966 );
967 }
968 } else if level == this.eval_libc_i32("IPPROTO_IP") {
969 let opt_ip_ttl = this.eval_libc_i32("IP_TTL");
970
971 if option_name == opt_ip_ttl {
972 if option_len != 4 {
973 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
975 }
976 let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.u32);
977 let ttl = this.read_scalar(&option_value)?.to_u32()?;
978
979 let result = match &*socket.state.borrow() {
980 SocketState::Initial | SocketState::Bound(_) =>
981 throw_unsup_format!(
982 "setsockopt: setting option IP_TTL on level IPPROTO_IP is only supported \
983 on connected and listening sockets"
984 ),
985 SocketState::Listening(listener) => listener.set_ttl(ttl),
986 SocketState::Connecting(stream) | SocketState::Connected(stream) =>
987 stream.set_ttl(ttl),
988 SocketState::ConnectionFailed(_) => unreachable!(),
989 };
990
991 return match result {
992 Ok(_) => interp_ok(Scalar::from_i32(0)),
993 Err(e) => this.set_last_error_and_return_i32(e),
994 };
995 } else {
996 throw_unsup_format!(
997 "setsockopt: option {option_name:#x} is unsupported for level IPPROTO_IP",
998 );
999 }
1000 } else if level == this.eval_libc_i32("IPPROTO_TCP") {
1001 let opt_tcp_nodelay = this.eval_libc_i32("TCP_NODELAY");
1002
1003 if option_name == opt_tcp_nodelay {
1004 if option_len != 4 {
1005 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
1007 }
1008 let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32);
1009 let nodelay = this.read_scalar(&option_value)?.to_i32()? != 0;
1010
1011 let result = match &*socket.state.borrow() {
1012 SocketState::Initial | SocketState::Bound(_) | SocketState::Listening(_) =>
1013 throw_unsup_format!(
1014 "setsockopt: setting option TCP_NODELAY on level IPPROTO_TCP is only supported \
1015 on connected sockets"
1016 ),
1017 SocketState::Connecting(stream) | SocketState::Connected(stream) =>
1018 stream.set_nodelay(nodelay),
1019 SocketState::ConnectionFailed(_) => unreachable!(),
1020 };
1021
1022 return match result {
1023 Ok(_) => interp_ok(Scalar::from_i32(0)),
1024 Err(e) => this.set_last_error_and_return_i32(e),
1025 };
1026 } else {
1027 throw_unsup_format!(
1028 "setsockopt: option {option_name:#x} is unsupported for level IPPROTO_TCP"
1029 );
1030 }
1031 }
1032
1033 throw_unsup_format!(
1034 "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET, IPPROTO_IP \
1035 and IPPROTO_TCP are allowed"
1036 );
1037 }
1038
1039 fn getsockopt(
1040 &mut self,
1041 socket: &OpTy<'tcx>,
1042 level: &OpTy<'tcx>,
1043 option_name: &OpTy<'tcx>,
1044 option_value: &OpTy<'tcx>,
1045 option_len: &OpTy<'tcx>,
1046 ) -> InterpResult<'tcx, Scalar> {
1047 let this = self.eval_context_mut();
1048
1049 let socket = this.read_scalar(socket)?.to_i32()?;
1050 let level = this.read_scalar(level)?.to_i32()?;
1051 let option_name = this.read_scalar(option_name)?.to_i32()?;
1052 let option_value_ptr = this.read_pointer(option_value)?;
1058 let option_len_ptr = this.read_pointer(option_len)?;
1059
1060 let Some(fd) = this.machine.fds.get(socket) else {
1062 return this.set_last_error_and_return_i32(LibcError("EBADF"));
1063 };
1064
1065 let Some(socket) = fd.downcast::<Socket>() else {
1066 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
1068 };
1069
1070 if option_value_ptr == Pointer::null() || option_len_ptr == Pointer::null() {
1071 return this.set_last_error_and_return_i32(LibcError("EFAULT"));
1074 }
1075
1076 let socklen_layout = this.libc_ty_layout("socklen_t");
1077 let option_len_ptr_mplace = this.ptr_to_mplace(option_len_ptr, socklen_layout);
1078 let option_len: usize = this
1079 .read_scalar(&option_len_ptr_mplace)?
1080 .to_int(socklen_layout.size)?
1081 .try_into()
1082 .unwrap();
1083
1084 let value_buffer = if level == this.eval_libc_i32("SOL_SOCKET") {
1087 let opt_so_error = this.eval_libc_i32("SO_ERROR");
1088
1089 if option_name == opt_so_error {
1090 this.update_last_error(&socket);
1093
1094 let return_value = match socket.error.take() {
1095 Some(err) => this.io_error_to_errnum(err)?.to_i32()?,
1096 None => 0,
1098 };
1099
1100 socket.error.replace(None);
1102
1103 socket.io_readiness.borrow_mut().error = false;
1106 this.update_epoll_active_events(socket, false)?;
1107
1108 let value_buffer = this.allocate(this.machine.layouts.i32, MemoryKind::Stack)?;
1110 this.write_int(return_value, &value_buffer)?;
1111 value_buffer
1112 } else {
1113 throw_unsup_format!(
1114 "getsockopt: option {option_name:#x} is unsupported for level SOL_SOCKET",
1115 );
1116 }
1117 } else if level == this.eval_libc_i32("IPPROTO_IP") {
1118 let opt_ip_ttl = this.eval_libc_i32("IP_TTL");
1119
1120 if option_name == opt_ip_ttl {
1121 let ttl = match &*socket.state.borrow() {
1122 SocketState::Initial | SocketState::Bound(_) =>
1123 throw_unsup_format!(
1124 "getsockopt: reading option IP_TTL on level IPPROTO_IP is only supported \
1125 on connected and listening sockets"
1126 ),
1127 SocketState::Listening(listener) => listener.ttl(),
1128 SocketState::Connecting(stream) | SocketState::Connected(stream) =>
1129 stream.ttl(),
1130 SocketState::ConnectionFailed(_) => unreachable!(),
1131 };
1132
1133 let ttl = match ttl {
1134 Ok(ttl) => ttl,
1135 Err(e) => return this.set_last_error_and_return_i32(e),
1136 };
1137
1138 let value_buffer = this.allocate(this.machine.layouts.u32, MemoryKind::Stack)?;
1140 this.write_int(ttl, &value_buffer)?;
1141 value_buffer
1142 } else {
1143 throw_unsup_format!(
1144 "getsockopt: option {option_name:#x} is unsupported for level IPPROTO_IP",
1145 );
1146 }
1147 } else if level == this.eval_libc_i32("IPPROTO_TCP") {
1148 let opt_tcp_nodelay = this.eval_libc_i32("TCP_NODELAY");
1149
1150 if option_name == opt_tcp_nodelay {
1151 let nodelay = match &*socket.state.borrow() {
1152 SocketState::Initial | SocketState::Bound(_) | SocketState::Listening(_) =>
1153 throw_unsup_format!(
1154 "getsockopt: reading option TCP_NODELAY on level IPPROTO_TCP is only supported \
1155 on connected sockets"
1156 ),
1157 SocketState::Connecting(stream) | SocketState::Connected(stream) =>
1158 stream.nodelay(),
1159 SocketState::ConnectionFailed(_) => unreachable!(),
1160 };
1161
1162 let nodelay = match nodelay {
1163 Ok(nodelay) => nodelay,
1164 Err(e) => return this.set_last_error_and_return_i32(e),
1165 };
1166
1167 let value_buffer = this.allocate(this.machine.layouts.i32, MemoryKind::Stack)?;
1169 this.write_int(i32::from(nodelay), &value_buffer)?;
1170 value_buffer
1171 } else {
1172 throw_unsup_format!(
1173 "getsockopt: option {option_name:#x} is unsupported for level IPPROTO_TCP"
1174 );
1175 }
1176 } else {
1177 throw_unsup_format!(
1178 "getsockopt: level {level:#x} is unsupported, only SOL_SOCKET, IPPROTO_IP \
1179 and IPPROTO_TCP are allowed"
1180 )
1181 };
1182
1183 let output_value_len = value_buffer.layout.size.min(Size::from_bytes(option_len));
1185 this.mem_copy(
1187 value_buffer.ptr(),
1188 option_value_ptr,
1189 output_value_len,
1191 true,
1194 )?;
1195 this.deallocate_ptr(value_buffer.ptr(), None, MemoryKind::Stack)?;
1198
1199 this.write_scalar(
1202 Scalar::from_uint(output_value_len.bytes(), socklen_layout.size),
1203 &option_len_ptr_mplace,
1204 )?;
1205
1206 interp_ok(Scalar::from_i32(0))
1207 }
1208
1209 fn getsockname(
1210 &mut self,
1211 socket: &OpTy<'tcx>,
1212 address: &OpTy<'tcx>,
1213 address_len: &OpTy<'tcx>,
1214 ) -> InterpResult<'tcx, Scalar> {
1215 let this = self.eval_context_mut();
1216
1217 let socket = this.read_scalar(socket)?.to_i32()?;
1218 let address_ptr = this.read_pointer(address)?;
1219 let address_len_ptr = this.read_pointer(address_len)?;
1220
1221 let Some(fd) = this.machine.fds.get(socket) else {
1223 return this.set_last_error_and_return_i32(LibcError("EBADF"));
1224 };
1225
1226 let Some(socket) = fd.downcast::<Socket>() else {
1227 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
1229 };
1230
1231 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
1232 this.ensure_not_failed(&socket, "getsockname")?;
1233
1234 let state = socket.state.borrow();
1235
1236 let address = match &*state {
1237 SocketState::Bound(address) => {
1238 if address.port() == 0 {
1239 throw_unsup_format!(
1243 "getsockname: when the port is 0, getting the socket address before \
1244 calling `listen` or `connect` is unsupported"
1245 )
1246 }
1247
1248 *address
1249 }
1250 SocketState::Listening(listener) =>
1251 match listener.local_addr() {
1252 Ok(address) => address,
1253 Err(e) => return this.set_last_error_and_return_i32(e),
1254 },
1255 SocketState::Connecting(stream) | SocketState::Connected(stream) => {
1256 if cfg!(windows) && matches!(&*state, SocketState::Connecting(_)) {
1257 static DEDUP: AtomicBool = AtomicBool::new(false);
1264 if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) {
1265 this.emit_diagnostic(NonHaltingDiagnostic::ConnectingSocketGetsockname);
1266 }
1267 }
1268 match stream.local_addr() {
1269 Ok(address) => address,
1270 Err(e) => return this.set_last_error_and_return_i32(e),
1271 }
1272 }
1273 SocketState::Initial => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)),
1276 SocketState::ConnectionFailed(_) => unreachable!(),
1277 };
1278
1279 this.write_socket_address(&address, address_ptr, address_len_ptr, "getsockname")
1280 .map(|_| Scalar::from_i32(0))
1281 }
1282
1283 fn getpeername(
1284 &mut self,
1285 socket: &OpTy<'tcx>,
1286 address: &OpTy<'tcx>,
1287 address_len: &OpTy<'tcx>,
1288 dest: &MPlaceTy<'tcx>,
1290 ) -> InterpResult<'tcx> {
1291 let this = self.eval_context_mut();
1292
1293 let socket = this.read_scalar(socket)?.to_i32()?;
1294 let address_ptr = this.read_pointer(address)?;
1295 let address_len_ptr = this.read_pointer(address_len)?;
1296
1297 let Some(fd) = this.machine.fds.get(socket) else {
1299 return this.set_last_error_and_return(LibcError("EBADF"), dest);
1300 };
1301
1302 let Some(socket) = fd.downcast::<Socket>() else {
1303 return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
1305 };
1306
1307 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
1308
1309 let dest = dest.clone();
1310
1311 this.ensure_connected(
1314 socket.clone(),
1315 false,
1316 "getpeername",
1317 callback!(
1318 @capture<'tcx> {
1319 socket: FileDescriptionRef<Socket>,
1320 address_ptr: Pointer,
1321 address_len_ptr: Pointer,
1322 dest: MPlaceTy<'tcx>,
1323 } |this, result: Result<(), ()>| {
1324 if result.is_err() {
1325 return this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
1326 };
1327
1328 let SocketState::Connected(stream) = &*socket.state.borrow() else {
1329 unreachable!()
1330 };
1331
1332 let address = match stream.peer_addr() {
1333 Ok(address) => address,
1334 Err(e) => return this.set_last_error_and_return(e, &dest),
1335 };
1336
1337 this.write_socket_address(
1338 &address,
1339 address_ptr,
1340 address_len_ptr,
1341 "getpeername",
1342 )?;
1343 this.write_scalar(Scalar::from_i32(0), &dest)
1344 }
1345 ),
1346 )
1347 }
1348
1349 fn shutdown(&mut self, socket: &OpTy<'tcx>, how: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
1350 let this = self.eval_context_mut();
1351
1352 let socket = this.read_scalar(socket)?.to_i32()?;
1353 let how = this.read_scalar(how)?.to_i32()?;
1354
1355 let Some(fd) = this.machine.fds.get(socket) else {
1357 return this.set_last_error_and_return_i32(LibcError("EBADF"));
1358 };
1359
1360 let Some(socket) = fd.downcast::<Socket>() else {
1361 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
1363 };
1364
1365 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
1366 this.ensure_not_failed(&socket, "shutdown")?;
1367
1368 let state = socket.state.borrow();
1369
1370 let (SocketState::Connecting(stream) | SocketState::Connected(stream)) = &*state else {
1371 return this.set_last_error_and_return_i32(LibcError("ENOTCONN"));
1372 };
1373
1374 let is_read_shutdown = how == this.eval_libc_i32("SHUT_RD");
1375 let is_write_shutdown = how == this.eval_libc_i32("SHUT_WR");
1376 let is_read_write_shutdown = how == this.eval_libc_i32("SHUT_RDWR");
1377
1378 let how = match () {
1379 _ if is_read_shutdown => Shutdown::Read,
1380 _ if is_write_shutdown => Shutdown::Write,
1381 _ if is_read_write_shutdown => Shutdown::Both,
1382 _ => return this.set_last_error_and_return_i32(LibcError("EINVAL")),
1384 };
1385
1386 if let Err(e) = stream.shutdown(how) {
1387 return this.set_last_error_and_return_i32(e);
1388 };
1389
1390 drop(state);
1391
1392 let mut readiness = socket.io_readiness.borrow_mut();
1398 readiness.read_closed |= is_read_shutdown || is_read_write_shutdown;
1400 readiness.write_closed |= is_read_write_shutdown;
1403 readiness.readable |= is_read_write_shutdown;
1406
1407 drop(readiness);
1408
1409 this.update_epoll_active_events(socket, false)?;
1411
1412 interp_ok(Scalar::from_i32(0))
1413 }
1414}
1415
1416impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
1417trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1418 fn block_for_accept(
1425 &mut self,
1426 socket: FileDescriptionRef<Socket>,
1427 address_ptr: Pointer,
1428 address_len_ptr: Pointer,
1429 is_client_sock_nonblock: bool,
1430 dest: MPlaceTy<'tcx>,
1431 ) -> InterpResult<'tcx> {
1432 let this = self.eval_context_mut();
1433 this.block_thread_for_io(
1434 socket.clone(),
1435 BlockingIoInterest::Read,
1436 None,
1437 callback!(@capture<'tcx> {
1438 address_ptr: Pointer,
1439 address_len_ptr: Pointer,
1440 is_client_sock_nonblock: bool,
1441 socket: FileDescriptionRef<Socket>,
1442 dest: MPlaceTy<'tcx>,
1443 } |this, kind: UnblockKind| {
1444 assert_eq!(kind, UnblockKind::Ready);
1445
1446 this.machine.blocking_io.remove_blocked_thread(socket.id(), this.machine.threads.active_thread());
1448
1449 match this.try_non_block_accept(&socket, address_ptr, address_len_ptr, is_client_sock_nonblock)? {
1450 Ok(sockfd) => {
1451 this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), &dest)
1456 },
1457 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1458 this.block_for_accept(socket, address_ptr, address_len_ptr, is_client_sock_nonblock, dest)
1460 }
1461 Err(e) => this.set_last_error_and_return(e, &dest),
1462 }
1463 }),
1464 )
1465 }
1466
1467 fn try_non_block_accept(
1473 &mut self,
1474 socket: &FileDescriptionRef<Socket>,
1475 address_ptr: Pointer,
1476 address_len_ptr: Pointer,
1477 is_client_sock_nonblock: bool,
1478 ) -> InterpResult<'tcx, Result<i32, IoError>> {
1479 let this = self.eval_context_mut();
1480
1481 let state = socket.state.borrow();
1482 let SocketState::Listening(listener) = &*state else {
1483 panic!(
1484 "try_non_block_accept must only be called when socket is in `SocketState::Listening`"
1485 )
1486 };
1487
1488 let (stream, addr) = match listener.accept() {
1489 Ok(peer) => peer,
1490 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
1491 socket.io_readiness.borrow_mut().readable = false;
1493 this.update_epoll_active_events(socket.clone(), false)?;
1494
1495 return interp_ok(Err(IoError::HostError(e)));
1496 }
1497 Err(e) => return interp_ok(Err(IoError::HostError(e))),
1498 };
1499
1500 let family = match addr {
1501 SocketAddr::V4(_) => SocketFamily::IPv4,
1502 SocketAddr::V6(_) => SocketFamily::IPv6,
1503 };
1504
1505 if address_ptr != Pointer::null() {
1506 this.write_socket_address(&addr, address_ptr, address_len_ptr, "accept4")?;
1510 }
1511
1512 let fd = this.machine.fds.new_ref(Socket {
1513 family,
1514 state: RefCell::new(SocketState::Connected(stream)),
1515 is_non_block: Cell::new(is_client_sock_nonblock),
1516 io_readiness: RefCell::new(BlockingIoSourceReadiness::empty()),
1517 error: RefCell::new(None),
1518 });
1519 this.machine.blocking_io.register(fd.clone());
1522 let sockfd = this.machine.fds.insert(fd);
1523 interp_ok(Ok(sockfd))
1524 }
1525
1526 fn block_for_send(
1534 &mut self,
1535 socket: FileDescriptionRef<Socket>,
1536 buffer_ptr: Pointer,
1537 length: usize,
1538 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1539 ) -> InterpResult<'tcx> {
1540 let this = self.eval_context_mut();
1541 this.block_thread_for_io(
1542 socket.clone(),
1543 BlockingIoInterest::Write,
1544 None,
1545 callback!(@capture<'tcx> {
1546 socket: FileDescriptionRef<Socket>,
1547 buffer_ptr: Pointer,
1548 length: usize,
1549 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1550 } |this, kind: UnblockKind| {
1551 assert_eq!(kind, UnblockKind::Ready);
1552
1553 this.machine.blocking_io.remove_blocked_thread(socket.id(), this.machine.threads.active_thread());
1555
1556 match this.try_non_block_send(&socket, buffer_ptr, length)? {
1557 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1558 this.block_for_send(socket, buffer_ptr, length, finish)
1560 },
1561 result => finish.call(this, result)
1562 }
1563 }),
1564 )
1565 }
1566
1567 fn try_non_block_send(
1572 &mut self,
1573 socket: &FileDescriptionRef<Socket>,
1574 buffer_ptr: Pointer,
1575 length: usize,
1576 ) -> InterpResult<'tcx, Result<usize, IoError>> {
1577 let this = self.eval_context_mut();
1578
1579 let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1580 panic!("try_non_block_send must only be called when the socket is connected")
1581 };
1582
1583 let result = this.write_to_host(stream, length, buffer_ptr)?;
1585 match result {
1586 Err(IoError::HostError(e))
1587 if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) =>
1588 {
1589 socket.io_readiness.borrow_mut().writable = false;
1591 this.update_epoll_active_events(socket.clone(), false)?;
1592
1593 interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1596 }
1597 Ok(bytes_written) if bytes_written < length => {
1598 if cfg!(any(
1604 target_os = "android",
1606 target_os = "illumos",
1607 target_os = "linux",
1608 target_os = "redox",
1609 target_os = "dragonfly",
1611 target_os = "freebsd",
1612 target_os = "ios",
1613 target_os = "macos",
1614 target_os = "netbsd",
1615 target_os = "openbsd",
1616 target_os = "tvos",
1617 target_os = "visionos",
1618 target_os = "watchos",
1619 )) {
1620 socket.io_readiness.borrow_mut().writable = false;
1621 this.update_epoll_active_events(socket.clone(), false)?;
1622 } else {
1623 this.update_epoll_active_events(socket.clone(), true)?;
1633 }
1634 interp_ok(result)
1635 }
1636 result => interp_ok(result),
1637 }
1638 }
1639
1640 fn block_for_recv(
1648 &mut self,
1649 socket: FileDescriptionRef<Socket>,
1650 buffer_ptr: Pointer,
1651 length: usize,
1652 should_peek: bool,
1653 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1654 ) -> InterpResult<'tcx> {
1655 let this = self.eval_context_mut();
1656 this.block_thread_for_io(
1657 socket.clone(),
1658 BlockingIoInterest::Read,
1659 None,
1660 callback!(@capture<'tcx> {
1661 socket: FileDescriptionRef<Socket>,
1662 buffer_ptr: Pointer,
1663 length: usize,
1664 should_peek: bool,
1665 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1666 } |this, kind: UnblockKind| {
1667 assert_eq!(kind, UnblockKind::Ready);
1668
1669 this.machine.blocking_io.remove_blocked_thread(socket.id(), this.machine.threads.active_thread());
1671
1672 match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {
1673 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1674 this.block_for_recv(socket, buffer_ptr, length, should_peek, finish)
1676 },
1677 result => finish.call(this, result)
1678 }
1679 }),
1680 )
1681 }
1682
1683 fn try_non_block_recv(
1688 &mut self,
1689 socket: &FileDescriptionRef<Socket>,
1690 buffer_ptr: Pointer,
1691 length: usize,
1692 should_peek: bool,
1693 ) -> InterpResult<'tcx, Result<usize, IoError>> {
1694 let this = self.eval_context_mut();
1695
1696 let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1697 panic!("try_non_block_recv must only be called when the socket is connected")
1698 };
1699
1700 let result = this.read_from_host(
1702 |buf| {
1703 if should_peek { stream.peek(buf) } else { stream.read(buf) }
1704 },
1705 length,
1706 buffer_ptr,
1707 )?;
1708 match result {
1709 Err(IoError::HostError(e))
1710 if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) =>
1711 {
1712 socket.io_readiness.borrow_mut().readable = false;
1714 this.update_epoll_active_events(socket.clone(), false)?;
1715
1716 interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1719 }
1720 Ok(bytes_read) if !should_peek && bytes_read < length && bytes_read > 0 => {
1721 if cfg!(any(
1729 target_os = "android",
1731 target_os = "illumos",
1732 target_os = "linux",
1733 target_os = "redox",
1734 target_os = "dragonfly",
1736 target_os = "freebsd",
1737 target_os = "ios",
1738 target_os = "macos",
1739 target_os = "netbsd",
1740 target_os = "openbsd",
1741 target_os = "tvos",
1742 target_os = "visionos",
1743 target_os = "watchos",
1744 )) {
1745 socket.io_readiness.borrow_mut().readable = false;
1746 this.update_epoll_active_events(socket.clone(), false)?;
1747 } else {
1748 this.update_epoll_active_events(socket.clone(), true)?;
1758 }
1759 interp_ok(result)
1760 }
1761 result => interp_ok(result),
1762 }
1763 }
1764
1765 fn ensure_connected(
1778 &mut self,
1779 socket: FileDescriptionRef<Socket>,
1780 should_wait: bool,
1781 foreign_name: &'static str,
1782 action: DynMachineCallback<'tcx, Result<(), ()>>,
1783 ) -> InterpResult<'tcx> {
1784 let this = self.eval_context_mut();
1785
1786 let state = socket.state.borrow();
1787 match &*state {
1788 SocketState::Connecting(_) => { }
1789 SocketState::Connected(_) => {
1790 drop(state);
1791 return action.call(this, Ok(()));
1792 }
1793 _ => {
1794 drop(state);
1795 this.ensure_not_failed(&socket, foreign_name)?;
1796 return action.call(this, Err(()));
1797 }
1798 };
1799
1800 drop(state);
1801
1802 let deadline =
1808 if should_wait { None } else { Some(this.machine.monotonic_clock.now().into()) };
1809
1810 this.block_thread_for_io(
1811 socket.clone(),
1812 BlockingIoInterest::Write,
1813 deadline,
1814 callback!(
1815 @capture<'tcx> {
1816 socket: FileDescriptionRef<Socket>,
1817 should_wait: bool,
1818 foreign_name: &'static str,
1819 action: DynMachineCallback<'tcx, Result<(), ()>>,
1820 } |this, kind: UnblockKind| {
1821 this.machine.blocking_io.remove_blocked_thread(socket.id(), this.machine.threads.active_thread());
1823
1824 if UnblockKind::TimedOut == kind {
1825 assert!(!should_wait);
1828 return action.call(this, Err(()))
1829 }
1830
1831 let state = socket.state.borrow();
1834 match &*state {
1835 SocketState::Connecting(_) => { },
1836 SocketState::Connected(_) => {
1837 drop(state);
1838 return action.call(this, Ok(()))
1841 },
1842 _ => {
1843 drop(state);
1844 this.ensure_not_failed(&socket, foreign_name)?;
1849 return action.call(this, Err(()))
1850 }
1851 };
1852
1853 drop(state);
1854
1855 this.update_last_error(&socket);
1857
1858 if socket.error.borrow().is_some() {
1859 return action.call(this, Err(()))
1862 }
1863
1864 let mut state = socket.state.borrow_mut();
1883 let SocketState::Connecting(stream) = std::mem::replace(&mut*state, SocketState::Initial) else {
1884 unreachable!()
1886 };
1887 *state = SocketState::Connected(stream);
1888 drop(state);
1889 action.call(this, Ok(()))
1890 }
1891 ),
1892 )
1893 }
1894
1895 fn ensure_not_failed(
1899 &self,
1900 socket: &FileDescriptionRef<Socket>,
1901 foreign_name: &'static str,
1902 ) -> InterpResult<'tcx> {
1903 if let SocketState::ConnectionFailed(_) = &*socket.state.borrow() {
1904 throw_unsup_format!(
1905 "{foreign_name}: sockets are in an unspecified state after a failed `connect`; \
1906 any operation on such a socket is thus unsupported"
1907 );
1908 } else {
1909 interp_ok(())
1910 }
1911 }
1912
1913 fn update_last_error(&self, socket: &FileDescriptionRef<Socket>) {
1921 let mut state = socket.state.borrow_mut();
1922
1923 let new_error = match &*state {
1924 SocketState::Listening(listener) =>
1925 listener.take_error().expect("Reading SO_ERROR should not fail"),
1926 SocketState::Connecting(stream) | SocketState::Connected(stream) =>
1927 stream.take_error().expect("Reading SO_ERROR should not fail"),
1928 SocketState::Initial | SocketState::Bound(_) | SocketState::ConnectionFailed(_) => None,
1929 };
1930
1931 let Some(new_error) = new_error else { return };
1932
1933 socket.error.replace(Some(new_error));
1936
1937 if matches!(&*state, SocketState::Connecting(_)) {
1938 let SocketState::Connecting(stream) =
1945 std::mem::replace(&mut *state, SocketState::Initial)
1946 else {
1947 unreachable!()
1948 };
1949 *state = SocketState::ConnectionFailed(stream);
1950 }
1951 }
1952}
1953
1954impl VisitProvenance for FileDescriptionRef<Socket> {
1955 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
1958}
1959
1960impl SourceFileDescription for Socket {
1961 fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()> {
1962 let mut state = self.state.borrow_mut();
1963 match &mut *state {
1964 SocketState::Listening(listener) => f(listener),
1965 SocketState::Connecting(stream)
1966 | SocketState::Connected(stream)
1967 | SocketState::ConnectionFailed(stream) => f(stream),
1968 _ => unreachable!(),
1970 }
1971 }
1972
1973 fn get_readiness_mut(&self) -> RefMut<'_, BlockingIoSourceReadiness> {
1974 self.io_readiness.borrow_mut()
1975 }
1976}