Skip to main content

miri/shims/unix/
socket_address.rs

1use std::iter;
2use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
3
4use rustc_abi::Size;
5use rustc_target::spec::Env;
6
7use crate::*;
8
9impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
10pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
11    fn getaddrinfo(
12        &mut self,
13        node: &OpTy<'tcx>,
14        service: &OpTy<'tcx>,
15        hints: &OpTy<'tcx>,
16        res: &OpTy<'tcx>,
17    ) -> InterpResult<'tcx, Scalar> {
18        let this = self.eval_context_mut();
19
20        let node_ptr = this.read_pointer(node)?;
21        let service_ptr = this.read_pointer(service)?;
22        let hints_ptr = this.read_pointer(hints)?;
23        let res_mplace = this.deref_pointer(res)?;
24
25        if node_ptr == Pointer::null() {
26            // We cannot get an address without the `node` part because
27            // the [`ToSocketAddrs`] trait requires an address.
28            throw_unsup_format!(
29                "getaddrinfo: getting the address info without a `node` is unsupported"
30            );
31        }
32
33        let mut port = 0;
34        if service_ptr != Pointer::null() {
35            // The C-string at `service_ptr` is either a port number or a name of a
36            // well-known service.
37            let service_c_str = this.read_c_str(service_ptr)?;
38
39            // Try to parse `service_c_str` as a number -- the only case we support.
40            match str::from_utf8(service_c_str).ok().and_then(|s| s.parse::<u16>().ok()) {
41                Some(service_port) => port = service_port,
42                None => {
43                    // The string is not a valid port number; this is unsupported
44                    // because the standard library's [`ToSocketAddrs`] only supports
45                    // numeric ports.
46                    throw_unsup_format!(
47                        "getaddrinfo: non-numeric `service` arguments aren't supported"
48                    )
49                }
50            }
51        }
52
53        let node_c_str = this.read_c_str(node_ptr)?;
54        let Some(node_str) = str::from_utf8(node_c_str).ok() else {
55            throw_unsup_format!("getaddrinfo: node is not a valid UTF-8 string")
56        };
57
58        if hints_ptr == Pointer::null() {
59            // The standard library only supports getting TCP address information. The
60            // empty hints pointer would allow any socket type so we cannot support it.
61            throw_unsup_format!(
62                "getaddrinfo: getting address info without providing socket type hint is unsupported"
63            )
64        }
65
66        let hints_layout = this.libc_ty_layout("addrinfo");
67        let hints_mplace = this.ptr_to_mplace(hints_ptr, hints_layout);
68
69        let family_field = this.project_field_named(&hints_mplace, "ai_family")?;
70        let family = this.read_scalar(&family_field)?;
71        if family != Scalar::from_i32(0) {
72            // We cannot provide a family hint to the standard library implementation.
73            throw_unsup_format!("getaddrinfo: family hints are not supported")
74        }
75
76        let socktype_field = this.project_field_named(&hints_mplace, "ai_socktype")?;
77        let socktype = this.read_scalar(&socktype_field)?;
78        if socktype != this.eval_libc("SOCK_STREAM") {
79            // The standard library only supports getting TCP address information.
80            throw_unsup_format!(
81                "getaddrinfo: only queries with socket type SOCK_STREAM are supported"
82            )
83        }
84
85        let protocol_field = this.project_field_named(&hints_mplace, "ai_protocol")?;
86        let protocol = this.read_scalar(&protocol_field)?;
87        if protocol != Scalar::from_i32(0) {
88            // We cannot provide a protocol hint to the standard library implementation.
89            throw_unsup_format!("getaddrinfo: protocol hints are not supported")
90        }
91
92        let flags_field = this.project_field_named(&hints_mplace, "ai_flags")?;
93        let flags = this.read_scalar(&flags_field)?;
94        if flags != Scalar::from_i32(0) {
95            // We cannot provide any flag hints to the standard library implementation.
96            throw_unsup_format!("getaddrinfo: flag hints are not supported")
97        }
98
99        let socket_addrs = match (node_str, port).to_socket_addrs() {
100            Ok(addrs) => addrs,
101            Err(e) => {
102                // `getaddrinfo` returns negative integer values when there was an error during socket
103                // address resolution. Because the standard library doesn't expose those integer values
104                // directly, we just return a generic protocol error.
105                // The actual error is emitted as part of a warning diagnostic.
106                this.emit_diagnostic(NonHaltingDiagnostic::SocketAddressResolution { error: e });
107                this.set_last_error(LibcError("EPROTO"))?;
108                return interp_ok(this.eval_libc("EAI_SYSTEM"));
109            }
110        };
111
112        let res_ptr = this.allocate_address_infos(socket_addrs)?;
113
114        this.write_pointer(res_ptr, &res_mplace)?;
115        interp_ok(Scalar::from_i32(0))
116    }
117
118    fn freeaddrinfo(&mut self, res: &OpTy<'tcx>) -> InterpResult<'tcx> {
119        let this = self.eval_context_mut();
120
121        let res_ptr = this.read_pointer(res)?;
122
123        this.free_address_infos(res_ptr)
124    }
125
126    /// Attempt to turn an address and length operand into a standard library socket address.
127    ///
128    /// Returns an IO error should the address length not match the address family length.
129    fn read_socket_address(
130        &self,
131        address: &OpTy<'tcx>,
132        address_len: &OpTy<'tcx>,
133        foreign_name: &'static str,
134    ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
135        let this = self.eval_context_ref();
136
137        let socklen_layout = this.libc_ty_layout("socklen_t");
138        // We only support address lengths which can be stored in a u64 since the
139        // size of a layout is also stored in a u64.
140        let address_len: u64 =
141            this.read_scalar(address_len)?.to_int(socklen_layout.size)?.try_into().unwrap();
142
143        // Initially, treat address as generic sockaddr just to extract the family field.
144        let sockaddr_layout = this.libc_ty_layout("sockaddr");
145        if address_len < sockaddr_layout.size.bytes() {
146            // Address length should be at least as big as the generic sockaddr
147            return interp_ok(Err(LibcError("EINVAL")));
148        }
149        let address = this.deref_pointer_as(address, sockaddr_layout)?;
150
151        let family_field = this.project_field_named(&address, "sa_family")?;
152        let family_layout = this.libc_ty_layout("sa_family_t");
153        let family = this.read_scalar(&family_field)?.to_int(family_layout.size)?;
154
155        // Depending on the family, decide whether it's IPv4 or IPv6 and use specialized layout
156        // to extract address and port.
157        let socket_addr = if family == this.eval_libc_i32("AF_INET").into() {
158            let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
159            if address_len != sockaddr_in_layout.size.bytes() {
160                // Address length should be exactly the length of an IPv4 address.
161                return interp_ok(Err(LibcError("EINVAL")));
162            }
163            let address = address.transmute(sockaddr_in_layout, this)?;
164
165            let port_field = this.project_field_named(&address, "sin_port")?;
166            // Read bytes and treat them as big endian since port is stored in network byte order.
167            let port_bytes: [u8; 2] = this
168                .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
169                .try_into()
170                .unwrap();
171            let port = u16::from_be_bytes(port_bytes);
172
173            let addr_field = this.project_field_named(&address, "sin_addr")?;
174            let s_addr_field = this.project_field_named(&addr_field, "s_addr")?;
175            // Read bytes and treat them as big endian since address is stored in network byte order.
176            let addr_bytes: [u8; 4] = this
177                .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(4))?
178                .try_into()
179                .unwrap();
180            let addr_bits = u32::from_be_bytes(addr_bytes);
181
182            SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from_bits(addr_bits), port))
183        } else if family == this.eval_libc_i32("AF_INET6").into() {
184            let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
185            if address_len != sockaddr_in6_layout.size.bytes() {
186                // Address length should be exactly the length of an IPv6 address.
187                return interp_ok(Err(LibcError("EINVAL")));
188            }
189            // We cannot transmute since the `sockaddr_in6` layout is bigger than the `sockaddr` layout.
190            let address = address.offset(Size::ZERO, sockaddr_in6_layout, this)?;
191
192            let port_field = this.project_field_named(&address, "sin6_port")?;
193            // Read bytes and treat them as big endian since port is stored in network byte order.
194            let port_bytes: [u8; 2] = this
195                .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
196                .try_into()
197                .unwrap();
198            let port = u16::from_be_bytes(port_bytes);
199
200            let addr_field = this.project_field_named(&address, "sin6_addr")?;
201            let s_addr_field = this
202                .project_field_named(&addr_field, "s6_addr")?
203                .transmute(this.machine.layouts.u128, this)?;
204            // Read bytes and treat them as big endian since address is stored in network byte order.
205            let addr_bytes: [u8; 16] = this
206                .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(16))?
207                .try_into()
208                .unwrap();
209            let addr_bits = u128::from_be_bytes(addr_bytes);
210
211            let flowinfo_field = this.project_field_named(&address, "sin6_flowinfo")?;
212            // flowinfo doesn't get the big endian treatment as this field is stored in native byte order
213            // and not in network byte order.
214            let flowinfo = this.read_scalar(&flowinfo_field)?.to_u32()?;
215
216            let scope_id_field = this.project_field_named(&address, "sin6_scope_id")?;
217            // scope_id doesn't get the big endian treatment as this field is stored in native byte order
218            // and not in network byte order.
219            let scope_id = this.read_scalar(&scope_id_field)?.to_u32()?;
220
221            SocketAddr::V6(SocketAddrV6::new(
222                Ipv6Addr::from_bits(addr_bits),
223                port,
224                flowinfo,
225                scope_id,
226            ))
227        } else {
228            // Socket of other types shouldn't be created in a first place and
229            // thus also no address family of another type should be supported.
230            throw_unsup_format!(
231                "{foreign_name}: address family {family:#x} is unsupported, \
232                only AF_INET and AF_INET6 are allowed"
233            );
234        };
235
236        interp_ok(Ok(socket_addr))
237    }
238
239    /// Attempt to write a standard library socket address into a pointer.
240    ///
241    /// The `address_len_ptr` parameter serves both as input and output parameter.
242    /// On input, it points to the size of the buffer `address_ptr` points to, and
243    /// on output it points to the non-truncated size of the written address in the
244    /// buffer pointed to by `address_ptr`.
245    ///
246    /// If the address buffer doesn't fit the whole address, the address is truncated to not
247    /// overflow the buffer.
248    fn write_socket_address(
249        &mut self,
250        address: &SocketAddr,
251        address_ptr: Pointer,
252        address_len_ptr: Pointer,
253        foreign_name: &'static str,
254    ) -> InterpResult<'tcx> {
255        let this = self.eval_context_mut();
256
257        if address_ptr == Pointer::null() || address_len_ptr == Pointer::null() {
258            // The POSIX man page doesn't account for the cases where the `address_ptr` or
259            // `address_len_ptr` could be null pointers. Thus, this behavior is undefined!
260            throw_ub_format!(
261                "{foreign_name}: writing a socket address but the address or the length pointer is a null pointer"
262            )
263        }
264
265        let socklen_layout = this.libc_ty_layout("socklen_t");
266        let address_buffer_len_place = this.ptr_to_mplace(address_len_ptr, socklen_layout);
267        // We only support buffer lengths which can be stored in a u64 since the
268        // size of a layout in bytes is also stored in a u64.
269        let address_buffer_len: u64 = this
270            .read_scalar(&address_buffer_len_place)?
271            .to_int(socklen_layout.size)?
272            .try_into()
273            .unwrap();
274
275        let address_buffer = match address {
276            SocketAddr::V4(address) => {
277                // IPv4 address bytes; already stored in network byte order.
278                let address_bytes = address.ip().octets();
279                // Port needs to be manually turned into network byte order.
280                let port = address.port().to_be();
281
282                let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
283                // Allocate new buffer on the stack with the `sockaddr_in` layout.
284                // We need a temporary buffer as `address_ptr` might not point to a large enough
285                // buffer, in which case we have to truncate.
286                let address_buffer = this.allocate(sockaddr_in_layout, MemoryKind::Stack)?;
287                // Zero the whole buffer as some libc targets have additional fields which we fill
288                // with zero bytes (just like the standard library does it).
289                this.write_bytes_ptr(
290                    address_buffer.ptr(),
291                    iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
292                )?;
293
294                let sin_family_field = this.project_field_named(&address_buffer, "sin_family")?;
295                // We cannot simply write the `AF_INET` scalar into the `sin_family_field` because on most
296                // systems the field has a layout of 16-bit whilst the scalar has a size of 32-bit.
297                // Since the `AF_INET` constant is chosen such that it can safely be converted into
298                // a 16-bit integer, we use the following logic to get a scalar of the right size.
299                let af_inet = this.eval_libc("AF_INET");
300                let address_family =
301                    Scalar::from_int(af_inet.to_int(af_inet.size())?, sin_family_field.layout.size);
302                this.write_scalar(address_family, &sin_family_field)?;
303
304                let sin_port_field = this.project_field_named(&address_buffer, "sin_port")?;
305                // Write the port in target native endianness bytes as we already converted it
306                // to big endian above.
307                this.write_bytes_ptr(sin_port_field.ptr(), port.to_ne_bytes())?;
308
309                let sin_addr_field = this.project_field_named(&address_buffer, "sin_addr")?;
310                let s_addr_field = this.project_field_named(&sin_addr_field, "s_addr")?;
311                this.write_bytes_ptr(s_addr_field.ptr(), address_bytes)?;
312
313                address_buffer
314            }
315            SocketAddr::V6(address) => {
316                // IPv6 address bytes; already stored in network byte order.
317                let address_bytes = address.ip().octets();
318                // Port needs to be manually turned into network byte order.
319                let port = address.port().to_be();
320                // Flowinfo is stored in native byte order.
321                let flowinfo = address.flowinfo();
322                // Scope id is stored in native byte order.
323                let scope_id = address.scope_id();
324
325                let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
326                // Allocate new buffer on the stack with the `sockaddr_in6` layout.
327                // We need a temporary buffer as `address_ptr` might not point to a large enough
328                // buffer, in which case we have to truncate.
329                let address_buffer = this.allocate(sockaddr_in6_layout, MemoryKind::Stack)?;
330                // Zero the whole buffer as some libc targets have additional fields which we fill
331                // with zero bytes (just like the standard library does it).
332                this.write_bytes_ptr(
333                    address_buffer.ptr(),
334                    iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
335                )?;
336
337                let sin6_family_field = this.project_field_named(&address_buffer, "sin6_family")?;
338                // We cannot simply write the `AF_INET6` scalar into the `sin6_family_field` because on most
339                // systems the field has a layout of 16-bit whilst the scalar has a size of 32-bit.
340                // Since the `AF_INET6` constant is chosen such that it can safely be converted into
341                // a 16-bit integer, we use the following logic to get a scalar of the right size.
342                let af_inet6 = this.eval_libc("AF_INET6");
343                let address_family = Scalar::from_int(
344                    af_inet6.to_int(af_inet6.size())?,
345                    sin6_family_field.layout.size,
346                );
347                this.write_scalar(address_family, &sin6_family_field)?;
348
349                let sin6_port_field = this.project_field_named(&address_buffer, "sin6_port")?;
350                // Write the port in target native endianness bytes as we already converted it
351                // to big endian above.
352                this.write_bytes_ptr(sin6_port_field.ptr(), port.to_ne_bytes())?;
353
354                let sin6_flowinfo_field =
355                    this.project_field_named(&address_buffer, "sin6_flowinfo")?;
356                this.write_scalar(Scalar::from_u32(flowinfo), &sin6_flowinfo_field)?;
357
358                let sin6_scope_id_field =
359                    this.project_field_named(&address_buffer, "sin6_scope_id")?;
360                this.write_scalar(Scalar::from_u32(scope_id), &sin6_scope_id_field)?;
361
362                let sin6_addr_field = this.project_field_named(&address_buffer, "sin6_addr")?;
363                let s6_addr_field = this.project_field_named(&sin6_addr_field, "s6_addr")?;
364                this.write_bytes_ptr(s6_addr_field.ptr(), address_bytes)?;
365
366                address_buffer
367            }
368        };
369
370        // Copy the truncated address into the pointer pointed to by `address_ptr`.
371        this.mem_copy(
372            address_buffer.ptr(),
373            address_ptr,
374            // Truncate the address to fit the provided buffer.
375            address_buffer.layout.size.min(Size::from_bytes(address_buffer_len)),
376            // The buffers are guaranteed to not overlap since the `address_buffer`
377            // was just newly allocated on the stack.
378            true,
379        )?;
380        // Deallocate the address buffer as it was only needed to construct the address and
381        // copy it into the buffer pointed to by `address_ptr`.
382        this.deallocate_ptr(address_buffer.ptr(), None, MemoryKind::Stack)?;
383        // Size of the non-truncated address.
384        let address_len = address_buffer.layout.size.bytes();
385
386        this.write_scalar(
387            Scalar::from_uint(address_len, socklen_layout.size),
388            &address_buffer_len_place,
389        )?;
390
391        interp_ok(())
392    }
393}
394
395impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
396trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
397    /// Allocate a linked list of address info structs from an iterator of [`SocketAddr`]s.
398    /// Returns a pointer pointing to the head of the linked list.
399    fn allocate_address_infos(
400        &mut self,
401        mut addresses: impl Iterator<Item = SocketAddr>,
402    ) -> InterpResult<'tcx, Pointer> {
403        let this = self.eval_context_mut();
404
405        let Some(address) = addresses.next() else {
406            // Iterator is empty; we return a null pointer.
407            return interp_ok(Pointer::null());
408        };
409
410        let addrinfo_layout = this.libc_ty_layout("addrinfo");
411
412        let addrinfo_mplace =
413            this.allocate(addrinfo_layout, MiriMemoryKind::SocketAddress.into())?;
414
415        let flags_mplace = this.project_field_named(&addrinfo_mplace, "ai_flags")?;
416        // We don't support flag hints and depending on the target libc we have different default values:
417        // "According to POSIX.1, specifying hints as NULL should cause `ai_flags` to be assumed as 0.
418        // The GNU C library instead assumes a value of (AI_V4MAPPED | AI_ADDRCONFIG) for this case,
419        // since this value is considered an improvement on the specification."
420        let flags = if matches!(this.tcx.sess.target.env, Env::Gnu) {
421            this.eval_libc_i32("AI_V4MAPPED") | this.eval_libc_i32("AI_ADDRCONFIG")
422        } else {
423            0
424        };
425        this.write_int(flags, &flags_mplace)?;
426
427        let family_mplace = this.project_field_named(&addrinfo_mplace, "ai_family")?;
428        let family = match &address {
429            SocketAddr::V4(_) => this.eval_libc("AF_INET"),
430            SocketAddr::V6(_) => this.eval_libc("AF_INET6"),
431        };
432        this.write_scalar(family, &family_mplace)?;
433
434        let socktype_mplace = this.project_field_named(&addrinfo_mplace, "ai_socktype")?;
435        this.write_scalar(this.eval_libc("SOCK_STREAM"), &socktype_mplace)?;
436
437        let protocol_mplace = this.project_field_named(&addrinfo_mplace, "ai_protocol")?;
438        // We don't support protocol hints and thus we just return zero which falls back
439        // to the default protocol for the provided socket type.
440        this.write_int(0, &protocol_mplace)?;
441
442        // `sockaddr_storage` is guaranteed to fit any `sockaddr_*` address structure.
443        let sockaddr_layout = this.libc_ty_layout("sockaddr_storage");
444
445        let addrlen_mplace = this.project_field_named(&addrinfo_mplace, "ai_addrlen")?;
446        let addr_mplace = this.project_field_named(&addrinfo_mplace, "ai_addr")?;
447        this.write_int(sockaddr_layout.size.bytes(), &addrlen_mplace)?;
448
449        let sockaddr_mplace = this.allocate(sockaddr_layout, MiriMemoryKind::Machine.into())?;
450        // Zero the newly allocated socket address struct.
451        this.write_bytes_ptr(
452            sockaddr_mplace.ptr(),
453            iter::repeat_n(0, sockaddr_mplace.layout.size.bytes_usize()),
454        )?;
455        this.write_socket_address(
456            &address,
457            sockaddr_mplace.ptr(),
458            addrlen_mplace.ptr(),
459            "getaddrinfo",
460        )?;
461        this.write_pointer(sockaddr_mplace.ptr(), &addr_mplace)?;
462
463        let canonname_mplace = this.project_field_named(&addrinfo_mplace, "ai_canonname")?;
464        this.write_pointer(Pointer::null(), &canonname_mplace)?;
465
466        // Allocate remaining list and store a pointer to it.
467        let next_mplace = this.project_field_named(&addrinfo_mplace, "ai_next")?;
468        let next_ptr = this.allocate_address_infos(addresses)?;
469        this.write_pointer(next_ptr, &next_mplace)?;
470
471        interp_ok(addrinfo_mplace.ptr())
472    }
473
474    /// Deallocate the linked list of address info structs.
475    /// `address_ptr` points to the start from where we deallocate recursively.
476    fn free_address_infos(&mut self, address_ptr: Pointer) -> InterpResult<'tcx> {
477        let this = self.eval_context_mut();
478
479        if address_ptr == Pointer::null() {
480            // We're at the end of the linked list.
481            return interp_ok(());
482        }
483
484        let addrinfo_layout = this.libc_ty_layout("addrinfo");
485        let addrinfo_mplace = this.ptr_to_mplace(address_ptr, addrinfo_layout);
486
487        let addr_field = this.project_field_named(&addrinfo_mplace, "ai_addr")?;
488        let addr_ptr = this.read_pointer(&addr_field)?;
489        this.deallocate_ptr(addr_ptr, None, MiriMemoryKind::Machine.into())?;
490
491        let next_field = this.project_field_named(&addrinfo_mplace, "ai_next")?;
492        let next_ptr = this.read_pointer(&next_field)?;
493        this.free_address_infos(next_ptr)?;
494
495        this.deallocate_ptr(address_ptr, None, MiriMemoryKind::SocketAddress.into())?;
496
497        interp_ok(())
498    }
499}