Skip to main content

miri/shims/unix/
socket_address.rs

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