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 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 let service_c_str = this.read_c_str(service_ptr)?;
38
39 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 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 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 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 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 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 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 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 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 let address_len: u64 =
141 this.read_scalar(address_len)?.to_int(socklen_layout.size)?.try_into().unwrap();
142
143 let sockaddr_layout = this.libc_ty_layout("sockaddr");
145 if address_len < sockaddr_layout.size.bytes() {
146 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 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 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 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 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 return interp_ok(Err(LibcError("EINVAL")));
188 }
189 let address = address.offset(Size::ZERO, sockaddr_in6_layout, this)?;
191
192 let port_field = this.project_field_named(&address, "sin6_port")?;
193 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 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 let flowinfo = this.read_scalar(&flowinfo_field)?.to_u32()?;
215
216 let scope_id_field = this.project_field_named(&address, "sin6_scope_id")?;
217 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 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 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 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 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 let address_bytes = address.ip().octets();
279 let port = address.port().to_be();
281
282 let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
283 let address_buffer = this.allocate(sockaddr_in_layout, MemoryKind::Stack)?;
287 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 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 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 let address_bytes = address.ip().octets();
318 let port = address.port().to_be();
320 let flowinfo = address.flowinfo();
322 let scope_id = address.scope_id();
324
325 let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
326 let address_buffer = this.allocate(sockaddr_in6_layout, MemoryKind::Stack)?;
330 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 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 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 this.mem_copy(
372 address_buffer.ptr(),
373 address_ptr,
374 address_buffer.layout.size.min(Size::from_bytes(address_buffer_len)),
376 true,
379 )?;
380 this.deallocate_ptr(address_buffer.ptr(), None, MemoryKind::Stack)?;
383 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 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 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 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 this.write_int(0, &protocol_mplace)?;
441
442 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 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 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 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 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}