1use std::cell::{Cell, RefCell};
2use std::io::Read;
3use std::net::{Ipv4Addr, Ipv6Addr, Shutdown, 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 && protocol != this.eval_libc_i32("IPPROTO_TCP") {
320 throw_unsup_format!(
321 "socket: socket protocol {protocol} is unsupported, \
322 only IPPROTO_TCP and 0 are 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 fn shutdown(&mut self, socket: &OpTy<'tcx>, how: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
1058 let this = self.eval_context_mut();
1059
1060 let socket = this.read_scalar(socket)?.to_i32()?;
1061 let how = this.read_scalar(how)?.to_i32()?;
1062
1063 let Some(fd) = this.machine.fds.get(socket) else {
1065 return this.set_last_error_and_return_i32(LibcError("EBADF"));
1066 };
1067
1068 let Some(socket) = fd.downcast::<Socket>() else {
1069 return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
1071 };
1072
1073 assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
1074
1075 let state = socket.state.borrow();
1076
1077 let (SocketState::Connecting(stream) | SocketState::Connected(stream)) = &*state else {
1078 return this.set_last_error_and_return_i32(LibcError("ENOTCONN"));
1079 };
1080
1081 let shut_rd = this.eval_libc_i32("SHUT_RD");
1082 let shut_wr = this.eval_libc_i32("SHUT_WR");
1083 let shut_rdwr = this.eval_libc_i32("SHUT_RDWR");
1084
1085 let how = match () {
1086 _ if how == shut_rd => Shutdown::Read,
1087 _ if how == shut_wr => Shutdown::Write,
1088 _ if how == shut_rdwr => Shutdown::Both,
1089 _ => return this.set_last_error_and_return_i32(LibcError("EINVAL")),
1091 };
1092
1093 match stream.shutdown(how) {
1094 Ok(_) => interp_ok(Scalar::from_i32(0)),
1095 Err(e) => this.set_last_error_and_return_i32(e),
1096 }
1097 }
1098}
1099
1100impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
1101trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1102 fn socket_address(
1106 &self,
1107 address: &OpTy<'tcx>,
1108 address_len: &OpTy<'tcx>,
1109 foreign_name: &'static str,
1110 ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
1111 let this = self.eval_context_ref();
1112
1113 let socklen_layout = this.libc_ty_layout("socklen_t");
1114 let address_len: u64 =
1117 this.read_scalar(address_len)?.to_int(socklen_layout.size)?.try_into().unwrap();
1118
1119 let sockaddr_layout = this.libc_ty_layout("sockaddr");
1121 if address_len < sockaddr_layout.size.bytes() {
1122 return interp_ok(Err(LibcError("EINVAL")));
1124 }
1125 let address = this.deref_pointer_as(address, sockaddr_layout)?;
1126
1127 let family_field = this.project_field_named(&address, "sa_family")?;
1128 let family_layout = this.libc_ty_layout("sa_family_t");
1129 let family = this.read_scalar(&family_field)?.to_int(family_layout.size)?;
1130
1131 let socket_addr = if family == this.eval_libc_i32("AF_INET").into() {
1134 let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
1135 if address_len != sockaddr_in_layout.size.bytes() {
1136 return interp_ok(Err(LibcError("EINVAL")));
1138 }
1139 let address = address.transmute(sockaddr_in_layout, this)?;
1140
1141 let port_field = this.project_field_named(&address, "sin_port")?;
1142 let port_bytes: [u8; 2] = this
1144 .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
1145 .try_into()
1146 .unwrap();
1147 let port = u16::from_be_bytes(port_bytes);
1148
1149 let addr_field = this.project_field_named(&address, "sin_addr")?;
1150 let s_addr_field = this.project_field_named(&addr_field, "s_addr")?;
1151 let addr_bytes: [u8; 4] = this
1153 .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(4))?
1154 .try_into()
1155 .unwrap();
1156 let addr_bits = u32::from_be_bytes(addr_bytes);
1157
1158 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from_bits(addr_bits), port))
1159 } else if family == this.eval_libc_i32("AF_INET6").into() {
1160 let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
1161 if address_len != sockaddr_in6_layout.size.bytes() {
1162 return interp_ok(Err(LibcError("EINVAL")));
1164 }
1165 let address = address.offset(Size::ZERO, sockaddr_in6_layout, this)?;
1167
1168 let port_field = this.project_field_named(&address, "sin6_port")?;
1169 let port_bytes: [u8; 2] = this
1171 .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
1172 .try_into()
1173 .unwrap();
1174 let port = u16::from_be_bytes(port_bytes);
1175
1176 let addr_field = this.project_field_named(&address, "sin6_addr")?;
1177 let s_addr_field = this
1178 .project_field_named(&addr_field, "s6_addr")?
1179 .transmute(this.machine.layouts.u128, this)?;
1180 let addr_bytes: [u8; 16] = this
1182 .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(16))?
1183 .try_into()
1184 .unwrap();
1185 let addr_bits = u128::from_be_bytes(addr_bytes);
1186
1187 let flowinfo_field = this.project_field_named(&address, "sin6_flowinfo")?;
1188 let flowinfo = this.read_scalar(&flowinfo_field)?.to_u32()?;
1191
1192 let scope_id_field = this.project_field_named(&address, "sin6_scope_id")?;
1193 let scope_id = this.read_scalar(&scope_id_field)?.to_u32()?;
1196
1197 SocketAddr::V6(SocketAddrV6::new(
1198 Ipv6Addr::from_bits(addr_bits),
1199 port,
1200 flowinfo,
1201 scope_id,
1202 ))
1203 } else {
1204 throw_unsup_format!(
1207 "{foreign_name}: address family {family:#x} is unsupported, \
1208 only AF_INET and AF_INET6 are allowed"
1209 );
1210 };
1211
1212 interp_ok(Ok(socket_addr))
1213 }
1214
1215 fn write_socket_address(
1225 &mut self,
1226 address: &SocketAddr,
1227 address_ptr: Pointer,
1228 address_len_ptr: Pointer,
1229 foreign_name: &'static str,
1230 ) -> InterpResult<'tcx, Result<(), IoError>> {
1231 let this = self.eval_context_mut();
1232
1233 if address_ptr == Pointer::null() || address_len_ptr == Pointer::null() {
1234 throw_ub_format!(
1237 "{foreign_name}: writing a socket address but the address or the length pointer is a null pointer"
1238 )
1239 }
1240
1241 let socklen_layout = this.libc_ty_layout("socklen_t");
1242 let address_buffer_len_place = this.ptr_to_mplace(address_len_ptr, socklen_layout);
1243 let address_buffer_len: u64 = this
1246 .read_scalar(&address_buffer_len_place)?
1247 .to_int(socklen_layout.size)?
1248 .try_into()
1249 .unwrap();
1250
1251 let (address_buffer, address_layout) = match address {
1252 SocketAddr::V4(address) => {
1253 let address_bytes = address.ip().octets();
1255 let port = address.port().to_be();
1257
1258 let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
1259 let address_buffer = this.allocate(sockaddr_in_layout, MemoryKind::Stack)?;
1263 this.write_bytes_ptr(
1266 address_buffer.ptr(),
1267 iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
1268 )?;
1269
1270 let sin_family_field = this.project_field_named(&address_buffer, "sin_family")?;
1271 let af_inet = this.eval_libc("AF_INET");
1276 let address_family =
1277 Scalar::from_int(af_inet.to_int(af_inet.size())?, sin_family_field.layout.size);
1278 this.write_scalar(address_family, &sin_family_field)?;
1279
1280 let sin_port_field = this.project_field_named(&address_buffer, "sin_port")?;
1281 this.write_bytes_ptr(sin_port_field.ptr(), port.to_ne_bytes())?;
1284
1285 let sin_addr_field = this.project_field_named(&address_buffer, "sin_addr")?;
1286 let s_addr_field = this.project_field_named(&sin_addr_field, "s_addr")?;
1287 this.write_bytes_ptr(s_addr_field.ptr(), address_bytes)?;
1288
1289 (address_buffer, sockaddr_in_layout)
1290 }
1291 SocketAddr::V6(address) => {
1292 let address_bytes = address.ip().octets();
1294 let port = address.port().to_be();
1296 let flowinfo = address.flowinfo();
1298 let scope_id = address.scope_id();
1300
1301 let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
1302 let address_buffer = this.allocate(sockaddr_in6_layout, MemoryKind::Stack)?;
1306 this.write_bytes_ptr(
1309 address_buffer.ptr(),
1310 iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
1311 )?;
1312
1313 let sin6_family_field = this.project_field_named(&address_buffer, "sin6_family")?;
1314 let af_inet6 = this.eval_libc("AF_INET6");
1319 let address_family = Scalar::from_int(
1320 af_inet6.to_int(af_inet6.size())?,
1321 sin6_family_field.layout.size,
1322 );
1323 this.write_scalar(address_family, &sin6_family_field)?;
1324
1325 let sin6_port_field = this.project_field_named(&address_buffer, "sin6_port")?;
1326 this.write_bytes_ptr(sin6_port_field.ptr(), port.to_ne_bytes())?;
1329
1330 let sin6_flowinfo_field =
1331 this.project_field_named(&address_buffer, "sin6_flowinfo")?;
1332 this.write_scalar(Scalar::from_u32(flowinfo), &sin6_flowinfo_field)?;
1333
1334 let sin6_scope_id_field =
1335 this.project_field_named(&address_buffer, "sin6_scope_id")?;
1336 this.write_scalar(Scalar::from_u32(scope_id), &sin6_scope_id_field)?;
1337
1338 let sin6_addr_field = this.project_field_named(&address_buffer, "sin6_addr")?;
1339 let s6_addr_field = this.project_field_named(&sin6_addr_field, "s6_addr")?;
1340 this.write_bytes_ptr(s6_addr_field.ptr(), address_bytes)?;
1341
1342 (address_buffer, sockaddr_in6_layout)
1343 }
1344 };
1345
1346 this.mem_copy(
1348 address_buffer.ptr(),
1349 address_ptr,
1350 address_layout.size.min(Size::from_bytes(address_buffer_len)),
1352 true,
1355 )?;
1356 this.deallocate_ptr(address_buffer.ptr(), None, MemoryKind::Stack)?;
1359 let address_len = address_layout.size.bytes();
1361
1362 this.write_scalar(
1363 Scalar::from_uint(address_len, socklen_layout.size),
1364 &address_buffer_len_place,
1365 )?;
1366
1367 interp_ok(Ok(()))
1368 }
1369
1370 fn block_for_accept(
1377 &mut self,
1378 socket: FileDescriptionRef<Socket>,
1379 address_ptr: Pointer,
1380 address_len_ptr: Pointer,
1381 is_client_sock_nonblock: bool,
1382 dest: MPlaceTy<'tcx>,
1383 ) {
1384 let this = self.eval_context_mut();
1385 this.block_thread_for_io(
1386 socket.clone(),
1387 Interest::READABLE,
1388 None,
1389 callback!(@capture<'tcx> {
1390 address_ptr: Pointer,
1391 address_len_ptr: Pointer,
1392 is_client_sock_nonblock: bool,
1393 socket: FileDescriptionRef<Socket>,
1394 dest: MPlaceTy<'tcx>,
1395 } |this, kind: UnblockKind| {
1396 assert_eq!(kind, UnblockKind::Ready);
1397
1398 match this.try_non_block_accept(&socket, address_ptr, address_len_ptr, is_client_sock_nonblock)? {
1399 Ok(sockfd) => {
1400 this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), &dest)
1405 },
1406 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1407 this.block_for_accept(socket, address_ptr, address_len_ptr, is_client_sock_nonblock, dest);
1409 interp_ok(())
1410 }
1411 Err(e) => this.set_last_error_and_return(e, &dest),
1412 }
1413 }),
1414 );
1415 }
1416
1417 fn try_non_block_accept(
1423 &mut self,
1424 socket: &FileDescriptionRef<Socket>,
1425 address_ptr: Pointer,
1426 address_len_ptr: Pointer,
1427 is_client_sock_nonblock: bool,
1428 ) -> InterpResult<'tcx, Result<i32, IoError>> {
1429 let this = self.eval_context_mut();
1430
1431 let state = socket.state.borrow();
1432 let SocketState::Listening(listener) = &*state else {
1433 panic!(
1434 "try_non_block_accept must only be called when socket is in `SocketState::Listening`"
1435 )
1436 };
1437
1438 let (stream, addr) = match listener.accept() {
1439 Ok(peer) => peer,
1440 Err(e) => return interp_ok(Err(IoError::HostError(e))),
1441 };
1442
1443 let family = match addr {
1444 SocketAddr::V4(_) => SocketFamily::IPv4,
1445 SocketAddr::V6(_) => SocketFamily::IPv6,
1446 };
1447
1448 if address_ptr != Pointer::null() {
1449 if let Err(e) =
1453 this.write_socket_address(&addr, address_ptr, address_len_ptr, "accept4")?
1454 {
1455 return interp_ok(Err(e));
1456 };
1457 }
1458
1459 let fd = this.machine.fds.new_ref(Socket {
1460 family,
1461 state: RefCell::new(SocketState::Connected(stream)),
1462 is_non_block: Cell::new(is_client_sock_nonblock),
1463 });
1464 let sockfd = this.machine.fds.insert(fd);
1465 interp_ok(Ok(sockfd))
1466 }
1467
1468 fn block_for_send(
1476 &mut self,
1477 socket: FileDescriptionRef<Socket>,
1478 buffer_ptr: Pointer,
1479 length: usize,
1480 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1481 ) {
1482 let this = self.eval_context_mut();
1483 this.block_thread_for_io(
1484 socket.clone(),
1485 Interest::WRITABLE,
1486 None,
1487 callback!(@capture<'tcx> {
1488 socket: FileDescriptionRef<Socket>,
1489 buffer_ptr: Pointer,
1490 length: usize,
1491 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1492 } |this, kind: UnblockKind| {
1493 assert_eq!(kind, UnblockKind::Ready);
1494
1495 match this.try_non_block_send(&socket, buffer_ptr, length)? {
1496 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1497 this.block_for_send(socket, buffer_ptr, length, finish);
1498 interp_ok(())
1499 },
1500 result => finish.call(this, result)
1501 }
1502 }),
1503 );
1504 }
1505
1506 fn try_non_block_send(
1511 &mut self,
1512 socket: &FileDescriptionRef<Socket>,
1513 buffer_ptr: Pointer,
1514 length: usize,
1515 ) -> InterpResult<'tcx, Result<usize, IoError>> {
1516 let this = self.eval_context_mut();
1517
1518 let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1519 panic!("try_non_block_send must only be called when the socket is connected")
1520 };
1521
1522 let result = this.write_to_host(stream, length, buffer_ptr)?;
1524 match result {
1527 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::NotConnected => {
1528 interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1531 }
1532 Err(IoError::HostError(e))
1533 if cfg!(windows)
1534 && matches!(e.raw_os_error(), Some(10058)) =>
1535 {
1536 interp_ok(Err(IoError::HostError(io::ErrorKind::BrokenPipe.into())))
1540 }
1541 result => interp_ok(result),
1542 }
1543 }
1544
1545 fn block_for_recv(
1553 &mut self,
1554 socket: FileDescriptionRef<Socket>,
1555 buffer_ptr: Pointer,
1556 length: usize,
1557 should_peek: bool,
1558 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1559 ) {
1560 let this = self.eval_context_mut();
1561 this.block_thread_for_io(
1562 socket.clone(),
1563 Interest::READABLE,
1564 None,
1565 callback!(@capture<'tcx> {
1566 socket: FileDescriptionRef<Socket>,
1567 buffer_ptr: Pointer,
1568 length: usize,
1569 should_peek: bool,
1570 finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1571 } |this, kind: UnblockKind| {
1572 assert_eq!(kind, UnblockKind::Ready);
1573
1574 match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {
1575 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1576 this.block_for_recv(socket, buffer_ptr, length, should_peek, finish);
1578 interp_ok(())
1579 },
1580 result => finish.call(this, result)
1581 }
1582 }),
1583 );
1584 }
1585
1586 fn try_non_block_recv(
1591 &mut self,
1592 socket: &FileDescriptionRef<Socket>,
1593 buffer_ptr: Pointer,
1594 length: usize,
1595 should_peek: bool,
1596 ) -> InterpResult<'tcx, Result<usize, IoError>> {
1597 let this = self.eval_context_mut();
1598
1599 let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1600 panic!("try_non_block_recv must only be called when the socket is connected")
1601 };
1602
1603 let result = this.read_from_host(
1605 |buf| {
1606 if should_peek { stream.peek(buf) } else { stream.read(buf) }
1607 },
1608 length,
1609 buffer_ptr,
1610 )?;
1611 match result {
1614 Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::NotConnected => {
1615 interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1618 }
1619 result => interp_ok(result),
1620 }
1621 }
1622
1623 fn ensure_connected(
1633 &mut self,
1634 socket: FileDescriptionRef<Socket>,
1635 should_wait: bool,
1636 foreign_name: &'static str,
1637 action: DynMachineCallback<'tcx, Result<(), ()>>,
1638 ) -> InterpResult<'tcx> {
1639 let this = self.eval_context_mut();
1640
1641 let state = socket.state.borrow();
1642 match &*state {
1643 SocketState::Connecting(_) => { }
1644 SocketState::Connected(_) => {
1645 drop(state);
1646 return action.call(this, Ok(()));
1647 }
1648 _ => {
1649 drop(state);
1650 return action.call(this, Err(()));
1651 }
1652 };
1653
1654 drop(state);
1655
1656 let timeout = if should_wait {
1662 None
1663 } else {
1664 Some((TimeoutClock::Monotonic, TimeoutAnchor::Absolute, Duration::ZERO))
1665 };
1666
1667 this.block_thread_for_io(
1668 socket.clone(),
1669 Interest::WRITABLE,
1670 timeout,
1671 callback!(
1672 @capture<'tcx> {
1673 socket: FileDescriptionRef<Socket>,
1674 should_wait: bool,
1675 foreign_name: &'static str,
1676 action: DynMachineCallback<'tcx, Result<(), ()>>,
1677 } |this, kind: UnblockKind| {
1678 if UnblockKind::TimedOut == kind {
1679 assert!(!should_wait);
1682 this.machine.blocking_io.deregister(socket.id(), InterestReceiver::UnblockThread(this.active_thread()));
1683 return action.call(this, Err(()))
1684 }
1685
1686 let mut state = socket.state.borrow_mut();
1689 let stream = match &*state {
1690 SocketState::Connecting(stream) => stream,
1691 SocketState::Connected(_) => {
1692 drop(state);
1693 return action.call(this, Ok(()))
1696 },
1697 _ => {
1698 drop(state);
1699 return action.call(this, Err(()))
1704 }
1705 };
1706
1707 if let Ok(Some(_)) = stream.take_error() {
1709 *state = SocketState::Initial;
1720 drop(state);
1721 return action.call(this, Err(()))
1722 }
1723
1724 match stream.peer_addr() {
1730 Ok(_) => { },
1731 Err(e) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::InProgress) => {
1732 panic!("{foreign_name}: received writable event from OS but socket is not yet connected")
1735 },
1736 Err(_) => {
1737 }
1741 }
1742
1743 let SocketState::Connecting(stream) = std::mem::replace(&mut*state, SocketState::Initial) else {
1747 unreachable!()
1749 };
1750 *state = SocketState::Connected(stream);
1751 drop(state);
1752 action.call(this, Ok(()))
1753 }
1754 ),
1755 );
1756
1757 interp_ok(())
1758 }
1759}
1760
1761impl VisitProvenance for FileDescriptionRef<Socket> {
1762 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
1765}
1766
1767impl WithSource for Socket {
1768 fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()> {
1769 let mut state = self.state.borrow_mut();
1770 match &mut *state {
1771 SocketState::Listening(listener) => f(listener),
1772 SocketState::Connecting(stream) | SocketState::Connected(stream) => f(stream),
1773 _ => unreachable!(),
1775 }
1776 }
1777}