1use std::cell::{Cell, RefCell};
2use std::io::Read;
3use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
4use std::time::Duration;
5use std::{io, iter};
6
7use mio::Interest;
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::concurrency::blocking_io::InterestReceiver;
16use crate::shims::files::{EvalContextExt as _, FdId, FileDescription, FileDescriptionRef};
17use crate::shims::unix::UnixFileDescription;
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}
49
50#[derive(Debug)]
51struct Socket {
52 family: SocketFamily,
55 state: RefCell<SocketState>,
57 is_non_block: Cell<bool>,
59}
60
61impl FileDescription for Socket {
62 fn name(&self) -> &'static str {
63 "socket"
64 }
65
66 fn destroy<'tcx>(
67 self,
68 _self_id: FdId,
69 communicate_allowed: bool,
70 _ecx: &mut MiriInterpCx<'tcx>,
71 ) -> InterpResult<'tcx, std::io::Result<()>> {
72 assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
73
74 interp_ok(Ok(()))
75 }
76
77 fn read<'tcx>(
78 self: FileDescriptionRef<Self>,
79 communicate_allowed: bool,
80 ptr: Pointer,
81 len: usize,
82 ecx: &mut MiriInterpCx<'tcx>,
83 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
84 ) -> InterpResult<'tcx> {
85 assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
86
87 let socket = self;
88
89 ecx.ensure_connected(
90 socket.clone(),
91 !socket.is_non_block.get(),
92 "read",
93 callback!(
94 @capture<'tcx> {
95 socket: FileDescriptionRef<Socket>,
96 ptr: Pointer,
97 len: usize,
98 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
99 } |this, result: Result<(), ()>| {
100 if result.is_err() {
101 return finish.call(this, Err(LibcError("ENOTCONN")))
102 }
103
104 if socket.is_non_block.get() {
108 let result = this.try_non_block_recv(&socket, ptr, len, false)?;
111 finish.call(this, result)
112 } else {
113 this.block_for_recv(socket, ptr, len, false, finish);
116 interp_ok(())
117 }
118 }
119 ),
120 )
121 }
122
123 fn write<'tcx>(
124 self: FileDescriptionRef<Self>,
125 communicate_allowed: bool,
126 ptr: Pointer,
127 len: usize,
128 ecx: &mut MiriInterpCx<'tcx>,
129 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
130 ) -> InterpResult<'tcx> {
131 assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
132
133 let socket = self;
134
135 ecx.ensure_connected(
136 socket.clone(),
137 !socket.is_non_block.get(),
138 "write",
139 callback!(
140 @capture<'tcx> {
141 socket: FileDescriptionRef<Socket>,
142 ptr: Pointer,
143 len: usize,
144 finish: DynMachineCallback<'tcx, Result<usize, IoError>>
145 } |this, result: Result<(), ()>| {
146 if result.is_err() {
147 return finish.call(this, Err(LibcError("ENOTCONN")))
148 }
149
150 if socket.is_non_block.get() {
154 let result = this.try_non_block_send(&socket, ptr, len)?;
157 return finish.call(this, result)
158 } else {
159 this.block_for_send(socket, ptr, len, finish);
162 interp_ok(())
163 }
164 }
165 ),
166 )
167 }
168
169 fn short_fd_operations(&self) -> bool {
170 false
177 }
178
179 fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {
180 self
181 }
182
183 fn get_flags<'tcx>(&self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> {
184 let mut flags = ecx.eval_libc_i32("O_RDWR");
185
186 if self.is_non_block.get() {
187 flags |= ecx.eval_libc_i32("O_NONBLOCK");
188 }
189
190 interp_ok(Scalar::from_i32(flags))
191 }
192
193 fn set_flags<'tcx>(
194 &self,
195 mut flag: i32,
196 ecx: &mut MiriInterpCx<'tcx>,
197 ) -> InterpResult<'tcx, Scalar> {
198 let o_nonblock = ecx.eval_libc_i32("O_NONBLOCK");
199
200 if flag & o_nonblock == o_nonblock {
202 self.is_non_block.set(true);
203 flag &= !o_nonblock;
204 } else {
205 self.is_non_block.set(false);
206 }
207
208 if flag != 0 {
210 throw_unsup_format!("fcntl: only O_NONBLOCK is supported for sockets")
211 }
212
213 interp_ok(Scalar::from_i32(0))
214 }
215}
216
217impl UnixFileDescription for Socket {
218 fn ioctl<'tcx>(
219 &self,
220 op: Scalar,
221 arg: Option<&OpTy<'tcx>>,
222 ecx: &mut MiriInterpCx<'tcx>,
223 ) -> InterpResult<'tcx, i32> {
224 assert!(ecx.machine.communicate(), "cannot have `Socket` with isolation enabled!");
225
226 let fionbio = ecx.eval_libc("FIONBIO");
227
228 if op == fionbio {
229 if !matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android | Os::MacOs | Os::FreeBsd)
232 {
233 throw_unsup_format!(
238 "ioctl: setting FIONBIO on sockets is unsupported on target {}",
239 ecx.tcx.sess.target.os
240 );
241 }
242
243 let Some(value_ptr) = arg else {
244 throw_ub_format!("ioctl: setting FIONBIO on sockets requires a third argument");
245 };
246 let value = ecx.deref_pointer_as(value_ptr, ecx.machine.layouts.i32)?;
247 let non_block = ecx.read_scalar(&value)?.to_i32()? != 0;
248 self.is_non_block.set(non_block);
249 return interp_ok(0);
250 }
251
252 throw_unsup_format!("ioctl: unsupported operation {op:#x} on socket");
253 }
254}
255
256impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
257pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
258 fn socket(
261 &mut self,
262 domain: &OpTy<'tcx>,
263 type_: &OpTy<'tcx>,
264 protocol: &OpTy<'tcx>,
265 ) -> InterpResult<'tcx, Scalar> {
266 let this = self.eval_context_mut();
267
268 let domain = this.read_scalar(domain)?.to_i32()?;
269 let mut flags = this.read_scalar(type_)?.to_i32()?;
270 let protocol = this.read_scalar(protocol)?.to_i32()?;
271
272 if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
274 this.reject_in_isolation("`socket`", reject_with)?;
275 return this.set_last_error_and_return_i32(LibcError("EACCES"));
276 }
277
278 let mut is_sock_nonblock = false;
279
280 if matches!(
283 this.tcx.sess.target.os,
284 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
285 ) {
286 let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
289 let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
290 if flags & sock_nonblock == sock_nonblock {
291 is_sock_nonblock = true;
292 flags &= !sock_nonblock;
293 }
294 if flags & sock_cloexec == sock_cloexec {
295 flags &= !sock_cloexec;
297 }
298 }
299
300 let family = if domain == this.eval_libc_i32("AF_INET") {
301 SocketFamily::IPv4
302 } else if domain == this.eval_libc_i32("AF_INET6") {
303 SocketFamily::IPv6
304 } else {
305 throw_unsup_format!(
306 "socket: domain {:#x} is unsupported, only AF_INET and \
307 AF_INET6 are allowed.",
308 domain
309 );
310 };
311
312 if flags != this.eval_libc_i32("SOCK_STREAM") {
313 throw_unsup_format!(
314 "socket: type {:#x} is unsupported, only SOCK_STREAM, \
315 SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
316 flags
317 );
318 }
319 if protocol != 0 {
320 throw_unsup_format!(
321 "socket: socket protocol {protocol} is unsupported, \
322 only 0 is allowed"
323 );
324 }
325
326 let fds = &mut this.machine.fds;
327 let fd = fds.new_ref(Socket {
328 family,
329 state: RefCell::new(SocketState::Initial),
330 is_non_block: Cell::new(is_sock_nonblock),
331 });
332
333 interp_ok(Scalar::from_i32(fds.insert(fd)))
334 }
335
336 fn bind(
337 &mut self,
338 socket: &OpTy<'tcx>,
339 address: &OpTy<'tcx>,
340 address_len: &OpTy<'tcx>,
341 ) -> InterpResult<'tcx, Scalar> {
342 let this = self.eval_context_mut();
343
344 let socket = this.read_scalar(socket)?.to_i32()?;
345 let address = match this.socket_address(address, address_len, "bind")? {
346 Ok(addr) => addr,
347 Err(e) => return this.set_last_error_and_return_i32(e),
348 };
349
350 let Some(fd) = this.machine.fds.get(socket) else {
352 return this.set_last_error_and_return_i32(LibcError("EBADF"));
353 };
354
355 let Some(socket) = fd.downcast::<Socket>() else {
356 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
358 };
359
360 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
361
362 let mut state = socket.state.borrow_mut();
363
364 match *state {
365 SocketState::Initial => {
366 let address_family = match &address {
367 SocketAddr::V4(_) => SocketFamily::IPv4,
368 SocketAddr::V6(_) => SocketFamily::IPv6,
369 };
370
371 if socket.family != address_family {
372 let err = if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android) {
375 LibcError("EINVAL")
378 } else {
379 LibcError("EAFNOSUPPORT")
383 };
384 return this.set_last_error_and_return_i32(err);
385 }
386
387 *state = SocketState::Bound(address);
388 }
389 SocketState::Connecting(_) | SocketState::Connected(_) =>
390 throw_unsup_format!(
391 "bind: socket is already connected and binding a
392 connected socket is unsupported"
393 ),
394 SocketState::Bound(_) | SocketState::Listening(_) =>
395 throw_unsup_format!(
396 "bind: socket is already bound and binding a socket \
397 multiple times is unsupported"
398 ),
399 }
400
401 interp_ok(Scalar::from_i32(0))
402 }
403
404 fn listen(&mut self, socket: &OpTy<'tcx>, backlog: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
405 let this = self.eval_context_mut();
406
407 let socket = this.read_scalar(socket)?.to_i32()?;
408 let _backlog = this.read_scalar(backlog)?.to_i32()?;
410
411 let Some(fd) = this.machine.fds.get(socket) else {
413 return this.set_last_error_and_return_i32(LibcError("EBADF"));
414 };
415
416 let Some(socket) = fd.downcast::<Socket>() else {
417 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
419 };
420
421 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
422
423 let mut state = socket.state.borrow_mut();
424
425 match *state {
426 SocketState::Bound(socket_addr) =>
427 match TcpListener::bind(socket_addr) {
428 Ok(listener) => *state = SocketState::Listening(listener),
429 Err(e) => return this.set_last_error_and_return_i32(e),
430 },
431 SocketState::Initial => {
432 throw_unsup_format!(
433 "listen: listening on a socket which isn't bound is unsupported"
434 )
435 }
436 SocketState::Listening(_) => {
437 throw_unsup_format!("listen: listening on a socket multiple times is unsupported")
438 }
439 SocketState::Connecting(_) | SocketState::Connected(_) => {
440 throw_unsup_format!("listen: listening on a connected socket is unsupported")
441 }
442 }
443
444 interp_ok(Scalar::from_i32(0))
445 }
446
447 fn accept4(
450 &mut self,
451 socket: &OpTy<'tcx>,
452 address: &OpTy<'tcx>,
453 address_len: &OpTy<'tcx>,
454 flags: Option<&OpTy<'tcx>>,
455 dest: &MPlaceTy<'tcx>,
457 ) -> InterpResult<'tcx> {
458 let this = self.eval_context_mut();
459
460 let socket = this.read_scalar(socket)?.to_i32()?;
461 let address_ptr = this.read_pointer(address)?;
462 let address_len_ptr = this.read_pointer(address_len)?;
463 let mut flags =
464 if let Some(flags) = flags { this.read_scalar(flags)?.to_i32()? } else { 0 };
465
466 let Some(fd) = this.machine.fds.get(socket) else {
468 return this.set_last_error_and_return(LibcError("EBADF"), dest);
469 };
470
471 let Some(socket) = fd.downcast::<Socket>() else {
472 return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
474 };
475
476 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
477
478 if !matches!(*socket.state.borrow(), SocketState::Listening(_)) {
479 throw_unsup_format!(
480 "accept4: accepting incoming connections is only allowed when socket is listening"
481 )
482 };
483
484 let mut is_client_sock_nonblock = false;
485
486 if matches!(
489 this.tcx.sess.target.os,
490 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
491 ) {
492 let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
495 let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
496 if flags & sock_nonblock == sock_nonblock {
497 is_client_sock_nonblock = true;
498 flags &= !sock_nonblock;
499 }
500 if flags & sock_cloexec == sock_cloexec {
501 flags &= !sock_cloexec;
503 }
504 }
505
506 if flags != 0 {
507 throw_unsup_format!(
508 "accept4: flag {flags:#x} is unsupported, only SOCK_CLOEXEC \
509 and SOCK_NONBLOCK are allowed",
510 );
511 }
512
513 if socket.is_non_block.get() {
514 match this.try_non_block_accept(
517 &socket,
518 address_ptr,
519 address_len_ptr,
520 is_client_sock_nonblock,
521 )? {
522 Ok(sockfd) => {
523 this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), dest)
528 }
529 Err(e) => this.set_last_error_and_return(e, dest),
530 }
531 } else {
532 this.block_for_accept(
535 socket,
536 address_ptr,
537 address_len_ptr,
538 is_client_sock_nonblock,
539 dest.clone(),
540 );
541 interp_ok(())
542 }
543 }
544
545 fn connect(
546 &mut self,
547 socket: &OpTy<'tcx>,
548 address: &OpTy<'tcx>,
549 address_len: &OpTy<'tcx>,
550 dest: &MPlaceTy<'tcx>,
552 ) -> InterpResult<'tcx> {
553 let this = self.eval_context_mut();
554
555 let socket = this.read_scalar(socket)?.to_i32()?;
556 let address = match this.socket_address(address, address_len, "connect")? {
557 Ok(address) => address,
558 Err(e) => return this.set_last_error_and_return(e, dest),
559 };
560
561 let Some(fd) = this.machine.fds.get(socket) else {
563 return this.set_last_error_and_return(LibcError("EBADF"), dest);
564 };
565
566 let Some(socket) = fd.downcast::<Socket>() else {
567 return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
569 };
570
571 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
572
573 match &*socket.state.borrow() {
574 SocketState::Initial => { }
575 SocketState::Connecting(_) =>
577 return this.set_last_error_and_return(LibcError("EALREADY"), dest),
578 _ =>
582 throw_unsup_format!(
583 "connect: connecting is only supported for sockets which are neither \
584 bound, listening nor already connected"
585 ),
586 }
587
588 match TcpStream::connect(address) {
594 Ok(stream) => *socket.state.borrow_mut() = SocketState::Connecting(stream),
595 Err(e) => return this.set_last_error_and_return(e, dest),
596 };
597
598 if socket.is_non_block.get() {
599 this.set_last_error_and_return(LibcError("EINPROGRESS"), dest)
606 } else {
607 let dest = dest.clone();
611
612 this.ensure_connected(
613 socket,
614 true,
615 "connect",
616 callback!(
617 @capture<'tcx> {
618 dest: MPlaceTy<'tcx>
619 } |this, result: Result<(), ()>| {
620 if result.is_err() {
621 this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
622 } else {
623 this.write_scalar(Scalar::from_i32(0), &dest)
624 }
625 }
626 ),
627 )
628 }
629 }
630
631 fn send(
632 &mut self,
633 socket: &OpTy<'tcx>,
634 buffer: &OpTy<'tcx>,
635 length: &OpTy<'tcx>,
636 flags: &OpTy<'tcx>,
637 dest: &MPlaceTy<'tcx>,
639 ) -> InterpResult<'tcx> {
640 let this = self.eval_context_mut();
641
642 let socket = this.read_scalar(socket)?.to_i32()?;
643 let buffer_ptr = this.read_pointer(buffer)?;
644 let size_layout = this.libc_ty_layout("size_t");
645 let length: usize =
646 this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
647 let mut flags = this.read_scalar(flags)?.to_i32()?;
648
649 let Some(fd) = this.machine.fds.get(socket) else {
651 return this.set_last_error_and_return(LibcError("EBADF"), dest);
652 };
653
654 let Some(socket) = fd.downcast::<Socket>() else {
655 return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
657 };
658
659 let mut is_op_non_block = false;
660
661 if matches!(
664 this.tcx.sess.target.os,
665 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
666 ) {
667 let msg_nosignal = this.eval_libc_i32("MSG_NOSIGNAL");
670 let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
671 if flags & msg_nosignal == msg_nosignal {
672 flags &= !msg_nosignal;
676 }
677 if flags & msg_dontwait == msg_dontwait {
678 flags &= !msg_dontwait;
679 is_op_non_block = true;
680 }
681 }
682
683 if flags != 0 {
684 throw_unsup_format!(
685 "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL and MSG_DONTWAIT are allowed",
686 );
687 }
688
689 let should_wait = !is_op_non_block && !socket.is_non_block.get();
692 let dest = dest.clone();
693
694 this.ensure_connected(
695 socket.clone(),
696 should_wait,
697 "send",
698 callback!(
699 @capture<'tcx> {
700 socket: FileDescriptionRef<Socket>,
701 flags: i32,
702 buffer_ptr: Pointer,
703 length: usize,
704 is_op_non_block: bool,
705 dest: MPlaceTy<'tcx>,
706 } |this, result: Result<(), ()>| {
707 if result.is_err() {
708 return this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
709 }
710
711 if is_op_non_block || socket.is_non_block.get() {
712 match this.try_non_block_send(&socket, buffer_ptr, length)? {
715 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
716 Err(e) => this.set_last_error_and_return(e, &dest),
717 }
718 } else {
719 this.block_for_send(
722 socket,
723 buffer_ptr,
724 length,
725 callback!(@capture<'tcx> {
726 dest: MPlaceTy<'tcx>
727 } |this, result: Result<usize, IoError>| {
728 match result {
729 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
730 Err(e) => this.set_last_error_and_return(e, &dest)
731 }
732 }),
733 );
734 interp_ok(())
735 }
736 }
737 ),
738 )
739 }
740
741 fn recv(
742 &mut self,
743 socket: &OpTy<'tcx>,
744 buffer: &OpTy<'tcx>,
745 length: &OpTy<'tcx>,
746 flags: &OpTy<'tcx>,
747 dest: &MPlaceTy<'tcx>,
749 ) -> InterpResult<'tcx> {
750 let this = self.eval_context_mut();
751
752 let socket = this.read_scalar(socket)?.to_i32()?;
753 let buffer_ptr = this.read_pointer(buffer)?;
754 let size_layout = this.libc_ty_layout("size_t");
755 let length: usize =
756 this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
757 let mut flags = this.read_scalar(flags)?.to_i32()?;
758
759 let Some(fd) = this.machine.fds.get(socket) else {
761 return this.set_last_error_and_return(LibcError("EBADF"), dest);
762 };
763
764 let Some(socket) = fd.downcast::<Socket>() else {
765 return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
767 };
768
769 let mut should_peek = false;
770 let mut is_op_non_block = false;
771
772 let msg_peek = this.eval_libc_i32("MSG_PEEK");
776 if flags & msg_peek == msg_peek {
777 should_peek = true;
778 flags &= !msg_peek;
779 }
780
781 if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
782 let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC");
785 if flags & msg_cmsg_cloexec == msg_cmsg_cloexec {
786 flags &= !msg_cmsg_cloexec;
788 }
789 }
790
791 if matches!(
792 this.tcx.sess.target.os,
793 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
794 ) {
795 let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
798 if flags & msg_dontwait == msg_dontwait {
799 flags &= !msg_dontwait;
800 is_op_non_block = true;
801 }
802 }
803
804 if flags != 0 {
805 throw_unsup_format!(
806 "recv: flag {flags:#x} is unsupported, only MSG_PEEK, MSG_DONTWAIT \
807 and MSG_CMSG_CLOEXEC are allowed",
808 );
809 }
810
811 let should_wait = !is_op_non_block && !socket.is_non_block.get();
814 let dest = dest.clone();
815
816 this.ensure_connected(
817 socket.clone(),
818 should_wait,
819 "recv",
820 callback!(
821 @capture<'tcx> {
822 socket: FileDescriptionRef<Socket>,
823 buffer_ptr: Pointer,
824 length: usize,
825 should_peek: bool,
826 is_op_non_block: bool,
827 dest: MPlaceTy<'tcx>,
828 } |this, result: Result<(), ()>| {
829 if result.is_err() {
830 return this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
831 }
832
833 if is_op_non_block || socket.is_non_block.get() {
834 match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {
837 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
838 Err(e) => this.set_last_error_and_return(e, &dest),
839 }
840 } else {
841 this.block_for_recv(
844 socket,
845 buffer_ptr,
846 length,
847 should_peek,
848 callback!(@capture<'tcx> {
849 dest: MPlaceTy<'tcx>
850 } |this, result: Result<usize, IoError>| {
851 match result {
852 Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
853 Err(e) => this.set_last_error_and_return(e, &dest)
854 }
855 }),
856 );
857 interp_ok(())
858 }
859 }
860 ),
861 )
862 }
863
864 fn setsockopt(
865 &mut self,
866 socket: &OpTy<'tcx>,
867 level: &OpTy<'tcx>,
868 option_name: &OpTy<'tcx>,
869 option_value: &OpTy<'tcx>,
870 option_len: &OpTy<'tcx>,
871 ) -> InterpResult<'tcx, Scalar> {
872 let this = self.eval_context_mut();
873
874 let socket = this.read_scalar(socket)?.to_i32()?;
875 let level = this.read_scalar(level)?.to_i32()?;
876 let option_name = this.read_scalar(option_name)?.to_i32()?;
877 let socklen_layout = this.libc_ty_layout("socklen_t");
878 let option_len = this.read_scalar(option_len)?.to_int(socklen_layout.size)?;
879
880 let Some(fd) = this.machine.fds.get(socket) else {
882 return this.set_last_error_and_return_i32(LibcError("EBADF"));
883 };
884
885 let Some(_socket) = fd.downcast::<Socket>() else {
886 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
888 };
889
890 if level == this.eval_libc_i32("SOL_SOCKET") {
891 let opt_so_reuseaddr = this.eval_libc_i32("SO_REUSEADDR");
892
893 if matches!(this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::NetBsd) {
894 let opt_so_nosigpipe = this.eval_libc_i32("SO_NOSIGPIPE");
896
897 if option_name == opt_so_nosigpipe {
898 if option_len != 4 {
899 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
901 }
902 let option_value =
903 this.deref_pointer_as(option_value, this.machine.layouts.i32)?;
904 let _val = this.read_scalar(&option_value)?.to_i32()?;
905 return interp_ok(Scalar::from_i32(0));
908 }
909 }
910
911 if option_name == opt_so_reuseaddr {
912 if option_len != 4 {
913 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
915 }
916 let option_value = this.deref_pointer_as(option_value, this.machine.layouts.i32)?;
917 let _val = this.read_scalar(&option_value)?.to_i32()?;
918 return interp_ok(Scalar::from_i32(0));
921 } else {
922 throw_unsup_format!(
923 "setsockopt: option {option_name:#x} is unsupported for level SOL_SOCKET",
924 );
925 }
926 }
927
928 throw_unsup_format!(
929 "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET is allowed"
930 );
931 }
932
933 fn getsockname(
934 &mut self,
935 socket: &OpTy<'tcx>,
936 address: &OpTy<'tcx>,
937 address_len: &OpTy<'tcx>,
938 ) -> InterpResult<'tcx, Scalar> {
939 let this = self.eval_context_mut();
940
941 let socket = this.read_scalar(socket)?.to_i32()?;
942 let address_ptr = this.read_pointer(address)?;
943 let address_len_ptr = this.read_pointer(address_len)?;
944
945 let Some(fd) = this.machine.fds.get(socket) else {
947 return this.set_last_error_and_return_i32(LibcError("EBADF"));
948 };
949
950 let Some(socket) = fd.downcast::<Socket>() else {
951 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
953 };
954
955 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
956
957 let state = socket.state.borrow();
958
959 let address = match &*state {
960 SocketState::Bound(address) => {
961 if address.port() == 0 {
962 throw_unsup_format!(
966 "getsockname: when the port is 0, getting the socket address before \
967 calling `listen` or `connect` is unsupported"
968 )
969 }
970
971 *address
972 }
973 SocketState::Listening(listener) =>
974 match listener.local_addr() {
975 Ok(address) => address,
976 Err(e) => return this.set_last_error_and_return_i32(e),
977 },
978 _ => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)),
981 };
982
983 match this.write_socket_address(&address, address_ptr, address_len_ptr, "getsockname")? {
984 Ok(_) => interp_ok(Scalar::from_i32(0)),
985 Err(e) => this.set_last_error_and_return_i32(e),
986 }
987 }
988
989 fn getpeername(
990 &mut self,
991 socket: &OpTy<'tcx>,
992 address: &OpTy<'tcx>,
993 address_len: &OpTy<'tcx>,
994 dest: &MPlaceTy<'tcx>,
996 ) -> InterpResult<'tcx> {
997 let this = self.eval_context_mut();
998
999 let socket = this.read_scalar(socket)?.to_i32()?;
1000 let address_ptr = this.read_pointer(address)?;
1001 let address_len_ptr = this.read_pointer(address_len)?;
1002
1003 let Some(fd) = this.machine.fds.get(socket) else {
1005 return this.set_last_error_and_return(LibcError("EBADF"), dest);
1006 };
1007
1008 let Some(socket) = fd.downcast::<Socket>() else {
1009 return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
1011 };
1012
1013 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
1014
1015 let dest = dest.clone();
1016
1017 this.ensure_connected(
1020 socket.clone(),
1021 false,
1022 "getpeername",
1023 callback!(
1024 @capture<'tcx> {
1025 socket: FileDescriptionRef<Socket>,
1026 address_ptr: Pointer,
1027 address_len_ptr: Pointer,
1028 dest: MPlaceTy<'tcx>,
1029 } |this, result: Result<(), ()>| {
1030 if result.is_err() {
1031 return this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
1032 };
1033
1034 let SocketState::Connected(stream) = &*socket.state.borrow() else {
1035 unreachable!()
1036 };
1037
1038 let address = match stream.peer_addr() {
1039 Ok(address) => address,
1040 Err(e) => return this.set_last_error_and_return(e, &dest),
1041 };
1042
1043 match this.write_socket_address(
1044 &address,
1045 address_ptr,
1046 address_len_ptr,
1047 "getpeername",
1048 )? {
1049 Ok(_) => this.write_scalar(Scalar::from_i32(0), &dest),
1050 Err(e) => this.set_last_error_and_return(e, &dest),
1051 }
1052 }
1053 ),
1054 )
1055 }
1056}
1057
1058impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
1059trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1060 fn socket_address(
1064 &self,
1065 address: &OpTy<'tcx>,
1066 address_len: &OpTy<'tcx>,
1067 foreign_name: &'static str,
1068 ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
1069 let this = self.eval_context_ref();
1070
1071 let socklen_layout = this.libc_ty_layout("socklen_t");
1072 let address_len: u64 =
1075 this.read_scalar(address_len)?.to_int(socklen_layout.size)?.try_into().unwrap();
1076
1077 let sockaddr_layout = this.libc_ty_layout("sockaddr");
1079 if address_len < sockaddr_layout.size.bytes() {
1080 return interp_ok(Err(LibcError("EINVAL")));
1082 }
1083 let address = this.deref_pointer_as(address, sockaddr_layout)?;
1084
1085 let family_field = this.project_field_named(&address, "sa_family")?;
1086 let family_layout = this.libc_ty_layout("sa_family_t");
1087 let family = this.read_scalar(&family_field)?.to_int(family_layout.size)?;
1088
1089 let socket_addr = if family == this.eval_libc_i32("AF_INET").into() {
1092 let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
1093 if address_len != sockaddr_in_layout.size.bytes() {
1094 return interp_ok(Err(LibcError("EINVAL")));
1096 }
1097 let address = address.transmute(sockaddr_in_layout, this)?;
1098
1099 let port_field = this.project_field_named(&address, "sin_port")?;
1100 let port_bytes: [u8; 2] = this
1102 .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
1103 .try_into()
1104 .unwrap();
1105 let port = u16::from_be_bytes(port_bytes);
1106
1107 let addr_field = this.project_field_named(&address, "sin_addr")?;
1108 let s_addr_field = this.project_field_named(&addr_field, "s_addr")?;
1109 let addr_bytes: [u8; 4] = this
1111 .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(4))?
1112 .try_into()
1113 .unwrap();
1114 let addr_bits = u32::from_be_bytes(addr_bytes);
1115
1116 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from_bits(addr_bits), port))
1117 } else if family == this.eval_libc_i32("AF_INET6").into() {
1118 let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
1119 if address_len != sockaddr_in6_layout.size.bytes() {
1120 return interp_ok(Err(LibcError("EINVAL")));
1122 }
1123 let address = address.offset(Size::ZERO, sockaddr_in6_layout, this)?;
1125
1126 let port_field = this.project_field_named(&address, "sin6_port")?;
1127 let port_bytes: [u8; 2] = this
1129 .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
1130 .try_into()
1131 .unwrap();
1132 let port = u16::from_be_bytes(port_bytes);
1133
1134 let addr_field = this.project_field_named(&address, "sin6_addr")?;
1135 let s_addr_field = this
1136 .project_field_named(&addr_field, "s6_addr")?
1137 .transmute(this.machine.layouts.u128, this)?;
1138 let addr_bytes: [u8; 16] = this
1140 .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(16))?
1141 .try_into()
1142 .unwrap();
1143 let addr_bits = u128::from_be_bytes(addr_bytes);
1144
1145 let flowinfo_field = this.project_field_named(&address, "sin6_flowinfo")?;
1146 let flowinfo = this.read_scalar(&flowinfo_field)?.to_u32()?;
1149
1150 let scope_id_field = this.project_field_named(&address, "sin6_scope_id")?;
1151 let scope_id = this.read_scalar(&scope_id_field)?.to_u32()?;
1154
1155 SocketAddr::V6(SocketAddrV6::new(
1156 Ipv6Addr::from_bits(addr_bits),
1157 port,
1158 flowinfo,
1159 scope_id,
1160 ))
1161 } else {
1162 throw_unsup_format!(
1165 "{foreign_name}: address family {family:#x} is unsupported, \
1166 only AF_INET and AF_INET6 are allowed"
1167 );
1168 };
1169
1170 interp_ok(Ok(socket_addr))
1171 }
1172
1173 fn write_socket_address(
1183 &mut self,
1184 address: &SocketAddr,
1185 address_ptr: Pointer,
1186 address_len_ptr: Pointer,
1187 foreign_name: &'static str,
1188 ) -> InterpResult<'tcx, Result<(), IoError>> {
1189 let this = self.eval_context_mut();
1190
1191 if address_ptr == Pointer::null() || address_len_ptr == Pointer::null() {
1192 throw_ub_format!(
1195 "{foreign_name}: writing a socket address but the address or the length pointer is a null pointer"
1196 )
1197 }
1198
1199 let socklen_layout = this.libc_ty_layout("socklen_t");
1200 let address_buffer_len_place = this.ptr_to_mplace(address_len_ptr, socklen_layout);
1201 let address_buffer_len: u64 = this
1204 .read_scalar(&address_buffer_len_place)?
1205 .to_int(socklen_layout.size)?
1206 .try_into()
1207 .unwrap();
1208
1209 let (address_buffer, address_layout) = match address {
1210 SocketAddr::V4(address) => {
1211 let address_bytes = address.ip().octets();
1213 let port = address.port().to_be();
1215
1216 let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
1217 let address_buffer = this.allocate(sockaddr_in_layout, MemoryKind::Stack)?;
1221 this.write_bytes_ptr(
1224 address_buffer.ptr(),
1225 iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
1226 )?;
1227
1228 let sin_family_field = this.project_field_named(&address_buffer, "sin_family")?;
1229 let af_inet = this.eval_libc("AF_INET");
1234 let address_family =
1235 Scalar::from_int(af_inet.to_int(af_inet.size())?, sin_family_field.layout.size);
1236 this.write_scalar(address_family, &sin_family_field)?;
1237
1238 let sin_port_field = this.project_field_named(&address_buffer, "sin_port")?;
1239 this.write_bytes_ptr(sin_port_field.ptr(), port.to_ne_bytes())?;
1242
1243 let sin_addr_field = this.project_field_named(&address_buffer, "sin_addr")?;
1244 let s_addr_field = this.project_field_named(&sin_addr_field, "s_addr")?;
1245 this.write_bytes_ptr(s_addr_field.ptr(), address_bytes)?;
1246
1247 (address_buffer, sockaddr_in_layout)
1248 }
1249 SocketAddr::V6(address) => {
1250 let address_bytes = address.ip().octets();
1252 let port = address.port().to_be();
1254 let flowinfo = address.flowinfo();
1256 let scope_id = address.scope_id();
1258
1259 let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
1260 let address_buffer = this.allocate(sockaddr_in6_layout, MemoryKind::Stack)?;
1264 this.write_bytes_ptr(
1267 address_buffer.ptr(),
1268 iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
1269 )?;
1270
1271 let sin6_family_field = this.project_field_named(&address_buffer, "sin6_family")?;
1272 let af_inet6 = this.eval_libc("AF_INET6");
1277 let address_family = Scalar::from_int(
1278 af_inet6.to_int(af_inet6.size())?,
1279 sin6_family_field.layout.size,
1280 );
1281 this.write_scalar(address_family, &sin6_family_field)?;
1282
1283 let sin6_port_field = this.project_field_named(&address_buffer, "sin6_port")?;
1284 this.write_bytes_ptr(sin6_port_field.ptr(), port.to_ne_bytes())?;
1287
1288 let sin6_flowinfo_field =
1289 this.project_field_named(&address_buffer, "sin6_flowinfo")?;
1290 this.write_scalar(Scalar::from_u32(flowinfo), &sin6_flowinfo_field)?;
1291
1292 let sin6_scope_id_field =
1293 this.project_field_named(&address_buffer, "sin6_scope_id")?;
1294 this.write_scalar(Scalar::from_u32(scope_id), &sin6_scope_id_field)?;
1295
1296 let sin6_addr_field = this.project_field_named(&address_buffer, "sin6_addr")?;
1297 let s6_addr_field = this.project_field_named(&sin6_addr_field, "s6_addr")?;
1298 this.write_bytes_ptr(s6_addr_field.ptr(), address_bytes)?;
1299
1300 (address_buffer, sockaddr_in6_layout)
1301 }
1302 };
1303
1304 this.mem_copy(
1306 address_buffer.ptr(),
1307 address_ptr,
1308 address_layout.size.min(Size::from_bytes(address_buffer_len)),
1310 true,
1313 )?;
1314 this.deallocate_ptr(address_buffer.ptr(), None, MemoryKind::Stack)?;
1317 let address_len = address_layout.size.bytes();
1319
1320 this.write_scalar(
1321 Scalar::from_uint(address_len, socklen_layout.size),
1322 &address_buffer_len_place,
1323 )?;
1324
1325 interp_ok(Ok(()))
1326 }
1327
1328 fn block_for_accept(
1335 &mut self,
1336 socket: FileDescriptionRef<Socket>,
1337 address_ptr: Pointer,
1338 address_len_ptr: Pointer,
1339 is_client_sock_nonblock: bool,
1340 dest: MPlaceTy<'tcx>,
1341 ) {
1342 let this = self.eval_context_mut();
1343 this.block_thread_for_io(
1344 socket.clone(),
1345 Interest::READABLE,
1346 None,
1347 callback!(@capture<'tcx> {
1348 address_ptr: Pointer,
1349 address_len_ptr: Pointer,
1350 is_client_sock_nonblock: bool,
1351 socket: FileDescriptionRef<Socket>,
1352 dest: MPlaceTy<'tcx>,
1353 } |this, kind: UnblockKind| {
1354 assert_eq!(kind, UnblockKind::Ready);
1355
1356 match this.try_non_block_accept(&socket, address_ptr, address_len_ptr, is_client_sock_nonblock)? {
1357 Ok(sockfd) => {
1358 this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), &dest)
1363 },
1364 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1365 this.block_for_accept(socket, address_ptr, address_len_ptr, is_client_sock_nonblock, dest);
1367 interp_ok(())
1368 }
1369 Err(e) => this.set_last_error_and_return(e, &dest),
1370 }
1371 }),
1372 );
1373 }
1374
1375 fn try_non_block_accept(
1381 &mut self,
1382 socket: &FileDescriptionRef<Socket>,
1383 address_ptr: Pointer,
1384 address_len_ptr: Pointer,
1385 is_client_sock_nonblock: bool,
1386 ) -> InterpResult<'tcx, Result<i32, IoError>> {
1387 let this = self.eval_context_mut();
1388
1389 let state = socket.state.borrow();
1390 let SocketState::Listening(listener) = &*state else {
1391 panic!(
1392 "try_non_block_accept must only be called when socket is in `SocketState::Listening`"
1393 )
1394 };
1395
1396 let (stream, addr) = match listener.accept() {
1397 Ok(peer) => peer,
1398 Err(e) => return interp_ok(Err(IoError::HostError(e))),
1399 };
1400
1401 let family = match addr {
1402 SocketAddr::V4(_) => SocketFamily::IPv4,
1403 SocketAddr::V6(_) => SocketFamily::IPv6,
1404 };
1405
1406 if address_ptr != Pointer::null() {
1407 if let Err(e) =
1411 this.write_socket_address(&addr, address_ptr, address_len_ptr, "accept4")?
1412 {
1413 return interp_ok(Err(e));
1414 };
1415 }
1416
1417 let fd = this.machine.fds.new_ref(Socket {
1418 family,
1419 state: RefCell::new(SocketState::Connected(stream)),
1420 is_non_block: Cell::new(is_client_sock_nonblock),
1421 });
1422 let sockfd = this.machine.fds.insert(fd);
1423 interp_ok(Ok(sockfd))
1424 }
1425
1426 fn block_for_send(
1434 &mut self,
1435 socket: FileDescriptionRef<Socket>,
1436 buffer_ptr: Pointer,
1437 length: usize,
1438 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1439 ) {
1440 let this = self.eval_context_mut();
1441 this.block_thread_for_io(
1442 socket.clone(),
1443 Interest::WRITABLE,
1444 None,
1445 callback!(@capture<'tcx> {
1446 socket: FileDescriptionRef<Socket>,
1447 buffer_ptr: Pointer,
1448 length: usize,
1449 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1450 } |this, kind: UnblockKind| {
1451 assert_eq!(kind, UnblockKind::Ready);
1452
1453 match this.try_non_block_send(&socket, buffer_ptr, length)? {
1454 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1455 this.block_for_send(socket, buffer_ptr, length, finish);
1456 interp_ok(())
1457 },
1458 result => finish.call(this, result)
1459 }
1460 }),
1461 );
1462 }
1463
1464 fn try_non_block_send(
1469 &mut self,
1470 socket: &FileDescriptionRef<Socket>,
1471 buffer_ptr: Pointer,
1472 length: usize,
1473 ) -> InterpResult<'tcx, Result<usize, IoError>> {
1474 let this = self.eval_context_mut();
1475
1476 let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1477 panic!("try_non_block_send must only be called when the socket is connected")
1478 };
1479
1480 let result = this.write_to_host(stream, length, buffer_ptr)?;
1482 match result {
1485 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::NotConnected => {
1486 interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1489 }
1490 result => interp_ok(result),
1491 }
1492 }
1493
1494 fn block_for_recv(
1502 &mut self,
1503 socket: FileDescriptionRef<Socket>,
1504 buffer_ptr: Pointer,
1505 length: usize,
1506 should_peek: bool,
1507 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1508 ) {
1509 let this = self.eval_context_mut();
1510 this.block_thread_for_io(
1511 socket.clone(),
1512 Interest::READABLE,
1513 None,
1514 callback!(@capture<'tcx> {
1515 socket: FileDescriptionRef<Socket>,
1516 buffer_ptr: Pointer,
1517 length: usize,
1518 should_peek: bool,
1519 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1520 } |this, kind: UnblockKind| {
1521 assert_eq!(kind, UnblockKind::Ready);
1522
1523 match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {
1524 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1525 this.block_for_recv(socket, buffer_ptr, length, should_peek, finish);
1527 interp_ok(())
1528 },
1529 result => finish.call(this, result)
1530 }
1531 }),
1532 );
1533 }
1534
1535 fn try_non_block_recv(
1540 &mut self,
1541 socket: &FileDescriptionRef<Socket>,
1542 buffer_ptr: Pointer,
1543 length: usize,
1544 should_peek: bool,
1545 ) -> InterpResult<'tcx, Result<usize, IoError>> {
1546 let this = self.eval_context_mut();
1547
1548 let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1549 panic!("try_non_block_recv must only be called when the socket is connected")
1550 };
1551
1552 let result = this.read_from_host(
1554 |buf| {
1555 if should_peek { stream.peek(buf) } else { stream.read(buf) }
1556 },
1557 length,
1558 buffer_ptr,
1559 )?;
1560 match result {
1563 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::NotConnected => {
1564 interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1567 }
1568 result => interp_ok(result),
1569 }
1570 }
1571
1572 fn ensure_connected(
1582 &mut self,
1583 socket: FileDescriptionRef<Socket>,
1584 should_wait: bool,
1585 foreign_name: &'static str,
1586 action: DynMachineCallback<'tcx, Result<(), ()>>,
1587 ) -> InterpResult<'tcx> {
1588 let this = self.eval_context_mut();
1589
1590 let state = socket.state.borrow();
1591 match &*state {
1592 SocketState::Connecting(_) => { }
1593 SocketState::Connected(_) => {
1594 drop(state);
1595 return action.call(this, Ok(()));
1596 }
1597 _ => {
1598 drop(state);
1599 return action.call(this, Err(()));
1600 }
1601 };
1602
1603 drop(state);
1604
1605 let timeout = if should_wait {
1611 None
1612 } else {
1613 Some((TimeoutClock::Monotonic, TimeoutAnchor::Absolute, Duration::ZERO))
1614 };
1615
1616 this.block_thread_for_io(
1617 socket.clone(),
1618 Interest::WRITABLE,
1619 timeout,
1620 callback!(
1621 @capture<'tcx> {
1622 socket: FileDescriptionRef<Socket>,
1623 should_wait: bool,
1624 foreign_name: &'static str,
1625 action: DynMachineCallback<'tcx, Result<(), ()>>,
1626 } |this, kind: UnblockKind| {
1627 if UnblockKind::TimedOut == kind {
1628 assert!(!should_wait);
1631 this.machine.blocking_io.deregister(socket.id(), InterestReceiver::UnblockThread(this.active_thread()));
1632 return action.call(this, Err(()))
1633 }
1634
1635 let mut state = socket.state.borrow_mut();
1638 let stream = match &*state {
1639 SocketState::Connecting(stream) => stream,
1640 SocketState::Connected(_) => {
1641 drop(state);
1642 return action.call(this, Ok(()))
1645 },
1646 _ => {
1647 drop(state);
1648 return action.call(this, Err(()))
1653 }
1654 };
1655
1656 if let Ok(Some(_)) = stream.take_error() {
1658 *state = SocketState::Initial;
1669 drop(state);
1670 return action.call(this, Err(()))
1671 }
1672
1673 match stream.peer_addr() {
1679 Ok(_) => { },
1680 Err(e) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::InProgress) => {
1681 panic!("{foreign_name}: received writable event from OS but socket is not yet connected")
1684 },
1685 Err(_) => {
1686 }
1690 }
1691
1692 let SocketState::Connecting(stream) = std::mem::replace(&mut*state, SocketState::Initial) else {
1696 unreachable!()
1698 };
1699 *state = SocketState::Connected(stream);
1700 drop(state);
1701 action.call(this, Ok(()))
1702 }
1703 ),
1704 );
1705
1706 interp_ok(())
1707 }
1708}
1709
1710impl VisitProvenance for FileDescriptionRef<Socket> {
1711 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
1714}
1715
1716impl WithSource for Socket {
1717 fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()> {
1718 let mut state = self.state.borrow_mut();
1719 match &mut *state {
1720 SocketState::Listening(listener) => f(listener),
1721 SocketState::Connecting(stream) | SocketState::Connected(stream) => f(stream),
1722 _ => unreachable!(),
1724 }
1725 }
1726}