Skip to main content

miri/shims/unix/
socket.rs

1use std::net::{Shutdown, SocketAddr};
2
3use rustc_abi::Size;
4use rustc_target::spec::Os;
5
6use crate::shims::FileDescriptionRef;
7use crate::shims::files::FdNum;
8use crate::shims::unix::UnixFileDescription;
9use crate::shims::unix::socket_address::EvalContextExt as _;
10use crate::shims::unix::tcp_socket::TcpSocket;
11use crate::*;
12
13#[derive(Debug, PartialEq)]
14pub enum SocketFamily {
15    // IPv4 internet protocols
16    IPv4,
17    // IPv6 internet protocols
18    IPv6,
19}
20
21/// Represents unix-specific socket file descriptions.
22///
23/// Not to be confused with Unix domain sockets.
24pub trait UnixSocketFileDescription: UnixFileDescription {
25    /// Bind the socket to `address`.
26    fn bind<'tcx>(
27        self: FileDescriptionRef<Self>,
28        _communicate_allowed: bool,
29        _address: SocketAddr,
30        _ecx: &mut MiriInterpCx<'tcx>,
31    ) -> InterpResult<'tcx, Result<(), IoError>> {
32        throw_unsup_format!("cannot bind {}", self.name());
33    }
34
35    /// Start listening on the socket.
36    /// `backlog` specifies how many pending incoming connections can exist at the same
37    /// time before new requests are rejected.
38    fn listen<'tcx>(
39        self: FileDescriptionRef<Self>,
40        _communicate_allowed: bool,
41        _backlog: i32,
42        _ecx: &mut MiriInterpCx<'tcx>,
43    ) -> InterpResult<'tcx, Result<(), IoError>> {
44        throw_unsup_format!("cannot listen on {}", self.name());
45    }
46
47    /// Accept an incoming connection on the socket.
48    /// `is_client_sock_non_block` specifies whether the newly accepted client connection
49    /// should be non-blocking.
50    /// After a successful accept, `finish` should be called with a tuple containing the
51    /// file descriptor of the peer socket and it's address.
52    fn accept<'tcx>(
53        self: FileDescriptionRef<Self>,
54        _communicate_allowed: bool,
55        _is_client_sock_non_block: bool,
56        _ecx: &mut MiriInterpCx<'tcx>,
57        _finish: DynMachineCallback<'tcx, Result<(FdNum, SocketAddr), IoError>>,
58    ) -> InterpResult<'tcx> {
59        throw_unsup_format!("cannot accept {}", self.name());
60    }
61
62    /// Connect the socket to `address`.
63    fn connect<'tcx>(
64        self: FileDescriptionRef<Self>,
65        _communicate_allowed: bool,
66        _address: SocketAddr,
67        _ecx: &mut MiriInterpCx<'tcx>,
68        _finish: DynMachineCallback<'tcx, Result<(), IoError>>,
69    ) -> InterpResult<'tcx> {
70        throw_unsup_format!("cannot connect {}", self.name());
71    }
72
73    /// Receive data on the socket into the given buffer `ptr`.
74    /// `len` indicates how many bytes we should try to receive.
75    /// `is_peek` specifies whether the receive removes the bytes from the receive buffer
76    /// ([`false`]) or leaves them in the receive buffer ([`true`]).
77    /// `is_non_block` specifies whether the receive operation is non-blocking.
78    /// After a successful receive, `finish` should be called with the amount of bytes received.
79    fn recv<'tcx>(
80        self: FileDescriptionRef<Self>,
81        _communicate_allowed: bool,
82        _ptr: Pointer,
83        _len: usize,
84        _is_peek: bool,
85        _is_non_block: bool,
86        _ecx: &mut MiriInterpCx<'tcx>,
87        _finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
88    ) -> InterpResult<'tcx> {
89        throw_unsup_format!("cannot receive from {}", self.name());
90    }
91
92    /// Send data from the given buffer `ptr` into the socket.
93    /// `len` indicates how many bytes we should try to send.
94    /// `is_non_block` specifies whether the send operation is non-blocking.
95    /// After a successful send, `finish` should be called with the amount of bytes sent.
96    fn send<'tcx>(
97        self: FileDescriptionRef<Self>,
98        _communicate_allowed: bool,
99        _ptr: Pointer,
100        _len: usize,
101        _is_non_block: bool,
102        _ecx: &mut MiriInterpCx<'tcx>,
103        _finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
104    ) -> InterpResult<'tcx> {
105        throw_unsup_format!("cannot send to {}", self.name());
106    }
107
108    /// Set the socket option `option` on `level`.
109    /// `value_ptr` points to the new value of the socket option, and `value_len` contains
110    /// the amount of bytes the value uses at `value_ptr`.
111    fn setsockopt<'tcx>(
112        self: FileDescriptionRef<Self>,
113        _level: i32,
114        _option: i32,
115        _value_ptr: Pointer,
116        _value_len: u64,
117        _ecx: &mut MiriInterpCx<'tcx>,
118    ) -> InterpResult<'tcx, Result<(), IoError>> {
119        throw_unsup_format!("cannot set socket option on {}", self.name());
120    }
121
122    /// Get the value of a socket option `option` on `level`.
123    fn getsockopt<'tcx>(
124        self: FileDescriptionRef<Self>,
125        _level: i32,
126        _option: i32,
127        _ecx: &mut MiriInterpCx<'tcx>,
128    ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
129        throw_unsup_format!("cannot get socket option for {}", self.name());
130    }
131
132    /// Get the local address of the socket.
133    fn getsockname<'tcx>(
134        self: FileDescriptionRef<Self>,
135        _communicate_allowed: bool,
136        _ecx: &mut MiriInterpCx<'tcx>,
137    ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
138        throw_unsup_format!("cannot get socket name for {}", self.name());
139    }
140
141    /// Get the remote address of the socket.
142    fn getpeername<'tcx>(
143        self: FileDescriptionRef<Self>,
144        _communicate_allowed: bool,
145        _ecx: &mut MiriInterpCx<'tcx>,
146        _finish: DynMachineCallback<'tcx, Result<SocketAddr, IoError>>,
147    ) -> InterpResult<'tcx> {
148        throw_unsup_format!("cannot get peer name for {}", self.name());
149    }
150
151    /// Shut down the read and/or the write end of the socket.
152    fn shutdown<'tcx>(
153        self: FileDescriptionRef<Self>,
154        _communicate_allowed: bool,
155        _how: Shutdown,
156        _ecx: &mut MiriInterpCx<'tcx>,
157    ) -> InterpResult<'tcx, Result<(), IoError>> {
158        throw_unsup_format!("cannot shut down {}", self.name());
159    }
160}
161
162impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
163pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
164    /// For more information on the arguments see the socket manpage:
165    /// <https://linux.die.net/man/2/socket>
166    fn socket(
167        &mut self,
168        domain: &OpTy<'tcx>,
169        type_: &OpTy<'tcx>,
170        protocol: &OpTy<'tcx>,
171    ) -> InterpResult<'tcx, Scalar> {
172        let this = self.eval_context_mut();
173
174        let domain = this.read_scalar(domain)?.to_i32()?;
175        let mut flags = this.read_scalar(type_)?.to_i32()?;
176        let protocol = this.read_scalar(protocol)?.to_i32()?;
177
178        // Reject if isolation is enabled
179        if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
180            this.reject_in_isolation("`socket`", reject_with)?;
181            return this.set_errno_and_return_neg1_i32(LibcError("EACCES"));
182        }
183
184        let mut is_non_block = false;
185
186        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
187        // if there is anything left at the end, that's an unsupported flag.
188        if matches!(
189            this.tcx.sess.target.os,
190            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
191        ) {
192            // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD,
193            // Solaris, and Illumos targets.
194            let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
195            let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
196            if flags & sock_nonblock == sock_nonblock {
197                is_non_block = true;
198                flags &= !sock_nonblock;
199            }
200            if flags & sock_cloexec == sock_cloexec {
201                // We don't support `exec` so we can ignore this.
202                flags &= !sock_cloexec;
203            }
204        }
205
206        let family = if domain == this.eval_libc_i32("AF_INET") {
207            SocketFamily::IPv4
208        } else if domain == this.eval_libc_i32("AF_INET6") {
209            SocketFamily::IPv6
210        } else {
211            throw_unsup_format!(
212                "socket: domain {:#x} is unsupported, only AF_INET and \
213            AF_INET6 are allowed.",
214                domain
215            );
216        };
217
218        if flags != this.eval_libc_i32("SOCK_STREAM") {
219            throw_unsup_format!(
220                "socket: type {:#x} is unsupported, only SOCK_STREAM, \
221            SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
222                flags
223            );
224        }
225        if protocol != 0 && protocol != this.eval_libc_i32("IPPROTO_TCP") {
226            throw_unsup_format!(
227                "socket: socket protocol {protocol} is unsupported, \
228            only IPPROTO_TCP and 0 are allowed"
229            );
230        }
231
232        let fds = &mut this.machine.fds;
233        let fd = fds.new_ref(TcpSocket::new(family, is_non_block));
234
235        interp_ok(Scalar::from_i32(fds.insert(fd)))
236    }
237
238    fn bind(
239        &mut self,
240        socket: &OpTy<'tcx>,
241        address: &OpTy<'tcx>,
242        address_len: &OpTy<'tcx>,
243    ) -> InterpResult<'tcx, Scalar> {
244        let this = self.eval_context_mut();
245
246        let socket = this.read_scalar(socket)?.to_i32()?;
247        let address = match this.read_socket_address(address, address_len, "bind")? {
248            Ok(addr) => addr,
249            Err(e) => return this.set_errno_and_return_neg1_i32(e),
250        };
251
252        // Get the file handle
253        let Some(fd) = this.machine.fds.get(socket) else {
254            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
255        };
256
257        let Some(socket) = fd.as_unix(this).as_socket(this) else {
258            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
259        };
260
261        match socket.bind(this.machine.communicate(), address, this)? {
262            Ok(_) => interp_ok(Scalar::from_i32(0)),
263            Err(e) => this.set_errno_and_return_neg1_i32(e),
264        }
265    }
266
267    fn listen(&mut self, socket: &OpTy<'tcx>, backlog: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
268        let this = self.eval_context_mut();
269
270        let socket = this.read_scalar(socket)?.to_i32()?;
271        let backlog = this.read_scalar(backlog)?.to_i32()?;
272
273        // Get the file handle
274        let Some(fd) = this.machine.fds.get(socket) else {
275            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
276        };
277
278        let Some(socket) = fd.as_unix(this).as_socket(this) else {
279            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
280        };
281
282        match socket.listen(this.machine.communicate(), backlog, this)? {
283            Ok(_) => interp_ok(Scalar::from_i32(0)),
284            Err(e) => this.set_errno_and_return_neg1_i32(e),
285        }
286    }
287
288    /// For more information on the arguments see the accept manpage:
289    /// <https://linux.die.net/man/2/accept4>
290    fn accept4(
291        &mut self,
292        socket: &OpTy<'tcx>,
293        address: &OpTy<'tcx>,
294        address_len: &OpTy<'tcx>,
295        flags: Option<&OpTy<'tcx>>,
296        // Location where the output scalar is written to.
297        dest: &MPlaceTy<'tcx>,
298    ) -> InterpResult<'tcx> {
299        let this = self.eval_context_mut();
300
301        let socket = this.read_scalar(socket)?.to_i32()?;
302        let address_ptr = this.read_pointer(address)?;
303        let address_len_ptr = this.read_pointer(address_len)?;
304        let mut flags =
305            if let Some(flags) = flags { this.read_scalar(flags)?.to_i32()? } else { 0 };
306
307        // Get the file handle
308        let Some(fd) = this.machine.fds.get(socket) else {
309            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
310        };
311
312        let Some(socket) = fd.as_unix(this).as_socket(this) else {
313            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
314        };
315
316        let mut is_client_sock_nonblock = false;
317
318        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
319        // if there is anything left at the end, that's an unsupported flag.
320        if matches!(
321            this.tcx.sess.target.os,
322            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
323        ) {
324            // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD,
325            // Solaris, and Illumos targets.
326            let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
327            let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
328            if flags & sock_nonblock == sock_nonblock {
329                is_client_sock_nonblock = true;
330                flags &= !sock_nonblock;
331            }
332            if flags & sock_cloexec == sock_cloexec {
333                // We don't support `exec` so we can ignore this.
334                flags &= !sock_cloexec;
335            }
336        }
337
338        if flags != 0 {
339            throw_unsup_format!(
340                "accept4: flag {flags:#x} is unsupported, only SOCK_CLOEXEC \
341                and SOCK_NONBLOCK are allowed",
342            );
343        }
344
345        let dest = dest.clone();
346        socket.accept(
347            this.machine.communicate(),
348            is_client_sock_nonblock,
349            this,
350            callback!(
351                @capture<'tcx> {
352                    address_ptr: Pointer,
353                    address_len_ptr: Pointer,
354                    dest: MPlaceTy<'tcx>
355                } |this, result: Result<(FdNum, SocketAddr), IoError>| {
356                    let (client_sockfd, address) = match result {
357                        Ok(data) => data,
358                        Err(e) => return this.set_errno_and_return_neg1(e, &dest),
359                    };
360
361                    if address_ptr != Pointer::null() {
362                        // We only attempt a write if the address pointer is not a null pointer.
363                        // If the address pointer is a null pointer the user isn't interested in the
364                        // address and we don't need to write anything.
365                        this.write_socket_address(&address, address_ptr, address_len_ptr, "accept4")?;
366                    }
367
368                    // We need to create the scalar using the destination size since
369                    // `syscall(SYS_accept4, ...)` returns a long which doesn't match
370                    // the int returned from the `accept`/`accept4` syscalls.
371                    // See <https://man7.org/linux/man-pages/man2/syscall.2.html>.
372                    this.write_scalar(Scalar::from_int(client_sockfd, dest.layout.size), &dest)
373                }
374            ),
375        )
376    }
377
378    fn connect(
379        &mut self,
380        socket: &OpTy<'tcx>,
381        address: &OpTy<'tcx>,
382        address_len: &OpTy<'tcx>,
383        // Location where the output scalar is written to.
384        dest: &MPlaceTy<'tcx>,
385    ) -> InterpResult<'tcx> {
386        let this = self.eval_context_mut();
387
388        let socket = this.read_scalar(socket)?.to_i32()?;
389        let address = match this.read_socket_address(address, address_len, "connect")? {
390            Ok(address) => address,
391            Err(e) => return this.set_errno_and_return_neg1(e, dest),
392        };
393
394        // Get the file handle
395        let Some(fd) = this.machine.fds.get(socket) else {
396            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
397        };
398
399        let Some(socket) = fd.as_unix(this).as_socket(this) else {
400            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
401        };
402
403        let dest = dest.clone();
404
405        socket.connect(
406            this.machine.communicate(),
407            address,
408            this,
409            callback!(
410                @capture<'tcx> {
411                    dest: MPlaceTy<'tcx>
412                 } |this, result: Result<(), IoError>| {
413                     match result {
414                         Ok(()) => this.write_null(&dest),
415                         Err(e) => this.set_errno_and_return_neg1(e, &dest)
416                     }
417                 }
418            ),
419        )
420    }
421
422    fn send(
423        &mut self,
424        socket: &OpTy<'tcx>,
425        buffer: &OpTy<'tcx>,
426        length: &OpTy<'tcx>,
427        flags: &OpTy<'tcx>,
428        // Location where the output scalar is written to.
429        dest: &MPlaceTy<'tcx>,
430    ) -> InterpResult<'tcx> {
431        let this = self.eval_context_mut();
432
433        let socket = this.read_scalar(socket)?.to_i32()?;
434        let buffer_ptr = this.read_pointer(buffer)?;
435        let size_layout = this.libc_ty_layout("size_t");
436        let length: usize =
437            this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
438        let mut flags = this.read_scalar(flags)?.to_i32()?;
439
440        // Get the file handle
441        let Some(fd) = this.machine.fds.get(socket) else {
442            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
443        };
444
445        let Some(socket) = fd.as_unix(this).as_socket(this) else {
446            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
447        };
448
449        let mut is_non_block = false;
450
451        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
452        // if there is anything left at the end, that's an unsupported flag.
453        if matches!(
454            this.tcx.sess.target.os,
455            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
456        ) {
457            // MSG_NOSIGNAL and MSG_DONTWAIT only exist on Linux, Android, FreeBSD,
458            // Solaris, and Illumos targets.
459            let msg_nosignal = this.eval_libc_i32("MSG_NOSIGNAL");
460            let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
461            if flags & msg_nosignal == msg_nosignal {
462                // This is only needed to ensure that no EPIPE signal is sent when
463                // trying to send into a stream which is no longer connected.
464                // Since we don't support signals, we can ignore this.
465                flags &= !msg_nosignal;
466            }
467            if flags & msg_dontwait == msg_dontwait {
468                flags &= !msg_dontwait;
469                is_non_block = true;
470            }
471        }
472
473        if flags != 0 {
474            throw_unsup_format!(
475                "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL and MSG_DONTWAIT are allowed",
476            );
477        }
478
479        let dest = dest.clone();
480
481        socket.send(
482            this.machine.communicate(),
483            buffer_ptr,
484            length,
485            is_non_block,
486            this,
487            callback!(
488                @capture<'tcx> {
489                    dest: MPlaceTy<'tcx>,
490                } |this, result: Result<usize, IoError>| {
491                    match result {
492                        Ok(bytes_sent) =>
493                            this.write_scalar(Scalar::from_target_usize(bytes_sent.try_into().unwrap(), this), &dest),
494                        Err(e) => this.set_errno_and_return_neg1(e, &dest)
495                    }
496                }
497            )
498        )
499    }
500
501    fn recv(
502        &mut self,
503        socket: &OpTy<'tcx>,
504        buffer: &OpTy<'tcx>,
505        length: &OpTy<'tcx>,
506        flags: &OpTy<'tcx>,
507        // Location where the output scalar is written to.
508        dest: &MPlaceTy<'tcx>,
509    ) -> InterpResult<'tcx> {
510        let this = self.eval_context_mut();
511
512        let socket = this.read_scalar(socket)?.to_i32()?;
513        let buffer_ptr = this.read_pointer(buffer)?;
514        let size_layout = this.libc_ty_layout("size_t");
515        let length: usize =
516            this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
517        let mut flags = this.read_scalar(flags)?.to_i32()?;
518
519        // Get the file handle
520        let Some(fd) = this.machine.fds.get(socket) else {
521            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
522        };
523
524        let Some(socket) = fd.as_unix(this).as_socket(this) else {
525            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
526        };
527
528        let mut is_peek = false;
529        let mut is_non_block = false;
530
531        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
532        // if there is anything left at the end, that's an unsupported flag.
533
534        let msg_peek = this.eval_libc_i32("MSG_PEEK");
535        if flags & msg_peek == msg_peek {
536            is_peek = true;
537            flags &= !msg_peek;
538        }
539
540        if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
541            // MSG_CMSG_CLOEXEC only exists on Linux, Android, FreeBSD,
542            // and Illumos targets.
543            let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC");
544            if flags & msg_cmsg_cloexec == msg_cmsg_cloexec {
545                // We don't support `exec` so we can ignore this.
546                flags &= !msg_cmsg_cloexec;
547            }
548        }
549
550        if matches!(
551            this.tcx.sess.target.os,
552            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
553        ) {
554            // MSG_DONTWAIT only exists on Linux, Android, FreeBSD,
555            // Solaris, and Illumos targets.
556            let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
557            if flags & msg_dontwait == msg_dontwait {
558                flags &= !msg_dontwait;
559                is_non_block = true;
560            }
561        }
562
563        if flags != 0 {
564            throw_unsup_format!(
565                "recv: flag {flags:#x} is unsupported, only MSG_PEEK, MSG_DONTWAIT \
566                and MSG_CMSG_CLOEXEC are allowed",
567            );
568        }
569
570        let dest = dest.clone();
571
572        socket.recv(
573            this.machine.communicate(),
574            buffer_ptr,
575            length,
576            is_peek,
577            is_non_block,
578            this,
579            callback!(
580                @capture<'tcx> {
581                    dest: MPlaceTy<'tcx>,
582                } |this, result: Result<usize, IoError>| {
583                    match result {
584                        Ok(bytes_sent) =>
585                            this.write_scalar(Scalar::from_target_usize(bytes_sent.try_into().unwrap(), this), &dest),
586                        Err(e) => this.set_errno_and_return_neg1(e, &dest)
587                    }
588                }
589            ),
590        )
591    }
592
593    fn setsockopt(
594        &mut self,
595        socket: &OpTy<'tcx>,
596        level: &OpTy<'tcx>,
597        option_name: &OpTy<'tcx>,
598        option_value: &OpTy<'tcx>,
599        option_len: &OpTy<'tcx>,
600    ) -> InterpResult<'tcx, Scalar> {
601        let this = self.eval_context_mut();
602
603        let socket = this.read_scalar(socket)?.to_i32()?;
604        let level = this.read_scalar(level)?.to_i32()?;
605        let option_name = this.read_scalar(option_name)?.to_i32()?;
606        let option_value_ptr = this.read_pointer(option_value)?;
607        let socklen_layout = this.libc_ty_layout("socklen_t");
608        let option_len: u64 =
609            this.read_scalar(option_len)?.to_int(socklen_layout.size)?.try_into().unwrap();
610
611        // Get the file handle
612        let Some(fd) = this.machine.fds.get(socket) else {
613            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
614        };
615
616        let Some(socket) = fd.as_unix(this).as_socket(this) else {
617            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
618        };
619
620        let result = socket.setsockopt(level, option_name, option_value_ptr, option_len, this)?;
621        match result {
622            Ok(_) => interp_ok(Scalar::from_i32(0)),
623            Err(e) => this.set_errno_and_return_neg1_i32(e),
624        }
625    }
626
627    fn getsockopt(
628        &mut self,
629        socket: &OpTy<'tcx>,
630        level: &OpTy<'tcx>,
631        option_name: &OpTy<'tcx>,
632        option_value: &OpTy<'tcx>,
633        option_len: &OpTy<'tcx>,
634    ) -> InterpResult<'tcx, Scalar> {
635        let this = self.eval_context_mut();
636
637        let socket = this.read_scalar(socket)?.to_i32()?;
638        let level = this.read_scalar(level)?.to_i32()?;
639        let option_name = this.read_scalar(option_name)?.to_i32()?;
640        // These two pointers are used to return the value: `len_ptr` initially stores how much space
641        // is available. If the actual value fits into that space, it is written to
642        // `value_ptr` and `len_ptr` is updated to represent how many bytes
643        // were actually written. If the value does not fit, it is silently truncated.
644        // Also see <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getsockopt.html>.
645        let option_value_ptr = this.read_pointer(option_value)?;
646        let option_len_ptr = this.read_pointer(option_len)?;
647
648        // Get the file handle
649        let Some(fd) = this.machine.fds.get(socket) else {
650            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
651        };
652
653        let Some(socket) = fd.as_unix(this).as_socket(this) else {
654            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
655        };
656
657        if option_value_ptr == Pointer::null() || option_len_ptr == Pointer::null() {
658            // This socket option returns a value and thus we need to return EFAULT
659            // when either the value or the length pointers are null pointers.
660            return this.set_errno_and_return_neg1_i32(LibcError("EFAULT"));
661        }
662
663        let socklen_layout = this.libc_ty_layout("socklen_t");
664        let option_len_ptr_mplace = this.ptr_to_mplace(option_len_ptr, socklen_layout);
665        let option_len: usize = this
666            .read_scalar(&option_len_ptr_mplace)?
667            .to_int(socklen_layout.size)?
668            .try_into()
669            .unwrap();
670
671        // `socket.getsockopt` returns a temporary buffer as `option_value_ptr` might not point
672        // to a large enough buffer, in which case we have to truncate.
673        let value_mplace = match socket.getsockopt(level, option_name, this)? {
674            Ok(value_mplace) => value_mplace,
675            Err(e) => return this.set_errno_and_return_neg1_i32(e),
676        };
677
678        // Truncated size of the output value.
679        let output_value_len = value_mplace.layout.size.min(Size::from_bytes(option_len));
680        // Copy the truncated value into the buffer pointed to by `option_value_ptr`.
681        this.mem_copy(
682            value_mplace.ptr(),
683            option_value_ptr,
684            // Truncate the value to fit the provided buffer.
685            output_value_len,
686            // The buffers are guaranteed to not overlap since the `value_mplace`
687            // was just newly allocated on the stack.
688            true,
689        )?;
690        // Deallocate the value buffer as it was only needed to store the value and
691        // copy it into the buffer pointed to by `option_value_ptr`.
692        this.deallocate_ptr(value_mplace.ptr(), None, MemoryKind::Stack)?;
693
694        // On output, the length pointer contains the amount of bytes written -- not the size
695        // of the value before truncation.
696        this.write_scalar(
697            Scalar::from_uint(output_value_len.bytes(), socklen_layout.size),
698            &option_len_ptr_mplace,
699        )?;
700
701        interp_ok(Scalar::from_i32(0))
702    }
703
704    fn getsockname(
705        &mut self,
706        socket: &OpTy<'tcx>,
707        address: &OpTy<'tcx>,
708        address_len: &OpTy<'tcx>,
709    ) -> InterpResult<'tcx, Scalar> {
710        let this = self.eval_context_mut();
711
712        let socket = this.read_scalar(socket)?.to_i32()?;
713        let address_ptr = this.read_pointer(address)?;
714        let address_len_ptr = this.read_pointer(address_len)?;
715
716        // Get the file handle
717        let Some(fd) = this.machine.fds.get(socket) else {
718            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
719        };
720
721        let Some(socket) = fd.as_unix(this).as_socket(this) else {
722            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
723        };
724
725        let address = match socket.getsockname(this.machine.communicate(), this)? {
726            Ok(address) => address,
727            Err(e) => return this.set_errno_and_return_neg1_i32(e),
728        };
729
730        this.write_socket_address(&address, address_ptr, address_len_ptr, "getsockname")
731            .map(|_| Scalar::from_i32(0))
732    }
733
734    fn getpeername(
735        &mut self,
736        socket: &OpTy<'tcx>,
737        address: &OpTy<'tcx>,
738        address_len: &OpTy<'tcx>,
739        // Location where the output scalar is written to.
740        dest: &MPlaceTy<'tcx>,
741    ) -> InterpResult<'tcx> {
742        let this = self.eval_context_mut();
743
744        let socket = this.read_scalar(socket)?.to_i32()?;
745        let address_ptr = this.read_pointer(address)?;
746        let address_len_ptr = this.read_pointer(address_len)?;
747
748        // Get the file handle
749        let Some(fd) = this.machine.fds.get(socket) else {
750            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
751        };
752
753        let Some(socket) = fd.as_unix(this).as_socket(this) else {
754            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
755        };
756
757        let dest = dest.clone();
758
759        socket.getpeername(
760            this.machine.communicate(),
761            this,
762            callback!(
763                @capture<'tcx> {
764                    address_ptr: Pointer,
765                    address_len_ptr: Pointer,
766                    dest: MPlaceTy<'tcx>,
767                } |this, result: Result<SocketAddr, IoError>| {
768                    let address = match result {
769                        Ok(address) => address,
770                        Err(e) => return this.set_errno_and_return_neg1(e, &dest)
771                    };
772
773                    this.write_socket_address(
774                        &address,
775                        address_ptr,
776                        address_len_ptr,
777                        "getpeername",
778                    )?;
779                   this.write_scalar(Scalar::from_i32(0), &dest)
780                }
781            ),
782        )
783    }
784
785    fn shutdown(&mut self, socket: &OpTy<'tcx>, how: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
786        let this = self.eval_context_mut();
787
788        let socket = this.read_scalar(socket)?.to_i32()?;
789        let how = this.read_scalar(how)?.to_i32()?;
790
791        // Get the file handle
792        let Some(fd) = this.machine.fds.get(socket) else {
793            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
794        };
795
796        let Some(socket) = fd.as_unix(this).as_socket(this) else {
797            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
798        };
799
800        let is_read_shutdown = how == this.eval_libc_i32("SHUT_RD");
801        let is_write_shutdown = how == this.eval_libc_i32("SHUT_WR");
802        let is_read_write_shutdown = how == this.eval_libc_i32("SHUT_RDWR");
803
804        let how = match () {
805            _ if is_read_shutdown => Shutdown::Read,
806            _ if is_write_shutdown => Shutdown::Write,
807            _ if is_read_write_shutdown => Shutdown::Both,
808            // An invalid value was passed to `how`.
809            _ => return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")),
810        };
811
812        match socket.shutdown(this.machine.communicate(), how, this)? {
813            Ok(_) => interp_ok(Scalar::from_i32(0)),
814            Err(e) => this.set_errno_and_return_neg1_i32(e),
815        }
816    }
817}